Exemple #1
0
 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__()
Exemple #2
0
    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__()
Exemple #3
0
    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__()
Exemple #4
0
    def _check_guts(self, data, last_build):
        if not os.path.exists(self.name):
            logger.info("Rebuilding %s because %s missing", self.tocbasename,
                        os.path.basename(self.name))
            return 1
        if not self.append_pkg and not os.path.exists(self.pkgname):
            logger.info("Rebuilding because %s missing",
                        os.path.basename(self.pkgname))
            return 1

        if Target._check_guts(self, data, last_build):
            return True

        if (data['versrsrc'] or data['resources']) and not is_win:
            # todo: really ignore :-)
            logger.warning(
                'ignoring version, manifest and resources, platform not capable'
            )
        if data['icon'] and not (is_win or is_darwin):
            logger.warning('ignoring icon, platform not capable')

        mtm = data['mtm']
        if mtm != misc.mtime(self.name):
            logger.info("Rebuilding %s because mtimes don't match",
                        self.tocbasename)
            return True
        if mtm < misc.mtime(self.pkg.tocfilename):
            logger.info("Rebuilding %s because pkg is more recent",
                        self.tocbasename)
            return True
        return False
Exemple #5
0
    def _check_guts(self, data, last_build):
        if not os.path.exists(self.name):
            logger.info("Rebuilding %s because %s missing",
                        self.tocbasename, os.path.basename(self.name))
            return 1
        if not self.append_pkg and not os.path.exists(self.pkgname):
            logger.info("Rebuilding because %s missing",
                        os.path.basename(self.pkgname))
            return 1

        if Target._check_guts(self, data, last_build):
            return True

        if (data['versrsrc'] or data['resources']) and not is_win:
            # todo: really ignore :-)
            logger.warn('ignoring version, manifest and resources, platform not capable')
        if data['icon'] and not (is_win or is_darwin):
            logger.warn('ignoring icon, platform not capable')

        mtm = data['mtm']
        if mtm != misc.mtime(self.name):
            logger.info("Rebuilding %s because mtimes don't match", self.tocbasename)
            return True
        if mtm < misc.mtime(self.pkg.tocfilename):
            logger.info("Rebuilding %s because pkg is more recent", self.tocbasename)
            return True
        return False
Exemple #6
0
    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 _check_guts(self, data, last_build):
        if Target._check_guts(self, data, last_build):
            return True

        # Check if the image has been modified.
        if misc.mtime(self.image_file) > last_build:
            logger.info("Building %s because file %s changed", self.tocbasename, self.image_file)
            return True

        return False
Exemple #8
0
 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__()
Exemple #9
0
    def _check_guts(self, data, last_build):
        if Target._check_guts(self, data, last_build):
            return True
        for fnm in self.inputs:
            if mtime(fnm) > last_build:
                logger.info("Building because %s changed", fnm)
                return True
        # Now we know that none of the input parameters and none of the input files has changed. So take the values
        # calculated resp. analysed in the last run and store them in `self`.
        self.scripts = TOC(data['scripts'])
        self.pure = TOC(data['pure'])
        self.binaries = TOC(data['binaries'])
        self.zipfiles = TOC(data['zipfiles'])
        self.zipped_data = TOC(data['zipped_data'])
        self.datas = TOC(data['datas'])

        # Store previously found binding redirects in CONF for later use by PKG/COLLECT
        from PyInstaller.config import CONF
        self.binding_redirects = CONF['binding_redirects'] = data[
            'binding_redirects']

        return False
Exemple #10
0
    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__()
Exemple #11
0
 def _check_guts(self, data, last_build):
     if Target._check_guts(self, data, last_build):
         return True
     return False
Exemple #12
0
    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__()
Exemple #13
0
 def _check_guts(self, data, last_build):
     if Target._check_guts(self, data, last_build):
         return True
     return False
Exemple #14
0
    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__()
Exemple #15
0
    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__()
Exemple #16
0
    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__()