def __init__(self, toc, name=None, cdict=None, exclude_binaries=0, strip_binaries=False, upx_binaries=False, upx_exclude=None, target_arch=None, codesign_identity=None, entitlements_file=None): """ toc A TOC (Table of Contents) name An optional filename for the PKG. cdict Dictionary that specifies compression by typecode. For Example, PYZ is left uncompressed so that it can be accessed inside the PKG. The default uses sensible values. If zlib is not available, no compression is used. exclude_binaries If True, EXTENSIONs and BINARYs will be left out of the PKG, and forwarded to its container (usually a COLLECT). strip_binaries If True, use 'strip' command to reduce the size of binary files. upx_binaries """ Target.__init__(self) self.toc = toc self.cdict = cdict self.name = name if name is None: self.name = os.path.splitext(self.tocfilename)[0] + '.pkg' self.exclude_binaries = exclude_binaries self.strip_binaries = strip_binaries self.upx_binaries = upx_binaries self.upx_exclude = upx_exclude or [] self.target_arch = target_arch self.codesign_identity = codesign_identity self.entitlements_file = entitlements_file # This dict tells PyInstaller what items embedded in the executable should # be compressed. if self.cdict is None: self.cdict = { 'EXTENSION': COMPRESSED, 'DATA': COMPRESSED, 'BINARY': COMPRESSED, 'EXECUTABLE': COMPRESSED, 'PYSOURCE': COMPRESSED, 'PYMODULE': COMPRESSED, 'SPLASH': COMPRESSED, # Do not compress PYZ as a whole. Single modules are # compressed when creating PYZ archive. 'PYZ': UNCOMPRESSED } self.__postinit__()
def __init__(self, *tocs, **kwargs): """ tocs One or more TOCs (Tables of Contents), normally an Analysis.pure. If this TOC has an attribute `_code_cache`, this is expected to be a dict of module code objects from ModuleGraph. kwargs Possible keywork arguments: name A filename for the .pyz. Normally not needed, as the generated name will do fine. cipher The block cipher that will be used to encrypt Python bytecode. """ from ..config import CONF Target.__init__(self) name = kwargs.get('name', None) cipher = kwargs.get('cipher', None) self.toc = TOC() # If available, use code objects directly from ModuleGraph to # speed up PyInstaller. self.code_dict = {} for t in tocs: self.toc.extend(t) self.code_dict.update(getattr(t, '_code_cache', {})) # Paths to remove from filenames embedded in code objects self.replace_paths = sys.path + CONF['pathex'] # Make sure paths end with os.sep self.replace_paths = [os.path.join(f, '') for f in self.replace_paths] self.name = name if name is None: self.name = os.path.splitext(self.tocfilename)[0] + '.pyz' # PyInstaller bootstrapping modules. self.dependencies = get_bootstrap_modules() # Bundle the crypto key. self.cipher = cipher if cipher: key_file = ('pyimod00_crypto_key', os.path.join(CONF['workpath'], 'pyimod00_crypto_key.pyc'), 'PYMODULE') # Insert the key as the first module in the list. The key module contains # just variables and does not depend on other modules. self.dependencies.insert(0, key_file) # Compile the top-level modules so that they end up in the CArchive and can be # imported by the bootstrap script. self.dependencies = misc.compile_py_files(self.dependencies, CONF['workpath']) self.__postinit__()
def __init__(self, *args, **kws): """ args One or more arguments that are either TOCs Targets. kws Possible keywork arguments: name The name of the directory to be built. """ from PyInstaller.config import CONF Target.__init__(self) self.strip_binaries = kws.get('strip', False) self.upx_exclude = kws.get("upx_exclude", []) self.console = True self.target_arch = None self.codesign_identity = None self.entitlements_file = None if CONF['hasUPX']: self.upx_binaries = kws.get('upx', False) else: self.upx_binaries = False self.name = kws.get('name') # Old .spec format included in 'name' the path where to collect files # for the created app. # app. New format includes only directory name. # # The 'name' directory is created in DISTPATH and necessary files are # then collected to this directory. self.name = os.path.join(CONF['distpath'], os.path.basename(self.name)) self.toc = TOC() for arg in args: if isinstance(arg, TOC): self.toc.extend(arg) elif isinstance(arg, Target): self.toc.append( (os.path.basename(arg.name), arg.name, arg.typ)) if isinstance(arg, EXE): self.console = arg.console self.target_arch = arg.target_arch self.codesign_identity = arg.codesign_identity self.entitlements_file = arg.entitlements_file for tocnm, fnm, typ in arg.toc: if tocnm == os.path.basename(arg.name) + ".manifest": self.toc.append((tocnm, fnm, typ)) if not arg.append_pkg: self.toc.append((os.path.basename(arg.pkgname), arg.pkgname, 'PKG')) self.toc.extend(arg.dependencies) else: self.toc.extend(arg) self.__postinit__()
def __init__(self, *args, **kws): """ args One or more arguments that are either TOCs Targets. kws Possible keywork arguments: name The name of the directory to be built. """ from ..config import CONF Target.__init__(self) self.strip_binaries = kws.get('strip', False) self.console = True if CONF['hasUPX']: self.upx_binaries = kws.get('upx', False) else: self.upx_binaries = False self.name = kws.get('name') # Old .spec format included in 'name' the path where to collect files # for the created app. # app. New format includes only directory name. # # The 'name' directory is created in DISTPATH and necessary files are # then collected to this directory. self.name = os.path.join(CONF['distpath'], os.path.basename(self.name)) self.toc = TOC() for arg in args: if isinstance(arg, TOC): self.toc.extend(arg) elif isinstance(arg, Target): self.toc.append((os.path.basename(arg.name), arg.name, arg.typ)) if isinstance(arg, EXE): self.console = arg.console for tocnm, fnm, typ in arg.toc: if tocnm == os.path.basename(arg.name) + ".manifest": self.toc.append((tocnm, fnm, typ)) if not arg.append_pkg: self.toc.append((os.path.basename(arg.pkgname), arg.pkgname, 'PKG')) self.toc.extend(arg.dependencies) else: self.toc.extend(arg) self.__postinit__()
def __init__(self, toc, name=None, cdict=None, exclude_binaries=0, strip_binaries=False, upx_binaries=False): """ toc A TOC (Table of Contents) name An optional filename for the PKG. cdict Dictionary that specifies compression by typecode. For Example, PYZ is left uncompressed so that it can be accessed inside the PKG. The default uses sensible values. If zlib is not available, no compression is used. exclude_binaries If True, EXTENSIONs and BINARYs will be left out of the PKG, and forwarded to its container (usually a COLLECT). strip_binaries If True, use 'strip' command to reduce the size of binary files. upx_binaries """ Target.__init__(self) self.toc = toc self.cdict = cdict self.name = name if name is None: self.name = os.path.splitext(self.tocfilename)[0] + '.pkg' self.exclude_binaries = exclude_binaries self.strip_binaries = strip_binaries self.upx_binaries = upx_binaries # This dict tells PyInstaller what items embedded in the executable should # be compressed. if self.cdict is None: self.cdict = {'EXTENSION': COMPRESSED, 'DATA': COMPRESSED, 'BINARY': COMPRESSED, 'EXECUTABLE': COMPRESSED, 'PYSOURCE': COMPRESSED, 'PYMODULE': COMPRESSED, # Do not compress PYZ as a whole. Single modules are # compressed when creating PYZ archive. 'PYZ': UNCOMPRESSED} self.__postinit__()
def __init__(self, *args, **kwargs): """ args One or more arguments that are either TOCs Targets. kwargs Possible keywork arguments: bootloader_ignore_signals Non-Windows only. If True, the bootloader process will ignore all ignorable signals. If False (default), it will forward all signals to the child process. Useful in situations where e.g. a supervisor process signals both the bootloader and child (e.g. via a process group) to avoid signalling the child twice. console On Windows or OSX governs whether to use the console executable or the windowed executable. Always True on Linux/Unix (always console executable - it does not matter there). debug Setting to True gives you progress mesages from the executable (for console=False there will be annoying MessageBoxes on Windows). name The filename for the executable. On Windows suffix '.exe' is appended. exclude_binaries Forwarded to the PKG the EXE builds. icon Windows or OSX only. icon='myicon.ico' to use an icon file or icon='notepad.exe,0' to grab an icon resource. version Windows only. version='myversion.txt'. Use grab_version.py to get a version resource from an executable and then edit the output to create your own. (The syntax of version resources is so arcane that I wouldn't attempt to write one from scratch). uac_admin Windows only. Setting to True creates a Manifest with will request elevation upon application restart uac_uiaccess Windows only. Setting to True allows an elevated application to work with Remote Desktop """ from ..config import CONF Target.__init__(self) # Available options for EXE in .spec files. self.exclude_binaries = kwargs.get('exclude_binaries', False) self.bootloader_ignore_signals = kwargs.get( 'bootloader_ignore_signals', False) self.console = kwargs.get('console', True) self.debug = kwargs.get('debug', False) self.name = kwargs.get('name', None) self.icon = kwargs.get('icon', None) self.versrsrc = kwargs.get('version', None) self.manifest = kwargs.get('manifest', None) self.resources = kwargs.get('resources', []) self.strip = kwargs.get('strip', False) self.runtime_tmpdir = kwargs.get('runtime_tmpdir', None) # If ``append_pkg`` is false, the archive will not be appended # to the exe, but copied beside it. self.append_pkg = kwargs.get('append_pkg', True) # On Windows allows the exe to request admin privileges. self.uac_admin = kwargs.get('uac_admin', False) self.uac_uiaccess = kwargs.get('uac_uiaccess', False) if CONF['hasUPX']: self.upx = kwargs.get('upx', False) else: self.upx = False # Old .spec format included in 'name' the path where to put created # app. New format includes only exename. # # Ignore fullpath in the 'name' and prepend DISTPATH or WORKPATH. # DISTPATH - onefile # WORKPATH - onedir if self.exclude_binaries: # onedir mode - create executable in WORKPATH. self.name = os.path.join(CONF['workpath'], os.path.basename(self.name)) else: # onefile mode - create executable in DISTPATH. self.name = os.path.join(CONF['distpath'], os.path.basename(self.name)) # Old .spec format included on Windows in 'name' .exe suffix. if is_win or is_cygwin: # Append .exe suffix if it is not already there. if not self.name.endswith('.exe'): self.name += '.exe' base_name = os.path.splitext(os.path.basename(self.name))[0] else: base_name = os.path.basename(self.name) self.pkgname = base_name + '.pkg' self.toc = TOC() for arg in args: if isinstance(arg, TOC): self.toc.extend(arg) elif isinstance(arg, Target): self.toc.append( (os.path.basename(arg.name), arg.name, arg.typ)) self.toc.extend(arg.dependencies) else: self.toc.extend(arg) if self.runtime_tmpdir is not None: self.toc.append( ("pyi-runtime-tmpdir " + self.runtime_tmpdir, "", "OPTION")) if self.bootloader_ignore_signals: # no value; presence means "true" self.toc.append(("pyi-bootloader-ignore-signals", "", "OPTION")) if is_win: filename = os.path.join(CONF['workpath'], CONF['specnm'] + ".exe.manifest") self.manifest = winmanifest.create_manifest( filename, self.manifest, self.console, self.uac_admin, self.uac_uiaccess) manifest_filename = os.path.basename(self.name) + ".manifest" self.toc.append((manifest_filename, filename, 'BINARY')) if not self.exclude_binaries: # Onefile mode: manifest file is explicitly loaded. # Store name of manifest file as bootloader option. Allows # the exe to be renamed. self.toc.append( ("pyi-windows-manifest-filename " + manifest_filename, "", "OPTION")) self.pkg = PKG( self.toc, cdict=kwargs.get('cdict', None), exclude_binaries=self.exclude_binaries, strip_binaries=self.strip, upx_binaries=self.upx, ) self.dependencies = self.pkg.dependencies # Get the path of the bootloader and store it in a TOC, so it # can be checked for being changed. exe = self._bootloader_file('run', '.exe' if is_win or is_cygwin else '') self.exefiles = TOC([(os.path.basename(exe), exe, 'EXECUTABLE')]) self.__postinit__()
def __init__(self, *args, **kwargs): """ args One or more arguments that are either TOCs Targets. kwargs Possible keywork arguments: console On Windows or OSX governs whether to use the console executable or the windowed executable. Always True on Linux/Unix (always console executable - it does not matter there). debug Setting to True gives you progress mesages from the executable (for console=False there will be annoying MessageBoxes on Windows). name The filename for the executable. On Windows suffix '.exe' is appended. exclude_binaries Forwarded to the PKG the EXE builds. icon Windows or OSX only. icon='myicon.ico' to use an icon file or icon='notepad.exe,0' to grab an icon resource. version Windows only. version='myversion.txt'. Use grab_version.py to get a version resource from an executable and then edit the output to create your own. (The syntax of version resources is so arcane that I wouldn't attempt to write one from scratch). uac_admin Windows only. Setting to True creates a Manifest with will request elevation upon application restart uac_uiaccess Windows only. Setting to True allows an elevated application to work with Remote Desktop """ from ..config import CONF Target.__init__(self) # Available options for EXE in .spec files. self.exclude_binaries = kwargs.get('exclude_binaries', False) self.console = kwargs.get('console', True) self.debug = kwargs.get('debug', False) self.name = kwargs.get('name', None) self.icon = kwargs.get('icon', None) self.versrsrc = kwargs.get('version', None) self.manifest = kwargs.get('manifest', None) self.resources = kwargs.get('resources', []) self.strip = kwargs.get('strip', False) # If ``append_pkg`` is false, the archive will not be appended # to the exe, but copied beside it. self.append_pkg = kwargs.get('append_pkg', True) # On Windows allows the exe to request admin privileges. self.uac_admin = kwargs.get('uac_admin', False) self.uac_uiaccess = kwargs.get('uac_uiaccess', False) if CONF['hasUPX']: self.upx = kwargs.get('upx', False) else: self.upx = False # Old .spec format included in 'name' the path where to put created # app. New format includes only exename. # # Ignore fullpath in the 'name' and prepend DISTPATH or WORKPATH. # DISTPATH - onefile # WORKPATH - onedir if self.exclude_binaries: # onedir mode - create executable in WORKPATH. self.name = os.path.join(CONF['workpath'], os.path.basename(self.name)) else: # onefile mode - create executable in DISTPATH. self.name = os.path.join(CONF['distpath'], os.path.basename(self.name)) # Old .spec format included on Windows in 'name' .exe suffix. if is_win or is_cygwin: # Append .exe suffix if it is not already there. if not self.name.endswith('.exe'): self.name += '.exe' base_name = os.path.splitext(os.path.basename(self.name))[0] else: base_name = os.path.basename(self.name) self.pkgname = base_name + '.pkg' self.toc = TOC() for arg in args: if isinstance(arg, TOC): self.toc.extend(arg) elif isinstance(arg, Target): self.toc.append((os.path.basename(arg.name), arg.name, arg.typ)) self.toc.extend(arg.dependencies) else: self.toc.extend(arg) if is_win: filename = os.path.join(CONF['workpath'], CONF['specnm'] + ".exe.manifest") self.manifest = winmanifest.create_manifest(filename, self.manifest, self.console, self.uac_admin, self.uac_uiaccess) manifest_filename = os.path.basename(self.name) + ".manifest" self.toc.append((manifest_filename, filename, 'BINARY')) if not self.exclude_binaries: # Onefile mode: manifest file is explicitly loaded. # Store name of manifest file as bootloader option. Allows # the exe to be renamed. self.toc.append(("pyi-windows-manifest-filename " + manifest_filename, "", "OPTION")) self.pkg = PKG(self.toc, cdict=kwargs.get('cdict', None), exclude_binaries=self.exclude_binaries, strip_binaries=self.strip, upx_binaries=self.upx, ) self.dependencies = self.pkg.dependencies # Get the path of the bootloader and store it in a TOC, so it # can be checked for being changed. exe = self._bootloader_file('run', '.exe' if is_win or is_cygwin else '') self.exefiles = TOC([(os.path.basename(exe), exe, 'EXECUTABLE')]) self.__postinit__()
def __init__(self, *args, **kws): from PyInstaller.config import CONF # BUNDLE only has a sense under Mac OS X, it's a noop on other platforms if not is_darwin: return # get a path to a .icns icon for the app bundle. self.icon = kws.get('icon') if not self.icon: # --icon not specified; use the default in the pyinstaller folder self.icon = os.path.join( os.path.dirname(os.path.dirname(__file__)), 'bootloader', 'images', 'icon-windowed.icns') else: # user gave an --icon=path. If it is relative, make it # relative to the spec file location. if not os.path.isabs(self.icon): self.icon = os.path.join(CONF['specpath'], self.icon) # ensure icon path is absolute self.icon = os.path.abspath(self.icon) Target.__init__(self) # .app bundle is created in DISTPATH. self.name = kws.get('name', None) base_name = os.path.basename(self.name) self.name = os.path.join(CONF['distpath'], base_name) self.appname = os.path.splitext(base_name)[0] self.version = kws.get("version", "0.0.0") self.toc = TOC() self.strip = False self.upx = False self.console = True self.target_arch = None self.codesign_identity = None self.entitlements_file = None # .app bundle identifier for Code Signing self.bundle_identifier = kws.get('bundle_identifier') if not self.bundle_identifier: # Fallback to appname. self.bundle_identifier = self.appname self.info_plist = kws.get('info_plist', None) for arg in args: if isinstance(arg, EXE): self.toc.append( (os.path.basename(arg.name), arg.name, arg.typ)) self.toc.extend(arg.dependencies) self.strip = arg.strip self.upx = arg.upx self.upx_exclude = arg.upx_exclude self.console = arg.console self.target_arch = arg.target_arch self.codesign_identity = arg.codesign_identity self.entitlements_file = arg.entitlements_file elif isinstance(arg, TOC): self.toc.extend(arg) # TOC doesn't have a strip or upx attribute, so there is no way for us to # tell which cache we should draw from. elif isinstance(arg, COLLECT): self.toc.extend(arg.toc) self.strip = arg.strip_binaries self.upx = arg.upx_binaries self.upx_exclude = arg.upx_exclude self.console = arg.console self.target_arch = arg.target_arch self.codesign_identity = arg.codesign_identity self.entitlements_file = arg.entitlements_file else: logger.info("unsupported entry %s", arg.__class__.__name__) # Now, find values for app filepath (name), app name (appname), and name # of the actual executable (exename) from the first EXECUTABLE item in # toc, which might have come from a COLLECT too (not from an EXE). for inm, name, typ in self.toc: if typ == "EXECUTABLE": self.exename = name break self.__postinit__()
def __init__(self, *args, **kwargs): """ args One or more arguments that are either TOCs Targets. kwargs Possible keyword arguments: bootloader_ignore_signals Non-Windows only. If True, the bootloader process will ignore all ignorable signals. If False (default), it will forward all signals 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. console On Windows or Mac OS governs whether to use the console executable or the windowed executable. Always True on Linux/Unix (always console executable - it does not matter there). disable_windowed_traceback Disable traceback dump of unhandled exception in windowed (noconsole) mode (Windows and macOS only), and instead display a message that this feature is disabled. debug Setting to True gives you progress messages from the executable (for console=False there will be annoying MessageBoxes on Windows). name The filename for the executable. On Windows suffix '.exe' is appended. exclude_binaries Forwarded to the PKG the EXE builds. icon Windows and Mac OS only. icon='myicon.ico' to use an icon file or icon='notepad.exe,0' to grab an icon resource. Defaults to use PyInstaller's console or windowed icon. Use icon=`NONE` to not add any icon. version Windows only. version='myversion.txt'. Use grab_version.py to get a version resource from an executable and then edit the output to create your own. (The syntax of version resources is so arcane that I would not attempt to write one from scratch). uac_admin Windows only. Setting to True creates a Manifest with will request elevation upon application start. uac_uiaccess Windows only. Setting to True allows an elevated application to work with Remote Desktop. embed_manifest Windows only. Setting to True (the default) embeds the manifest into the executable. Setting to False generates an external .exe.manifest file. Applicable only in onedir mode (exclude_binaries=True); in onefile mode (exclude_binaries=False), the manifest is always embedded in the executable, regardless of this option. target_arch macOS only. Used to explicitly specify the target architecture; either single-arch ('x86_64' or 'arm64') or 'universal2'. Used in checks that the collected binaries contain the requires arch slice(s) and/or to convert fat binaries into thin ones as necessary. If not specified (default), a single-arch build corresponding to running architecture is assumed. codesign_identity macOS only. Use the provided identity to sign collected binaries and the generated executable. If signing identity is not provided, ad-hoc signing is performed. entitlements_file macOS only. Optional path to entitlements file to use with code signing of collected binaries (--entitlements option to codesign utility). """ from PyInstaller.config import CONF Target.__init__(self) # Available options for EXE in .spec files. self.exclude_binaries = kwargs.get('exclude_binaries', False) self.bootloader_ignore_signals = kwargs.get('bootloader_ignore_signals', False) self.console = kwargs.get('console', True) self.disable_windowed_traceback = kwargs.get('disable_windowed_traceback', False) self.debug = kwargs.get('debug', False) self.name = kwargs.get('name', None) self.icon = kwargs.get('icon', None) self.versrsrc = kwargs.get('version', None) self.manifest = kwargs.get('manifest', None) self.embed_manifest = kwargs.get('embed_manifest', True) self.resources = kwargs.get('resources', []) self.strip = kwargs.get('strip', False) self.upx_exclude = kwargs.get("upx_exclude", []) self.runtime_tmpdir = kwargs.get('runtime_tmpdir', None) # If ``append_pkg`` is false, the archive will not be appended to the exe, but copied beside it. self.append_pkg = kwargs.get('append_pkg', True) # On Windows allows the exe to request admin privileges. self.uac_admin = kwargs.get('uac_admin', False) self.uac_uiaccess = kwargs.get('uac_uiaccess', False) # Target architecture (macOS only) self.target_arch = kwargs.get('target_arch', None) if is_darwin: if self.target_arch is None: import platform self.target_arch = platform.machine() else: assert self.target_arch in {'x86_64', 'arm64', 'universal2'}, \ f"Unsupported target arch: {self.target_arch}" logger.info("EXE target arch: %s", self.target_arch) else: self.target_arch = None # explicitly disable # Code signing identity (macOS only) self.codesign_identity = kwargs.get('codesign_identity', None) if is_darwin: logger.info("Code signing identity: %s", self.codesign_identity) else: self.codesign_identity = None # explicitly disable # Code signing entitlements self.entitlements_file = kwargs.get('entitlements_file', None) if CONF['hasUPX']: self.upx = kwargs.get('upx', False) else: self.upx = False # Old .spec format included in 'name' the path where to put created app. New format includes only exename. # # Ignore fullpath in the 'name' and prepend DISTPATH or WORKPATH. # DISTPATH - onefile # WORKPATH - onedir if self.exclude_binaries: # onedir mode - create executable in WORKPATH. self.name = os.path.join(CONF['workpath'], os.path.basename(self.name)) else: # onefile mode - create executable in DISTPATH. self.name = os.path.join(CONF['distpath'], os.path.basename(self.name)) # Old .spec format included on Windows in 'name' .exe suffix. if is_win or is_cygwin: # Append .exe suffix if it is not already there. if not self.name.endswith('.exe'): self.name += '.exe' base_name = os.path.splitext(os.path.basename(self.name))[0] else: base_name = os.path.basename(self.name) # Create the CArchive PKG in WORKPATH. When instancing PKG(), set name so that guts check can test whether the # file already exists. self.pkgname = os.path.join(CONF['workpath'], base_name + '.pkg') self.toc = TOC() for arg in args: if isinstance(arg, TOC): self.toc.extend(arg) elif isinstance(arg, Target): self.toc.append((os.path.basename(arg.name), arg.name, arg.typ)) self.toc.extend(arg.dependencies) else: self.toc.extend(arg) if self.runtime_tmpdir is not None: self.toc.append(("pyi-runtime-tmpdir " + self.runtime_tmpdir, "", "OPTION")) if self.bootloader_ignore_signals: # no value; presence means "true" self.toc.append(("pyi-bootloader-ignore-signals", "", "OPTION")) if self.disable_windowed_traceback: # no value; presence means "true" self.toc.append(("pyi-disable-windowed-traceback", "", "OPTION")) if is_win: if not self.exclude_binaries: # onefile mode forces embed_manifest=True if not self.embed_manifest: logger.warning("Ignoring embed_manifest=False setting in onefile mode!") self.embed_manifest = True if not self.icon: # --icon not specified; use default from bootloader folder if self.console: ico = 'icon-console.ico' else: ico = 'icon-windowed.ico' self.icon = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'bootloader', 'images', ico) filename = os.path.join(CONF['workpath'], CONF['specnm'] + ".exe.manifest") self.manifest = winmanifest.create_manifest( filename, self.manifest, self.console, self.uac_admin, self.uac_uiaccess ) manifest_filename = os.path.basename(self.name) + ".manifest" # If external manifest file is requested (supported only in onedir mode), add the file to the TOC in order # for it to be collected as an external manifest file. Otherwise, the assembly pipeline will embed the # manifest into the executable later on. if not self.embed_manifest: self.toc.append((manifest_filename, filename, 'BINARY')) if self.versrsrc: if not isinstance(self.versrsrc, versioninfo.VSVersionInfo) and not os.path.isabs(self.versrsrc): # relative version-info path is relative to spec file self.versrsrc = os.path.join(CONF['specpath'], self.versrsrc) self.pkg = PKG( self.toc, name=self.pkgname, cdict=kwargs.get('cdict', None), exclude_binaries=self.exclude_binaries, strip_binaries=self.strip, upx_binaries=self.upx, upx_exclude=self.upx_exclude, target_arch=self.target_arch, codesign_identity=self.codesign_identity, entitlements_file=self.entitlements_file ) self.dependencies = self.pkg.dependencies # Get the path of the bootloader and store it in a TOC, so it can be checked for being changed. exe = self._bootloader_file('run', '.exe' if is_win or is_cygwin else '') self.exefiles = TOC([(os.path.basename(exe), exe, 'EXECUTABLE')]) self.__postinit__()
def __init__(self, image_file, binaries, datas, **kwargs): """ :param str image_file: A path-like object to the image to be used. Only the PNG file format is supported. .. note:: If a different file format is supplied and PIL (Pillow) is installed, the file will be converted automatically. .. note:: *Windows*: Due to the implementation, the color Magenta/ RGB(255, 0, 255) must not be used in the image or text. .. note:: If PIL (Pillow) is installed and the image is bigger than max_img_size, the image will be resized to fit into the specified area. :param TOC binaries: The TOC of binaries the Analysis build target found. This TOC includes all extensionmodules and their dependencies. This is required to figure out, if the users program uses tkinter. :param TOC datas: The TOC of data the Analysis build target found. This TOC includes all data-file dependencies of the modules. This is required to check if all splash screen requirements can be bundled. :keyword text_pos: An optional 2x integer tuple that represents the origin of the text on the splash screen image. The origin of the text is its lower left corner. A unit in the respective coordinate system is a pixel of the image, its origin lies in the top left corner of the image. This parameter also acts like a switch for the text feature. If omitted, no text will be displayed on the splash screen. This text will be used to show textual progress in onefile mode. :type text_pos: Tuple[int, int] :keyword text_size: The desired size of the font. If the size argument is a positive number, it is interpreted as a size in points. If size is a negative number, its absolute value is interpreted as a size in pixels. Default: ``12`` :type text_size: int :keyword text_font: An optional name of a font for the text. This font must be installed on the user system, otherwise the system default font is used. If this parameter is omitted, the default font is also used. :keyword text_color: An optional color for the text. Either RGB HTML notation or color names are supported. Default: black (Windows: Due to a implementation issue the color magenta/ rgb(255, 0, 255) is forbidden) :type text_color: str :keyword text_default: The default text which will be displayed before the extraction starts. Default: "Initializing" :type text_default: str :keyword full_tk: By default Splash bundles only the necessary files for the splash screen (some tk components). This options enables adding full tk and making it a requirement, meaning all tk files will be unpacked before the splash screen can be started. This is useful during development of the splash screen script. Default: ``False`` :type full_tk: bool :keyword minify_script: The splash screen is created by executing an Tcl/Tk script. This option enables minimizing the script, meaning removing all non essential parts from the script. Default: True :keyword rundir: The folder name in which tcl/tk will be extracted at runtime. There should be no matching folder in your application to avoid conflicts. Default: ``__splash`` :type rundir: str :keyword name: An optional alternative filename for the .res file. If not specified, a name is generated. :type name: str :keyword script_name: An optional alternative filename for the Tcl script, that will be generated. If not specified, a name is generated. :type script_name: str :keyword max_img_size: Maximum size of the splash screen image as a tuple. If the supplied image exceeds this limit, it will be resized to fit the maximum width (to keep the original aspect ratio). This option can be disabled by setting it to None. Default: (760, 480) :type max_img_size: Tuple[int, int] """ from ..config import CONF Target.__init__(self) # Splash screen is not supported on macOS. It operates in a secondary thread and macOS disallows UI operations # in any thread other than main. if is_darwin: raise SystemExit("Splash screen is not supported on macOS.") # Make image path relative to .spec file if not os.path.isabs(image_file): image_file = os.path.join(CONF['specpath'], image_file) image_file = os.path.normpath(image_file) if not os.path.exists(image_file): raise ValueError("Image file '%s' not found" % image_file) # Copy all arguments self.image_file = image_file self.full_tk = kwargs.get("full_tk", False) self.name = kwargs.get("name", None) self.script_name = kwargs.get("script_name", None) self.minify_script = kwargs.get("minify_script", True) self.rundir = kwargs.get("rundir", None) self.max_img_size = kwargs.get("max_img_size", (760, 480)) # text options self.text_pos = kwargs.get("text_pos", None) self.text_size = kwargs.get("text_size", 12) self.text_font = kwargs.get("text_font", "TkDefaultFont") self.text_color = kwargs.get("text_color", "black") self.text_default = kwargs.get("text_default", "Initializing") # Save the generated file separately so that it is not necessary to generate the data again and again root = os.path.splitext(self.tocfilename)[0] if self.name is None: self.name = root + '.res' if self.script_name is None: self.script_name = root + '_script.tcl' if self.rundir is None: self.rundir = self._find_rundir(binaries + datas) # Internal variables try: # Do not import _tkinter at the toplevel, because on some systems _tkinter will fail to load, since it is # not installed. This would cause a runtime error in PyInstaller, since this module is imported from # build_main.py, instead we just want to inform the user that the splash screen feature is not supported on # his platform import _tkinter self._tkinter_module = _tkinter self._tkinter_file = self._tkinter_module.__file__ except ModuleNotFoundError: raise SystemExit( "You platform does not support the splash screen feature, since tkinter is not installed. Please " "install tkinter and try again." ) # Calculated / analysed values self.uses_tkinter = self._uses_tkinter(binaries) self.script = self.generate_script() self.tcl_lib, self.tk_lib = find_tcl_tk_shared_libs(self._tkinter_file) if is_darwin: # Outdated Tcl/Tk 8.5 system framework is not supported. Depending on macOS version, the library path will # come up empty (hidden system libraries on Big Sur), or will be # [/System]/Library/Frameworks/Tcl.framework/Tcl if self.tcl_lib[1] is None or 'Library/Frameworks/Tcl.framework' in self.tcl_lib[1]: raise SystemExit("The splash screen feature does not support macOS system framework version of Tcl/Tk.") # Check if tcl/tk was found assert all(self.tcl_lib) assert all(self.tk_lib) logger.debug("Use Tcl Library from %s and Tk From %s" % (self.tcl_lib, self.tk_lib)) self.splash_requirements = set([self.tcl_lib[0], self.tk_lib[0]] + splash_requirements) logger.info("Collect tcl/tk binaries for the splash screen") tcltk_tree = collect_tcl_tk_files(self._tkinter_file) if self.full_tk: # The user wants a full copy of tk, so make all tk files a requirement. self.splash_requirements.update(toc[0] for toc in tcltk_tree) self.binaries = TOC() if not self.uses_tkinter: # The user's script does not use tkinter, so we need to provide a TOC of all necessary files add the shared # libraries to the binaries. self.binaries.append((self.tcl_lib[0], self.tcl_lib[1], 'BINARY')) self.binaries.append((self.tk_lib[0], self.tk_lib[1], 'BINARY')) # Only add the intersection of the required and the collected resources, or add all entries if full_tk is # true. self.binaries.extend(toc for toc in tcltk_tree if toc[0] in self.splash_requirements) # Check if all requirements were found. fnames = [toc[0] for toc in (binaries + datas + self.binaries)] def _filter(_item): if _item not in fnames: # Item is not bundled, so warn the user about it. This actually may happen on some tkinter installations # that are missing the license.terms file. logger.warning( "The local Tcl/Tk installation is missing the file %s. The behavior of the splash screen is " "therefore undefined and may be unsupported." % _item ) return False return True # Remove all files which were not found. self.splash_requirements = set(filter(_filter, self.splash_requirements)) # Test if the tcl/tk version is supported by the bootloader. self.test_tk_version() logger.debug("Splash Requirements: %s" % self.splash_requirements) self.__postinit__()