예제 #1
0
파일: utils.py 프로젝트: lRods/Study
def create_py3_base_library(libzip_filename, graph):
    """
    Package basic Python modules into .zip file. The .zip file with basic
    modules is necessary to have on PYTHONPATH for initializing libpython3
    in order to run the frozen executable with Python 3.
    """
    # Construct regular expression for matching modules that should be bundled
    # into base_library.zip.
    # Excluded are plain 'modules' or 'submodules.ANY_NAME'.
    # The match has to be exact - start and end of string not substring.
    regex_modules = '|'.join([r'(^%s$)' % x for x in PY3_BASE_MODULES])
    regex_submod = '|'.join([r'(^%s\..*$)' % x for x in PY3_BASE_MODULES])
    regex_str = regex_modules + '|' + regex_submod
    module_filter = re.compile(regex_str)

    try:
        # Remove .zip from previous run.
        if os.path.exists(libzip_filename):
            os.remove(libzip_filename)
        logger.debug('Adding python files to base_library.zip')
        # Class zipfile.PyZipFile is not suitable for PyInstaller needs.
        with zipfile.ZipFile(libzip_filename, mode='w') as zf:
            zf.debug = 3
            for mod in graph.flatten():
                if type(mod) in (modulegraph.SourceModule,
                                 modulegraph.Package):
                    # Bundling just required modules.
                    if module_filter.match(mod.identifier):
                        st = os.stat(mod.filename)
                        timestamp = int(st.st_mtime)
                        size = st.st_size & 0xFFFFFFFF
                        # Name inside the archive. The ZIP format
                        # specification requires forward slashes as
                        # directory separator.
                        # TODO use .pyo suffix if optimize flag is enabled.
                        if type(mod) is modulegraph.Package:
                            new_name = mod.identifier.replace('.', '/') \
                                + '/__init__.pyc'
                        else:
                            new_name = mod.identifier.replace('.', '/') \
                                + '.pyc'

                        # Write code to a file.
                        # This code is similar to py_compile.compile().
                        with io.BytesIO() as fc:
                            # Prepare all data in byte stream file-like object.
                            fc.write(BYTECODE_MAGIC)
                            if is_py37:
                                # Additional bitfield according to PEP 552
                                # 0b01 means hash based but don't check the hash
                                fc.write(struct.pack('<I', 0b01))
                                with open(mod.filename, 'rb') as fs:
                                    source_bytes = fs.read()
                                source_hash = importlib_source_hash(
                                    source_bytes)
                                fc.write(source_hash)
                            else:
                                fc.write(struct.pack('<II', timestamp, size))
                            marshal.dump(mod.code, fc)
                            # Use a ZipInfo to set timestamp for deterministic build
                            info = zipfile.ZipInfo(new_name)
                            zf.writestr(info, fc.getvalue())

    except Exception as e:
        logger.error('base_library.zip could not be created!')
        raise
예제 #2
0
def create_py3_base_library(libzip_filename, graph):
    """
    Package basic Python modules into .zip file. The .zip file with basic modules is necessary to have on PYTHONPATH
    for initializing libpython3 in order to run the frozen executable with Python 3.
    """
    # Import strip_paths_in_code locally to avoid cyclic import between building.utils and depend.utils (this module);
    # building.utils imports depend.bindepend, which in turn imports depend.utils.
    from PyInstaller.building.utils import strip_paths_in_code

    # Construct regular expression for matching modules that should be bundled into base_library.zip. Excluded are plain
    # 'modules' or 'submodules.ANY_NAME'. The match has to be exact - start and end of string not substring.
    regex_modules = '|'.join([rf'(^{x}$)' for x in compat.PY3_BASE_MODULES])
    regex_submod = '|'.join([rf'(^{x}\..*$)' for x in compat.PY3_BASE_MODULES])
    regex_str = regex_modules + '|' + regex_submod
    module_filter = re.compile(regex_str)

    try:
        # Remove .zip from previous run.
        if os.path.exists(libzip_filename):
            os.remove(libzip_filename)
        logger.debug('Adding python files to base_library.zip')
        # Class zipfile.PyZipFile is not suitable for PyInstaller needs.
        with zipfile.ZipFile(libzip_filename, mode='w') as zf:
            zf.debug = 3
            # Sort the graph nodes by identifier to ensure repeatable builds
            graph_nodes = list(graph.iter_graph())
            graph_nodes.sort(key=lambda item: item.identifier)
            for mod in graph_nodes:
                if type(mod) in (modulegraph.SourceModule, modulegraph.Package,
                                 modulegraph.CompiledModule):
                    # Bundling just required modules.
                    if module_filter.match(mod.identifier):
                        st = os.stat(mod.filename)
                        timestamp = int(st.st_mtime)
                        size = st.st_size & 0xFFFFFFFF
                        # Name inside the archive. The ZIP format specification requires forward slashes as directory
                        # separator.
                        if type(mod) is modulegraph.Package:
                            new_name = mod.identifier.replace(
                                '.', '/') + '/__init__.pyc'
                        else:
                            new_name = mod.identifier.replace('.',
                                                              '/') + '.pyc'

                        # Write code to a file. This code is similar to py_compile.compile().
                        with io.BytesIO() as fc:
                            # Prepare all data in byte stream file-like object.
                            fc.write(compat.BYTECODE_MAGIC)
                            if compat.is_py37:
                                # Additional bitfield according to PEP 552 0b01 means hash based but don't check the
                                # hash
                                fc.write(struct.pack('<I', 0b01))
                                with open(mod.filename, 'rb') as fs:
                                    source_bytes = fs.read()
                                source_hash = importlib_source_hash(
                                    source_bytes)
                                fc.write(source_hash)
                            else:
                                fc.write(struct.pack('<II', timestamp, size))
                            code = strip_paths_in_code(mod.code)  # Strip paths
                            marshal.dump(code, fc)
                            # Use a ZipInfo to set timestamp for deterministic build.
                            info = zipfile.ZipInfo(new_name)
                            zf.writestr(info, fc.getvalue())

    except Exception:
        logger.error('base_library.zip could not be created!')
        raise