| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923 |
- #-----------------------------------------------------------------------------
- # Copyright (c) 2005-2023, PyInstaller Development Team.
- #
- # Distributed under the terms of the GNU General Public License (version 2
- # or later) with exception for distributing the bootloader.
- #
- # The full license is in the file COPYING.txt, distributed with this software.
- #
- # SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
- #-----------------------------------------------------------------------------
- """
- Automatically build spec files containing a description of the project.
- """
- import argparse
- import os
- import re
- import sys
- from PyInstaller import DEFAULT_SPECPATH, HOMEPATH
- from PyInstaller import log as logging
- from PyInstaller.building.templates import bundleexetmplt, bundletmplt, onedirtmplt, onefiletmplt, splashtmpl
- from PyInstaller.compat import is_darwin, is_win
- logger = logging.getLogger(__name__)
- # This list gives valid choices for the ``--debug`` command-line option, except for the ``all`` choice.
- DEBUG_ARGUMENT_CHOICES = ['imports', 'bootloader', 'noarchive']
- # This is the ``all`` choice.
- DEBUG_ALL_CHOICE = ['all']
- def escape_win_filepath(path):
- # escape all \ with another \ after using normpath to clean up the path
- return os.path.normpath(path).replace('\\', '\\\\')
- def make_path_spec_relative(filename, spec_dir):
- """
- Make the filename relative to the directory containing .spec file if filename is relative and not absolute.
- Otherwise keep filename untouched.
- """
- if os.path.isabs(filename):
- return filename
- else:
- filename = os.path.abspath(filename)
- # Make it relative.
- filename = os.path.relpath(filename, start=spec_dir)
- return filename
- # Support for trying to avoid hard-coded paths in the .spec files. Eg, all files rooted in the Installer directory tree
- # will be written using "HOMEPATH", thus allowing this spec file to be used with any Installer installation. Same thing
- # could be done for other paths too.
- path_conversions = ((HOMEPATH, "HOMEPATH"),)
- class SourceDestAction(argparse.Action):
- """
- A command line option which takes multiple source:dest pairs.
- """
- def __init__(self, *args, default=None, metavar=None, **kwargs):
- super().__init__(*args, default=[], metavar='SOURCE:DEST', **kwargs)
- def __call__(self, parser, namespace, value, option_string=None):
- try:
- # Find the only separator that isn't a Windows drive.
- separator, = (m for m in re.finditer(rf"(^\w:[/\\])|[:{os.pathsep}]", value) if not m[1])
- except ValueError:
- # Split into SRC and DEST failed, wrong syntax
- raise argparse.ArgumentError(self, f'Wrong syntax, should be {self.option_strings[0]}=SOURCE:DEST')
- src = value[:separator.start()]
- dest = value[separator.end():]
- if not src or not dest:
- # Syntax was correct, but one or both of SRC and DEST was not given
- raise argparse.ArgumentError(self, "You have to specify both SOURCE and DEST")
- # argparse is not particularly smart with copy by reference typed defaults. If the current list is the default,
- # replace it before modifying it to avoid changing the default.
- if getattr(namespace, self.dest) is self.default:
- setattr(namespace, self.dest, [])
- getattr(namespace, self.dest).append((src, dest))
- def make_variable_path(filename, conversions=path_conversions):
- if not os.path.isabs(filename):
- # os.path.commonpath can not compare relative and absolute paths, and if filename is not absolute, none of the
- # paths in conversions will match anyway.
- return None, filename
- for (from_path, to_name) in conversions:
- assert os.path.abspath(from_path) == from_path, ("path '%s' should already be absolute" % from_path)
- try:
- common_path = os.path.commonpath([filename, from_path])
- except ValueError:
- # Per https://docs.python.org/3/library/os.path.html#os.path.commonpath, this raises ValueError in several
- # cases which prevent computing a common path.
- common_path = None
- if common_path == from_path:
- rest = filename[len(from_path):]
- if rest.startswith(('\\', '/')):
- rest = rest[1:]
- return to_name, rest
- return None, filename
- def removed_key_option(x):
- from PyInstaller.exceptions import RemovedCipherFeatureError
- raise RemovedCipherFeatureError("Please remove your --key=xxx argument.")
- class _RemovedFlagAction(argparse.Action):
- def __init__(self, *args, **kwargs):
- kwargs["help"] = argparse.SUPPRESS
- kwargs["nargs"] = 0
- super().__init__(*args, **kwargs)
- class _RemovedNoEmbedManifestAction(_RemovedFlagAction):
- def __call__(self, *args, **kwargs):
- from PyInstaller.exceptions import RemovedExternalManifestError
- raise RemovedExternalManifestError("Please remove your --no-embed-manifest argument.")
- class _RemovedWinPrivateAssembliesAction(_RemovedFlagAction):
- def __call__(self, *args, **kwargs):
- from PyInstaller.exceptions import RemovedWinSideBySideSupportError
- raise RemovedWinSideBySideSupportError("Please remove your --win-private-assemblies argument.")
- class _RemovedWinNoPreferRedirectsAction(_RemovedFlagAction):
- def __call__(self, *args, **kwargs):
- from PyInstaller.exceptions import RemovedWinSideBySideSupportError
- raise RemovedWinSideBySideSupportError("Please remove your --win-no-prefer-redirects argument.")
- # An object used in place of a "path string", which knows how to repr() itself using variable names instead of
- # hard-coded paths.
- class Path:
- def __init__(self, *parts):
- self.path = os.path.join(*parts)
- self.variable_prefix = self.filename_suffix = None
- def __repr__(self):
- if self.filename_suffix is None:
- self.variable_prefix, self.filename_suffix = make_variable_path(self.path)
- if self.variable_prefix is None:
- return repr(self.path)
- return "os.path.join(" + self.variable_prefix + "," + repr(self.filename_suffix) + ")"
- # An object used to construct extra preamble for the spec file, in order to accommodate extra collect_*() calls from the
- # command-line
- class Preamble:
- def __init__(
- self, datas, binaries, hiddenimports, collect_data, collect_binaries, collect_submodules, collect_all,
- copy_metadata, recursive_copy_metadata
- ):
- # Initialize with literal values - will be switched to preamble variable name later, if necessary
- self.binaries = binaries or []
- self.hiddenimports = hiddenimports or []
- self.datas = datas or []
- # Preamble content
- self.content = []
- # Import statements
- if collect_data:
- self._add_hookutil_import('collect_data_files')
- if collect_binaries:
- self._add_hookutil_import('collect_dynamic_libs')
- if collect_submodules:
- self._add_hookutil_import('collect_submodules')
- if collect_all:
- self._add_hookutil_import('collect_all')
- if copy_metadata or recursive_copy_metadata:
- self._add_hookutil_import('copy_metadata')
- if self.content:
- self.content += [''] # empty line to separate the section
- # Variables
- if collect_data or copy_metadata or collect_all or recursive_copy_metadata:
- self._add_var('datas', self.datas)
- self.datas = 'datas' # switch to variable
- if collect_binaries or collect_all:
- self._add_var('binaries', self.binaries)
- self.binaries = 'binaries' # switch to variable
- if collect_submodules or collect_all:
- self._add_var('hiddenimports', self.hiddenimports)
- self.hiddenimports = 'hiddenimports' # switch to variable
- # Content - collect_data_files
- for entry in collect_data:
- self._add_collect_data(entry)
- # Content - copy_metadata
- for entry in copy_metadata:
- self._add_copy_metadata(entry)
- # Content - copy_metadata(..., recursive=True)
- for entry in recursive_copy_metadata:
- self._add_recursive_copy_metadata(entry)
- # Content - collect_binaries
- for entry in collect_binaries:
- self._add_collect_binaries(entry)
- # Content - collect_submodules
- for entry in collect_submodules:
- self._add_collect_submodules(entry)
- # Content - collect_all
- for entry in collect_all:
- self._add_collect_all(entry)
- # Merge
- if self.content and self.content[-1] != '':
- self.content += [''] # empty line
- self.content = '\n'.join(self.content)
- def _add_hookutil_import(self, name):
- self.content += ['from PyInstaller.utils.hooks import {0}'.format(name)]
- def _add_var(self, name, initial_value):
- self.content += ['{0} = {1}'.format(name, initial_value)]
- def _add_collect_data(self, name):
- self.content += ['datas += collect_data_files(\'{0}\')'.format(name)]
- def _add_copy_metadata(self, name):
- self.content += ['datas += copy_metadata(\'{0}\')'.format(name)]
- def _add_recursive_copy_metadata(self, name):
- self.content += ['datas += copy_metadata(\'{0}\', recursive=True)'.format(name)]
- def _add_collect_binaries(self, name):
- self.content += ['binaries += collect_dynamic_libs(\'{0}\')'.format(name)]
- def _add_collect_submodules(self, name):
- self.content += ['hiddenimports += collect_submodules(\'{0}\')'.format(name)]
- def _add_collect_all(self, name):
- self.content += [
- 'tmp_ret = collect_all(\'{0}\')'.format(name),
- 'datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]'
- ]
- def __add_options(parser):
- """
- Add the `Makespec` options to a option-parser instance or a option group.
- """
- g = parser.add_argument_group('What to generate')
- g.add_argument(
- "-D",
- "--onedir",
- dest="onefile",
- action="store_false",
- default=None,
- help="Create a one-folder bundle containing an executable (default)",
- )
- g.add_argument(
- "-F",
- "--onefile",
- dest="onefile",
- action="store_true",
- default=None,
- help="Create a one-file bundled executable.",
- )
- g.add_argument(
- "--specpath",
- metavar="DIR",
- help="Folder to store the generated spec file (default: current directory)",
- )
- g.add_argument(
- "-n",
- "--name",
- help="Name to assign to the bundled app and spec file (default: first script's basename)",
- )
- g.add_argument(
- "--contents-directory",
- help="For onedir builds only, specify the name of the directory in which all supporting files (i.e. everything "
- "except the executable itself) will be placed in. Use \".\" to re-enable old onedir layout without contents "
- "directory.",
- )
- g = parser.add_argument_group('What to bundle, where to search')
- g.add_argument(
- '--add-data',
- action=SourceDestAction,
- dest='datas',
- help="Additional data files or directories containing data files to be added to the application. The argument "
- 'value should be in form of "source:dest_dir", where source is the path to file (or directory) to be '
- "collected, dest_dir is the destination directory relative to the top-level application directory, and both "
- "paths are separated by a colon (:). To put a file in the top-level application directory, use . as a "
- "dest_dir. This option can be used multiple times."
- )
- g.add_argument(
- '--add-binary',
- action=SourceDestAction,
- dest="binaries",
- help='Additional binary files to be added to the executable. See the ``--add-data`` option for the format. '
- 'This option can be used multiple times.',
- )
- g.add_argument(
- "-p",
- "--paths",
- dest="pathex",
- metavar="DIR",
- action="append",
- default=[],
- help="A path to search for imports (like using PYTHONPATH). Multiple paths are allowed, separated by ``%s``, "
- "or use this option multiple times. Equivalent to supplying the ``pathex`` argument in the spec file." %
- repr(os.pathsep),
- )
- g.add_argument(
- '--hidden-import',
- '--hiddenimport',
- action='append',
- default=[],
- metavar="MODULENAME",
- dest='hiddenimports',
- help='Name an import not visible in the code of the script(s). This option can be used multiple times.',
- )
- g.add_argument(
- '--collect-submodules',
- action="append",
- default=[],
- metavar="MODULENAME",
- dest='collect_submodules',
- help='Collect all submodules from the specified package or module. This option can be used multiple times.',
- )
- g.add_argument(
- '--collect-data',
- '--collect-datas',
- action="append",
- default=[],
- metavar="MODULENAME",
- dest='collect_data',
- help='Collect all data from the specified package or module. This option can be used multiple times.',
- )
- g.add_argument(
- '--collect-binaries',
- action="append",
- default=[],
- metavar="MODULENAME",
- dest='collect_binaries',
- help='Collect all binaries from the specified package or module. This option can be used multiple times.',
- )
- g.add_argument(
- '--collect-all',
- action="append",
- default=[],
- metavar="MODULENAME",
- dest='collect_all',
- help='Collect all submodules, data files, and binaries from the specified package or module. This option can '
- 'be used multiple times.',
- )
- g.add_argument(
- '--copy-metadata',
- action="append",
- default=[],
- metavar="PACKAGENAME",
- dest='copy_metadata',
- help='Copy metadata for the specified package. This option can be used multiple times.',
- )
- g.add_argument(
- '--recursive-copy-metadata',
- action="append",
- default=[],
- metavar="PACKAGENAME",
- dest='recursive_copy_metadata',
- help='Copy metadata for the specified package and all its dependencies. This option can be used multiple '
- 'times.',
- )
- g.add_argument(
- "--additional-hooks-dir",
- action="append",
- dest="hookspath",
- default=[],
- help="An additional path to search for hooks. This option can be used multiple times.",
- )
- g.add_argument(
- '--runtime-hook',
- action='append',
- dest='runtime_hooks',
- default=[],
- help='Path to a custom runtime hook file. A runtime hook is code that is bundled with the executable and is '
- 'executed before any other code or module to set up special features of the runtime environment. This option '
- 'can be used multiple times.',
- )
- g.add_argument(
- '--exclude-module',
- dest='excludes',
- action='append',
- default=[],
- help='Optional module or package (the Python name, not the path name) that will be ignored (as though it was '
- 'not found). This option can be used multiple times.',
- )
- g.add_argument(
- '--key',
- dest='key',
- help=argparse.SUPPRESS,
- type=removed_key_option,
- )
- g.add_argument(
- '--splash',
- dest='splash',
- metavar="IMAGE_FILE",
- help="(EXPERIMENTAL) Add an splash screen with the image IMAGE_FILE to the application. The splash screen can "
- "display progress updates while unpacking.",
- )
- g.add_argument(
- '--splash-center',
- dest='splash_center',
- default=None,
- choices={'default', 'primary', 'virtual', 'active'},
- help="Splash screen centering mode. See the splash screen documentation for details.",
- )
- g = parser.add_argument_group('How to generate')
- g.add_argument(
- "-d",
- "--debug",
- # If this option is not specified, then its default value is an empty list (no debug options selected).
- default=[],
- # Note that ``nargs`` is omitted. This produces a single item not stored in a list, as opposed to a list
- # containing one item, as per `nargs <https://docs.python.org/3/library/argparse.html#nargs>`_.
- nargs=None,
- # The options specified must come from this list.
- choices=DEBUG_ALL_CHOICE + DEBUG_ARGUMENT_CHOICES,
- # Append choice, rather than storing them (which would overwrite any previous selections).
- action='append',
- # Allow newlines in the help text; see the ``_SmartFormatter`` in ``__main__.py``.
- help=(
- "R|Provide assistance with debugging a frozen\n"
- "application. This argument may be provided multiple\n"
- "times to select several of the following options.\n"
- "\n"
- "- all: All three of the following options.\n"
- "\n"
- "- imports: specify the -v option to the underlying\n"
- " Python interpreter, causing it to print a message\n"
- " each time a module is initialized, showing the\n"
- " place (filename or built-in module) from which it\n"
- " is loaded. See\n"
- " https://docs.python.org/3/using/cmdline.html#id4.\n"
- "\n"
- "- bootloader: tell the bootloader to issue progress\n"
- " messages while initializing and starting the\n"
- " bundled app. Used to diagnose problems with\n"
- " missing imports.\n"
- "\n"
- "- noarchive: instead of storing all frozen Python\n"
- " source files as an archive inside the resulting\n"
- " executable, store them as files in the resulting\n"
- " output directory.\n"
- "\n"
- ),
- )
- g.add_argument(
- '--optimize',
- dest='optimize',
- metavar='LEVEL',
- type=int,
- choices={-1, 0, 1, 2},
- default=None,
- help='Bytecode optimization level used for collected python modules and scripts. For details, see the section '
- '“Bytecode Optimization Level” in PyInstaller manual.',
- )
- g.add_argument(
- '--python-option',
- dest='python_options',
- metavar='PYTHON_OPTION',
- action='append',
- default=[],
- help='Specify a command-line option to pass to the Python interpreter at runtime. Currently supports '
- '"v" (equivalent to "--debug imports"), "u", "W <warning control>", "X <xoption>", and "hash_seed=<value>". '
- 'For details, see the section "Specifying Python Interpreter Options" in PyInstaller manual.',
- )
- g.add_argument(
- "-s",
- "--strip",
- action="store_true",
- help="Apply a symbol-table strip to the executable and shared libs (not recommended for Windows)",
- )
- g.add_argument(
- "--noupx",
- action="store_true",
- default=False,
- help="Do not use UPX even if it is available (works differently between Windows and *nix)",
- )
- g.add_argument(
- "--upx-exclude",
- dest="upx_exclude",
- metavar="FILE",
- action="append",
- help="Prevent a binary from being compressed when using upx. This is typically used if upx corrupts certain "
- "binaries during compression. FILE is the filename of the binary without path. This option can be used "
- "multiple times.",
- )
- g = parser.add_argument_group('Windows and macOS specific options')
- g.add_argument(
- "-c",
- "--console",
- "--nowindowed",
- dest="console",
- action="store_true",
- default=None,
- help="Open a console window for standard i/o (default). On Windows this option has no effect if the first "
- "script is a '.pyw' file.",
- )
- g.add_argument(
- "-w",
- "--windowed",
- "--noconsole",
- dest="console",
- action="store_false",
- default=None,
- help="Windows and macOS: do not provide a console window for standard i/o. On macOS this also triggers "
- "building a macOS .app bundle. On Windows this option is automatically set if the first script is a '.pyw' "
- "file. This option is ignored on *NIX systems.",
- )
- g.add_argument(
- "--hide-console",
- type=str,
- choices={'hide-early', 'hide-late', 'minimize-early', 'minimize-late'},
- default=None,
- help="Windows only: in console-enabled executable, have bootloader automatically hide or minimize the console "
- "window if the program owns the console window (i.e., was not launched from an existing console window).",
- )
- g.add_argument(
- "-i",
- "--icon",
- action='append',
- dest="icon_file",
- metavar='<FILE.ico or FILE.exe,ID or FILE.icns or Image or "NONE">',
- help="FILE.ico: apply the icon to a Windows executable. FILE.exe,ID: extract the icon with ID from an exe. "
- "FILE.icns: apply the icon to the .app bundle on macOS. If an image file is entered that isn't in the "
- "platform format (ico on Windows, icns on Mac), PyInstaller tries to use Pillow to translate the icon into "
- "the correct format (if Pillow is installed). Use \"NONE\" to not apply any icon, thereby making the OS show "
- "some default (default: apply PyInstaller's icon). This option can be used multiple times.",
- )
- g.add_argument(
- "--disable-windowed-traceback",
- dest="disable_windowed_traceback",
- action="store_true",
- default=False,
- help="Disable traceback dump of unhandled exception in windowed (noconsole) mode (Windows and macOS only), "
- "and instead display a message that this feature is disabled.",
- )
- g = parser.add_argument_group('Windows specific options')
- g.add_argument(
- "--version-file",
- dest="version_file",
- metavar="FILE",
- help="Add a version resource from FILE to the exe.",
- )
- g.add_argument(
- "--manifest",
- metavar="<FILE or XML>",
- help="Add manifest FILE or XML to the exe.",
- )
- g.add_argument(
- "-m",
- dest="shorthand_manifest",
- metavar="<FILE or XML>",
- help="Deprecated shorthand for --manifest.",
- )
- g.add_argument(
- "--no-embed-manifest",
- action=_RemovedNoEmbedManifestAction,
- )
- g.add_argument(
- "-r",
- "--resource",
- dest="resources",
- metavar="RESOURCE",
- action="append",
- default=[],
- help="Add or update a resource to a Windows executable. The RESOURCE is one to four items, "
- "FILE[,TYPE[,NAME[,LANGUAGE]]]. FILE can be a data file or an exe/dll. For data files, at least TYPE and NAME "
- "must be specified. LANGUAGE defaults to 0 or may be specified as wildcard * to update all resources of the "
- "given TYPE and NAME. For exe/dll files, all resources from FILE will be added/updated to the final executable "
- "if TYPE, NAME and LANGUAGE are omitted or specified as wildcard *. This option can be used multiple times.",
- )
- g.add_argument(
- '--uac-admin',
- dest='uac_admin',
- action="store_true",
- default=False,
- help="Using this option creates a Manifest that will request elevation upon application start.",
- )
- g.add_argument(
- '--uac-uiaccess',
- dest='uac_uiaccess',
- action="store_true",
- default=False,
- help="Using this option allows an elevated application to work with Remote Desktop.",
- )
- g = parser.add_argument_group('Windows Side-by-side Assembly searching options (advanced)')
- g.add_argument(
- "--win-private-assemblies",
- action=_RemovedWinPrivateAssembliesAction,
- )
- g.add_argument(
- "--win-no-prefer-redirects",
- action=_RemovedWinNoPreferRedirectsAction,
- )
- g = parser.add_argument_group('macOS specific options')
- g.add_argument(
- "--argv-emulation",
- dest="argv_emulation",
- action="store_true",
- default=False,
- help="Enable argv emulation for macOS app bundles. If enabled, the initial open document/URL event is "
- "processed by the bootloader and the passed file paths or URLs are appended to sys.argv.",
- )
- g.add_argument(
- '--osx-bundle-identifier',
- dest='bundle_identifier',
- help="macOS .app bundle identifier is used as the default unique program name for code signing purposes. "
- "The usual form is a hierarchical name in reverse DNS notation. For example: com.mycompany.department.appname "
- "(default: first script's basename)",
- )
- g.add_argument(
- '--target-architecture',
- '--target-arch',
- dest='target_arch',
- metavar='ARCH',
- default=None,
- help="Target architecture (macOS only; valid values: x86_64, arm64, universal2). Enables switching between "
- "universal2 and single-arch version of frozen application (provided python installation supports the target "
- "architecture). If not target architecture is not specified, the current running architecture is targeted.",
- )
- g.add_argument(
- '--codesign-identity',
- dest='codesign_identity',
- metavar='IDENTITY',
- default=None,
- help="Code signing identity (macOS only). Use the provided identity to sign collected binaries and generated "
- "executable. If signing identity is not provided, ad-hoc signing is performed instead.",
- )
- g.add_argument(
- '--osx-entitlements-file',
- dest='entitlements_file',
- metavar='FILENAME',
- default=None,
- help="Entitlements file to use when code-signing the collected binaries (macOS only).",
- )
- g = parser.add_argument_group('Rarely used special options')
- g.add_argument(
- "--runtime-tmpdir",
- dest="runtime_tmpdir",
- metavar="PATH",
- help="Where to extract libraries and support files in `onefile` mode. If this option is given, the bootloader "
- "will ignore any temp-folder location defined by the run-time OS. The ``_MEIxxxxxx``-folder will be created "
- "here. Please use this option only if you know what you are doing. Note that on POSIX systems, PyInstaller's "
- "bootloader does NOT perform shell-style environment variable expansion on the given path string. Therefore, "
- "using environment variables (e.g., ``~`` or ``$HOME``) in path will NOT work.",
- )
- g.add_argument(
- "--bootloader-ignore-signals",
- action="store_true",
- default=False,
- help="Tell the bootloader to ignore signals rather than forwarding them to the child process. Useful in "
- "situations where for example a supervisor process signals both the bootloader and the child (e.g., via a "
- "process group) to avoid signalling the child twice.",
- )
- def main(
- scripts,
- name=None,
- onefile=False,
- console=True,
- debug=[],
- python_options=[],
- strip=False,
- noupx=False,
- upx_exclude=None,
- runtime_tmpdir=None,
- contents_directory=None,
- pathex=[],
- version_file=None,
- specpath=None,
- bootloader_ignore_signals=False,
- disable_windowed_traceback=False,
- datas=[],
- binaries=[],
- icon_file=None,
- manifest=None,
- resources=[],
- bundle_identifier=None,
- hiddenimports=[],
- hookspath=[],
- runtime_hooks=[],
- excludes=[],
- uac_admin=False,
- uac_uiaccess=False,
- collect_submodules=[],
- collect_binaries=[],
- collect_data=[],
- collect_all=[],
- copy_metadata=[],
- splash=None,
- recursive_copy_metadata=[],
- target_arch=None,
- codesign_identity=None,
- entitlements_file=None,
- argv_emulation=False,
- hide_console=None,
- optimize=None,
- splash_center=None,
- **_kwargs
- ):
- # Default values for onefile and console when not explicitly specified on command-line (indicated by None)
- if onefile is None:
- onefile = False
- if console is None:
- console = True
- # If appname is not specified - use the basename of the main script as name.
- if name is None:
- name = os.path.splitext(os.path.basename(scripts[0]))[0]
- # If specpath not specified - use default value - current working directory.
- if specpath is None:
- specpath = DEFAULT_SPECPATH
- else:
- # Expand starting tilde into user's home directory, as a work-around for tilde not being expanded by shell when
- # using `--specpath=~/path/abc` instead of `--specpath ~/path/abc` (or when the path argument is quoted).
- specpath = os.path.expanduser(specpath)
- # If cwd is the root directory of PyInstaller, generate the .spec file in ./appname/ subdirectory.
- if specpath == HOMEPATH:
- specpath = os.path.join(HOMEPATH, name)
- # Create directory tree if missing.
- if not os.path.exists(specpath):
- os.makedirs(specpath)
- # Handle additional EXE options.
- exe_options = ''
- if version_file:
- exe_options += "\n version='%s'," % escape_win_filepath(version_file)
- if uac_admin:
- exe_options += "\n uac_admin=True,"
- if uac_uiaccess:
- exe_options += "\n uac_uiaccess=True,"
- if icon_file:
- # Icon file for Windows.
- # On Windows, the default icon is embedded in the bootloader executable.
- if icon_file[0] == 'NONE':
- exe_options += "\n icon='NONE',"
- else:
- exe_options += "\n icon=[%s]," % ','.join("'%s'" % escape_win_filepath(ic) for ic in icon_file)
- # Icon file for macOS.
- # We need to encapsulate it into apostrofes.
- icon_file = "'%s'" % icon_file[0]
- else:
- # On macOS, the default icon has to be copied into the .app bundle.
- # The the text value 'None' means - use default icon.
- icon_file = 'None'
- if contents_directory:
- exe_options += "\n contents_directory='%s'," % (contents_directory or "_internal")
- if hide_console:
- exe_options += "\n hide_console='%s'," % hide_console
- if bundle_identifier:
- # We need to encapsulate it into apostrofes.
- bundle_identifier = "'%s'" % bundle_identifier
- if _kwargs["shorthand_manifest"]:
- manifest = _kwargs["shorthand_manifest"]
- logger.log(
- logging.DEPRECATION, "PyInstaller v7 will remove the -m shorthand flag. Please use --manifest=%s instead",
- manifest
- )
- if manifest:
- if "<" in manifest:
- # Assume XML string
- exe_options += "\n manifest='%s'," % manifest.replace("'", "\\'")
- else:
- # Assume filename
- exe_options += "\n manifest='%s'," % escape_win_filepath(manifest)
- if resources:
- resources = list(map(escape_win_filepath, resources))
- exe_options += "\n resources=%s," % repr(resources)
- hiddenimports = hiddenimports or []
- upx_exclude = upx_exclude or []
- if is_darwin and onefile and not console:
- from PyInstaller.building.osx import WINDOWED_ONEFILE_DEPRCATION
- logger.log(logging.DEPRECATION, WINDOWED_ONEFILE_DEPRCATION)
- # If file extension of the first script is '.pyw', force --windowed option.
- if is_win and os.path.splitext(scripts[0])[-1] == '.pyw':
- console = False
- # If script paths are relative, make them relative to the directory containing .spec file.
- scripts = [make_path_spec_relative(x, specpath) for x in scripts]
- # With absolute paths replace prefix with variable HOMEPATH.
- scripts = list(map(Path, scripts))
- # Translate the default of ``debug=None`` to an empty list.
- if debug is None:
- debug = []
- # Translate the ``all`` option.
- if DEBUG_ALL_CHOICE[0] in debug:
- debug = DEBUG_ARGUMENT_CHOICES
- # Create preamble (for collect_*() calls)
- preamble = Preamble(
- datas, binaries, hiddenimports, collect_data, collect_binaries, collect_submodules, collect_all, copy_metadata,
- recursive_copy_metadata
- )
- if splash:
- splash_options = ""
- if splash_center:
- splash_options = f"\n center={splash_center!r},"
- splash_init = splashtmpl % {
- 'splash_image': splash,
- 'splash_options': splash_options,
- }
- splash_binaries = "\n splash.binaries,"
- splash_target = "\n splash,"
- else:
- splash_init = splash_binaries = splash_target = ""
- # Infer byte-code optimization level.
- opt_level = sum([opt == 'O' for opt in python_options])
- if opt_level > 2:
- logger.warning(
- "The switch '--python-option O' has been specified %d times - it should be specified at most twice!",
- opt_level,
- )
- opt_level = 2
- if optimize is None:
- if opt_level == 0:
- # Infer from running python process
- optimize = sys.flags.optimize
- else:
- # Infer from `--python-option O` switch(es).
- optimize = opt_level
- elif optimize != opt_level and opt_level != 0:
- logger.warning(
- "Mismatch between optimization level passed via --optimize switch (%d) and number of '--python-option O' "
- "switches (%d)!",
- optimize,
- opt_level,
- )
- if optimize >= 0:
- # Ensure OPTIONs passed to bootloader match the optimization settings.
- python_options += max(0, optimize - opt_level) * ['O']
- # Create OPTIONs array
- if 'imports' in debug and 'v' not in python_options:
- python_options.append('v')
- python_options_array = [(opt, None, 'OPTION') for opt in python_options]
- d = {
- 'scripts': scripts,
- 'pathex': pathex or [],
- 'binaries': preamble.binaries,
- 'datas': preamble.datas,
- 'hiddenimports': preamble.hiddenimports,
- 'preamble': preamble.content,
- 'name': name,
- 'noarchive': 'noarchive' in debug,
- 'optimize': optimize,
- 'options': python_options_array,
- 'debug_bootloader': 'bootloader' in debug,
- 'bootloader_ignore_signals': bootloader_ignore_signals,
- 'strip': strip,
- 'upx': not noupx,
- 'upx_exclude': upx_exclude,
- 'runtime_tmpdir': runtime_tmpdir,
- 'exe_options': exe_options,
- # Directory with additional custom import hooks.
- 'hookspath': hookspath,
- # List with custom runtime hook files.
- 'runtime_hooks': runtime_hooks or [],
- # List of modules/packages to ignore.
- 'excludes': excludes or [],
- # only Windows and macOS distinguish windowed and console apps
- 'console': console,
- 'disable_windowed_traceback': disable_windowed_traceback,
- # Icon filename. Only macOS uses this item.
- 'icon': icon_file,
- # .app bundle identifier. Only macOS uses this item.
- 'bundle_identifier': bundle_identifier,
- # argv emulation (macOS only)
- 'argv_emulation': argv_emulation,
- # Target architecture (macOS only)
- 'target_arch': target_arch,
- # Code signing identity (macOS only)
- 'codesign_identity': codesign_identity,
- # Entitlements file (macOS only)
- 'entitlements_file': entitlements_file,
- # splash screen
- 'splash_init': splash_init,
- 'splash_target': splash_target,
- 'splash_binaries': splash_binaries,
- }
- # Write down .spec file to filesystem.
- specfnm = os.path.join(specpath, name + '.spec')
- with open(specfnm, 'w', encoding='utf-8') as specfile:
- if onefile:
- specfile.write(onefiletmplt % d)
- # For macOS create .app bundle.
- if is_darwin and not console:
- specfile.write(bundleexetmplt % d)
- else:
- specfile.write(onedirtmplt % d)
- # For macOS create .app bundle.
- if is_darwin and not console:
- specfile.write(bundletmplt % d)
- return specfnm
|