__main__.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. #-----------------------------------------------------------------------------
  2. # Copyright (c) 2013-2021, PyInstaller Development Team.
  3. #
  4. # Distributed under the terms of the GNU General Public License (version 2
  5. # or later) with exception for distributing the bootloader.
  6. #
  7. # The full license is in the file COPYING.txt, distributed with this software.
  8. #
  9. # SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
  10. #-----------------------------------------------------------------------------
  11. """
  12. Main command-line interface to PyInstaller.
  13. """
  14. import os
  15. import argparse
  16. import platform
  17. from PyInstaller import __version__
  18. from PyInstaller import log as logging
  19. # note: don't import anything else until this function is run!
  20. from PyInstaller.compat import check_requirements, is_conda
  21. logger = logging.getLogger(__name__)
  22. # Taken from https://stackoverflow.com/a/22157136 to format args more flexibly:
  23. # any help text which beings with ``R|`` will have all newlines preserved; the
  24. # help text will be line wrapped. See
  25. # https://docs.python.org/3/library/argparse.html#formatter-class.
  26. #
  27. # This is used by the ``--debug`` option.
  28. class _SmartFormatter(argparse.HelpFormatter):
  29. def _split_lines(self, text, width):
  30. if text.startswith('R|'):
  31. # The underlying implementation of ``RawTextHelpFormatter._split_lines``
  32. # invokes this; mimic it.
  33. return text[2:].splitlines()
  34. else:
  35. # Invoke the usual formatter.
  36. return super(_SmartFormatter, self)._split_lines(text, width)
  37. def run_makespec(filenames, **opts):
  38. # Split pathex by using the path separator
  39. temppaths = opts['pathex'][:]
  40. pathex = opts['pathex'] = []
  41. for p in temppaths:
  42. pathex.extend(p.split(os.pathsep))
  43. import PyInstaller.building.makespec
  44. spec_file = PyInstaller.building.makespec.main(filenames, **opts)
  45. logger.info('wrote %s' % spec_file)
  46. return spec_file
  47. def run_build(pyi_config, spec_file, **kwargs):
  48. import PyInstaller.building.build_main
  49. PyInstaller.building.build_main.main(pyi_config, spec_file, **kwargs)
  50. def __add_options(parser):
  51. parser.add_argument('-v', '--version', action='version',
  52. version=__version__,
  53. help='Show program version info and exit.')
  54. def generate_parser() -> argparse.ArgumentParser:
  55. """Build an argparse parser for PyInstaller's main CLI."""
  56. import PyInstaller.building.makespec
  57. import PyInstaller.building.build_main
  58. import PyInstaller.log
  59. parser = argparse.ArgumentParser(formatter_class=_SmartFormatter)
  60. parser.prog = "pyinstaller"
  61. __add_options(parser)
  62. PyInstaller.building.makespec.__add_options(parser)
  63. PyInstaller.building.build_main.__add_options(parser)
  64. PyInstaller.log.__add_options(parser)
  65. parser.add_argument('filenames', metavar='scriptname', nargs='+',
  66. help=("name of scriptfiles to be processed or "
  67. "exactly one .spec-file. If a .spec-file is "
  68. "specified, most options are unnecessary "
  69. "and are ignored."))
  70. return parser
  71. def run(pyi_args=None, pyi_config=None):
  72. """
  73. pyi_args allows running PyInstaller programatically without a subprocess
  74. pyi_config allows checking configuration once when running multiple tests
  75. """
  76. check_requirements()
  77. import PyInstaller.log
  78. try:
  79. parser = generate_parser()
  80. args = parser.parse_args(pyi_args)
  81. PyInstaller.log.__process_options(parser, args)
  82. # Print PyInstaller version, Python version and platform
  83. # as the first line to stdout.
  84. # This helps identify PyInstaller, Python and platform version
  85. # when users report issues.
  86. logger.info('PyInstaller: %s' % __version__)
  87. logger.info('Python: %s%s', platform.python_version(),
  88. " (conda)" if is_conda else "")
  89. logger.info('Platform: %s' % platform.platform())
  90. # Skip creating .spec when .spec file is supplied
  91. if args.filenames[0].endswith('.spec'):
  92. spec_file = args.filenames[0]
  93. else:
  94. spec_file = run_makespec(**vars(args))
  95. run_build(pyi_config, spec_file, **vars(args))
  96. except KeyboardInterrupt:
  97. raise SystemExit("Aborted by user request.")
  98. except RecursionError:
  99. from PyInstaller import _recursion_to_deep_message
  100. _recursion_to_deep_message.raise_with_msg()
  101. if __name__ == '__main__':
  102. run()