Пример #1
0
def getCompressorFunction(expect_compression):
    # spell-checker: ignore zstd, closefd

    try:
        from zstandard import ZstdCompressor  # pylint: disable=I0021,import-error
    except ImportError:
        assert not expect_compression

        @contextmanager
        def useSameFile(output_file):
            yield output_file

        return b"X", useSameFile
    else:
        assert expect_compression

        compressor_context = ZstdCompressor(level=22)

        @contextmanager
        def useCompressedFile(output_file):
            with compressor_context.stream_writer(
                    output_file, closefd=False) as compressed_file:
                yield compressed_file

        onefile_logger.info("Using compression for onefile payload.")

        return b"Y", useCompressedFile
Пример #2
0
def packDistFolderToOnefileBootstrap(onefile_output_filename, dist_dir):
    postprocessing_logger.info(
        "Creating single file from dist folder, this may take a while.")

    onefile_logger.info("Running bootstrap binary compilation via Scons.")

    # Now need to append to payload it, potentially compressing it.
    compressor_python = getCompressorPython()

    # First need to create the bootstrap binary for unpacking.
    _runOnefileScons(
        quiet=not Options.isShowScons(),
        onefile_compression=compressor_python is not None,
    )

    if isWin32Windows():
        executePostProcessingResources(manifest=None, onefile=True)

    Plugins.onBootstrapBinary(onefile_output_filename)

    if isMacOS():
        addMacOSCodeSignature(filenames=[onefile_output_filename])

    runOnefileCompressor(
        compressor_python=compressor_python,
        dist_dir=dist_dir,
        onefile_output_filename=onefile_output_filename,
        start_binary=getResultFullpath(onefile=False),
    )
Пример #3
0
def attachOnefilePayload(dist_dir, onefile_output_filename, start_binary,
                         expect_compression):
    # Somewhat detail rich, pylint: disable=too-many-locals
    compression_indicator, compressor = getCompressorFunction(
        expect_compression=expect_compression)

    with _openBinaryFileForAppending(onefile_output_filename) as output_file:
        # Seeking to end of file seems necessary on Python2 at least, maybe it's
        # just that tell reports wrong value initially.
        output_file.seek(0, 2)
        start_pos = output_file.tell()
        output_file.write(b"KA" + compression_indicator)

        # Move the binary to start immediately to the start position
        file_list = getFileList(dist_dir, normalize=False)
        file_list.remove(start_binary)
        file_list.insert(0, start_binary)

        if isWin32Windows():
            filename_encoding = "utf-16le"
        else:
            filename_encoding = "utf8"

        payload_size = 0

        setupProgressBar(
            stage="Onefile Payload",
            unit="module",
            total=len(file_list),
        )

        with compressor(output_file) as compressed_file:
            for filename_full in file_list:
                filename_relative = os.path.relpath(filename_full, dist_dir)

                reportProgressBar(
                    item=filename_relative,
                    update=False,
                )

                filename_encoded = (filename_relative +
                                    "\0").encode(filename_encoding)

                compressed_file.write(filename_encoded)
                payload_size += len(filename_encoded)

                with open(filename_full, "rb") as input_file:
                    input_file.seek(0, 2)
                    input_size = input_file.tell()
                    input_file.seek(0, 0)

                    compressed_file.write(struct.pack("Q", input_size))
                    shutil.copyfileobj(input_file, compressed_file)

                    payload_size += input_size + 8

                reportProgressBar(
                    item=filename_relative,
                    update=True,
                )

            # Using empty filename as a terminator.
            filename_encoded = "\0".encode(filename_encoding)
            compressed_file.write(filename_encoded)
            payload_size += len(filename_encoded)

            compressed_size = compressed_file.tell()

        if compression_indicator == b"Y":
            onefile_logger.info(
                "Onefile payload compression ratio (%.2f%%) size %d to %d." % (
                    (float(compressed_size) / payload_size) * 100,
                    payload_size,
                    compressed_size,
                ))

        if isWin32Windows():
            # add padding to have the start position at a double world boundary
            # this is needed on windows so that a possible certificate immediately
            # follows the start position
            pad = output_file.tell() % 8
            if pad != 0:
                output_file.write(bytes(8 - pad))

        output_file.seek(0, 2)
        end_pos = output_file.tell()

        # Size of the payload data plus the size of that size storage, so C code can
        # jump directly to it.
        output_file.write(struct.pack("Q", end_pos - start_pos))

    closeProgressBar()
Пример #4
0
def _runOnefileScons(quiet, onefile_compression):

    source_dir = OutputDirectories.getSourceDirectoryPath(onefile=True)
    SconsInterface.cleanSconsDirectory(source_dir)

    asBoolStr = SconsInterface.asBoolStr

    options = {
        "result_name":
        OutputDirectories.getResultBasepath(onefile=True),
        "result_exe":
        OutputDirectories.getResultFullpath(onefile=True),
        "source_dir":
        source_dir,
        "debug_mode":
        asBoolStr(Options.is_debug),
        "experimental":
        ",".join(Options.getExperimentalIndications()),
        "trace_mode":
        asBoolStr(Options.shallTraceExecution()),
        "target_arch":
        getArchitecture(),
        "python_prefix":
        sys.prefix,
        "nuitka_src":
        SconsInterface.getSconsDataPath(),
        "compiled_exe":
        OutputDirectories.getResultFullpath(onefile=False),
        "onefile_compression":
        asBoolStr(onefile_compression),
        "onefile_splash_screen":
        asBoolStr(Options.getWindowsSplashScreen() is not None),
    }

    if Options.isClang():
        options["clang_mode"] = "true"

    SconsInterface.setCommonOptions(options)

    onefile_env_values = {}

    if Options.isOnefileTempDirMode():
        onefile_env_values[
            "ONEFILE_TEMP_SPEC"] = Options.getOnefileTempDirSpec(
                use_default=True)
    else:
        # Merge version information if possible, to avoid collisions, or deep nesting
        # in file system.
        product_version = version_resources["ProductVersion"]
        file_version = version_resources["FileVersion"]

        if product_version != file_version:
            effective_version = "%s-%s" % (product_version, file_version)
        else:
            effective_version = file_version

        onefile_env_values["ONEFILE_COMPANY"] = version_resources[
            "CompanyName"]
        onefile_env_values["ONEFILE_PRODUCT"] = version_resources[
            "ProductName"]
        onefile_env_values["ONEFILE_VERSION"] = effective_version

    with withEnvironmentVarsOverridden(onefile_env_values):
        result = SconsInterface.runScons(options=options,
                                         quiet=quiet,
                                         scons_filename="Onefile.scons")

    # Exit if compilation failed.
    if not result:
        onefile_logger.sysexit("Error, onefile bootstrap binary build failed.")

    if Options.isRemoveBuildDir():
        onefile_logger.info("Removing onefile build directory %r." %
                            source_dir)

        removeDirectory(path=source_dir, ignore_errors=False)
        assert not os.path.exists(source_dir)
    else:
        onefile_logger.info("Keeping onefile build directory %r." % source_dir)