예제 #1
0
def pyi_modgraph():
    # Explicitly set the log level since the plugin `pytest-catchlog` (un-)
    # sets the root logger's level to NOTSET for the setup phase, which will
    # lead to TRACE messages been written out.
    import PyInstaller.log as logging
    logging.logger.setLevel(logging.DEBUG)
    initialize_modgraph()
예제 #2
0
def pyi_modgraph():
    # Explicitly set the log level since the plugin `pytest-catchlog` (un-)
    # sets the root logger's level to NOTSET for the setup phase, which will
    # lead to TRACE messages been written out.
    import PyInstaller.log as logging
    logging.logger.setLevel(logging.DEBUG)
    return initialize_modgraph()
예제 #3
0
def fresh_pyi_modgraph(monkeypatch):
    """
    Get a fresh PyiModuleGraph
    """
    def fake_base_modules(self):
        # speed up set up
        self._base_modules = ()

    logging.logger.setLevel(logging.DEBUG)
    # ensure we get a fresh PyiModuleGraph
    monkeypatch.setattr(analysis, "_cached_module_graph_", None)
    # speed up setup
    monkeypatch.setattr(analysis.PyiModuleGraph, "_analyze_base_modules",
                        fake_base_modules)
    return analysis.initialize_modgraph()
예제 #4
0
    def assemble(self):
        """
        This method is the MAIN method for finding all necessary files to be bundled.
        """
        from PyInstaller.config import CONF

        for m in self.excludes:
            logger.debug("Excluding module '%s'" % m)
        self.graph = initialize_modgraph(excludes=self.excludes,
                                         user_hook_dirs=self.hookspath)

        # TODO Find a better place where to put 'base_library.zip' and when to created it.
        # For Python 3 it is necessary to create file 'base_library.zip'
        # containing core Python modules. In Python 3 some built-in modules
        # are written in pure Python. base_library.zip is a way how to have
        # those modules as "built-in".
        libzip_filename = os.path.join(CONF['workpath'], 'base_library.zip')
        create_py3_base_library(libzip_filename, graph=self.graph)
        # Bundle base_library.zip as data file.
        # Data format of TOC item:   ('relative_path_in_dist_dir', 'absolute_path_on_disk', 'DATA')
        self.datas.append(
            (os.path.basename(libzip_filename), libzip_filename, 'DATA'))

        # Expand sys.path of module graph.
        # The attribute is the set of paths to use for imports: sys.path,
        # plus our loader, plus other paths from e.g. --path option).
        self.graph.path = self.pathex + self.graph.path
        self.graph.set_setuptools_nspackages()

        logger.info("running Analysis %s", self.tocbasename)
        # Get paths to Python and, in Windows, the manifest.
        python = compat.python_executable
        if not is_win:
            # Linux/MacOS: get a real, non-link path to the running Python executable.
            while os.path.islink(python):
                python = os.path.join(os.path.dirname(python),
                                      os.readlink(python))
            depmanifest = None
        else:
            # Windows: Create a manifest to embed into built .exe, containing the same
            # dependencies as python.exe.
            depmanifest = winmanifest.Manifest(
                type_="win32",
                name=CONF['specnm'],
                processorArchitecture=winmanifest.processor_architecture(),
                version=(1, 0, 0, 0))
            depmanifest.filename = os.path.join(
                CONF['workpath'], CONF['specnm'] + ".exe.manifest")

        # We record "binaries" separately from the modulegraph, as there
        # is no way to record those dependencies in the graph. These include
        # the python executable and any binaries added by hooks later.
        # "binaries" are not the same as "extensions" which are .so or .dylib
        # that are found and recorded as extension nodes in the graph.
        # Reset seen variable before running bindepend. We use bindepend only for
        # the python executable.
        bindepend.seen.clear()

        # Add binary and assembly dependencies of Python.exe.
        # This also ensures that its assembly depencies under Windows get added to the
        # built .exe's manifest. Python 2.7 extension modules have no assembly
        # dependencies, and rely on the app-global dependencies set by the .exe.
        self.binaries.extend(
            bindepend.Dependencies([('', python, '')],
                                   manifest=depmanifest,
                                   redirects=self.binding_redirects)[1:])
        if is_win:
            depmanifest.writeprettyxml()

        ### Module graph.
        #
        # Construct the module graph of import relationships between modules
        # required by this user's application. For each entry point (top-level
        # user-defined Python script), all imports originating from this entry
        # point are recursively parsed into a subgraph of the module graph. This
        # subgraph is then connected to this graph's root node, ensuring
        # imported module nodes will be reachable from the root node -- which is
        # is (arbitrarily) chosen to be the first entry point's node.

        # List to hold graph nodes of scripts and runtime hooks in use order.
        priority_scripts = []

        # Assume that if the script does not exist, Modulegraph will raise error.
        # Save the graph nodes of each in sequence.
        for script in self.inputs:
            logger.info("Analyzing %s", script)
            priority_scripts.append(self.graph.add_script(script))

        # Analyze the script's hidden imports (named on the command line)
        self.graph.add_hiddenimports(self.hiddenimports)

        ### Post-graph hooks.
        self.graph.process_post_graph_hooks(self)

        # Update 'binaries' TOC and 'datas' TOC.
        deps_proc = DependencyProcessor(self.graph,
                                        self.graph._additional_files_cache)
        self.binaries.extend(deps_proc.make_binaries_toc())
        self.datas.extend(deps_proc.make_datas_toc())
        self.zipped_data.extend(deps_proc.make_zipped_data_toc())
        # Note: zipped eggs are collected below

        ### Look for dlls that are imported by Python 'ctypes' module.
        # First get code objects of all modules that import 'ctypes'.
        logger.info('Looking for ctypes DLLs')
        # dict like:  {'module1': code_obj, 'module2': code_obj}
        ctypes_code_objs = self.graph.get_code_using("ctypes")

        for name, co in ctypes_code_objs.items():
            # Get dlls that might be needed by ctypes.
            logger.debug('Scanning %s for shared libraries or dlls', name)
            try:
                ctypes_binaries = scan_code_for_ctypes(co)
                self.binaries.extend(set(ctypes_binaries))
            except Exception as ex:
                raise RuntimeError(f"Failed to scan the module '{name}'. "
                                   f"This is a bug. Please report it.") from ex

        self.datas.extend((dest, source, "DATA")
                          for (dest, source) in format_binaries_and_datas(
                              self.graph.metadata_required()))

        # Analyze run-time hooks.
        # Run-time hooks has to be executed before user scripts. Add them
        # to the beginning of 'priority_scripts'.
        priority_scripts = self.graph.analyze_runtime_hooks(
            self.custom_runtime_hooks) + priority_scripts

        # 'priority_scripts' is now a list of the graph nodes of custom runtime
        # hooks, then regular runtime hooks, then the PyI loader scripts.
        # Further on, we will make sure they end up at the front of self.scripts

        ### Extract the nodes of the graph as TOCs for further processing.

        # Initialize the scripts list with priority scripts in the proper order.
        self.scripts = self.graph.nodes_to_toc(priority_scripts)

        # Extend the binaries list with all the Extensions modulegraph has found.
        self.binaries = self.graph.make_binaries_toc(self.binaries)
        # Fill the "pure" list with pure Python modules.
        assert len(self.pure) == 0
        self.pure = self.graph.make_pure_toc()
        # And get references to module code objects constructed by ModuleGraph
        # to avoid writing .pyc/pyo files to hdd.
        self.pure._code_cache = self.graph.get_code_objects()

        # Add remaining binary dependencies - analyze Python C-extensions and what
        # DLLs they depend on.
        logger.info('Looking for dynamic libraries')
        self.binaries.extend(
            bindepend.Dependencies(self.binaries,
                                   redirects=self.binding_redirects))

        ### Include zipped Python eggs.
        logger.info('Looking for eggs')
        self.zipfiles.extend(deps_proc.make_zipfiles_toc())

        # Verify that Python dynamic library can be found.
        # Without dynamic Python library PyInstaller cannot continue.
        self._check_python_library(self.binaries)

        if is_win:
            # Remove duplicate redirects
            self.binding_redirects[:] = list(set(self.binding_redirects))
            logger.info("Found binding redirects: \n%s",
                        self.binding_redirects)

        # Filter binaries to adjust path of extensions that come from
        # python's lib-dynload directory. Prefix them with lib-dynload
        # so that we'll collect them into subdirectory instead of
        # directly into _MEIPASS
        for idx, tpl in enumerate(self.binaries):
            name, path, typecode = tpl
            if typecode == 'EXTENSION' \
               and not os.path.dirname(os.path.normpath(name)) \
               and os.path.basename(os.path.dirname(path)) == 'lib-dynload':
                name = os.path.join('lib-dynload', name)
                self.binaries[idx] = (name, path, typecode)

        # Place Python source in data files for the noarchive case.
        if self.noarchive:
            # Create a new TOC of ``(dest path for .pyc, source for .py, type)``.
            new_toc = TOC()
            for name, path, typecode in self.pure:
                assert typecode == 'PYMODULE'
                # Transform a python module name into a file name.
                name = name.replace('.', os.sep)
                # Special case: modules have an implied filename to add.
                if os.path.splitext(os.path.basename(path))[0] == '__init__':
                    name += os.sep + '__init__'
                # Append the extension for the compiled result.
                # In python 3.5 (PEP-488) .pyo files were replaced by
                # .opt-1.pyc and .opt-2.pyc. However, it seems that for
                # bytecode-only module distribution, we always need to
                # use the .pyc extension.
                name += '.pyc'
                new_toc.append((name, path, typecode))
            # Put the result of byte-compiling this TOC in datas. Mark all entries as data.
            for name, path, typecode in compile_py_files(
                    new_toc, CONF['workpath']):
                self.datas.append((name, path, 'DATA'))
            # Store no source in the archive.
            self.pure = TOC()

        # Write warnings about missing modules.
        self._write_warnings()
        # Write debug information about hte graph
        self._write_graph_debug()
예제 #5
0
def pyi_modgraph():
    return initialize_modgraph()
예제 #6
0
def pyi_modgraph():
    return initialize_modgraph()