Esempio n. 1
0
def callInstallNameTool(filename, mapping):
    """Update the macOS shared library information for a binary or shared library.

    Args:
        filename - The file to be modified.
        mapping  - old_path, new_path pairs of values that should be changed

    Returns:
        None

    Notes:
        This is obviously macOS specific.
    """
    command = ["install_name_tool"]
    for old_path, new_path in mapping:
        command += ("-change", old_path, new_path)
    command.append(filename)

    with withMadeWritableFileMode(filename):
        result = subprocess.call(command, stdout=subprocess.PIPE)

    if result != 0:
        postprocessing_logger.sysexit(
            "Error, call to 'install_name_tool' to fix shared library path failed."
        )
Esempio n. 2
0
def callInstallNameToolAddRPath(filename, rpath):
    """Adds the rpath path name `rpath` in the specified `filename` Mach-O
    binary or shared library. If the Mach-O binary already contains the new
    `rpath` path name, it is an error.

    Args:
        filename - Mach-O binary or shared library file name.
        rpath  - rpath path name.

    Returns:
        None

    Notes:
        This is obviously macOS specific.
    """
    command = [
        "install_name_tool", "-add_rpath",
        os.path.join(rpath, "."), filename
    ]

    with withMadeWritableFileMode(filename):
        result = subprocess.call(command, stdout=subprocess.PIPE)

    if result != 0:
        postprocessing_logger.sysexit(
            "Error, call to 'install_name_tool' to add rpath failed.")
Esempio n. 3
0
def _filterSigntoolErrorOutput(stderr):
    stderr = b"\n".join(line for line in stderr.splitlines() if line
                        if b"replacing existing signature" not in line)

    if b"errSecInternalComponent" in stderr:
        postprocessing_logger.sysexit("""\
Access to the certificate is now allowed. Please allow all items or with
GUI, enable prompting for the certificate in KeyChain Access.""")

    return None, stderr
Esempio n. 4
0
def packDistFolderToOnefile(dist_dir, binary_filename):
    """Pack distribution to onefile, i.e. a single file that is directly executable."""

    if getOS() == "Linux":
        packDistFolderToOnefileLinux(dist_dir, binary_filename)
    elif getOS() == "Windows":
        packDistFolderToOnefileWindows(dist_dir)
    else:
        postprocessing_logger.sysexit(
            "Onefile mode is not yet available on %r." % getOS()
        )
Esempio n. 5
0
def packDistFolderToOnefile(dist_dir, binary_filename):
    """Pack distribution to onefile, i.e. a single file that is directly executable."""

    onefile_output_filename = getResultFullpath(onefile=True)

    if getOS() == "Windows" or Options.isExperimental("onefile-bootstrap"):
        packDistFolderToOnefileBootstrap(onefile_output_filename, dist_dir)
    elif getOS() == "Linux":
        packDistFolderToOnefileLinux(onefile_output_filename, dist_dir, binary_filename)
    else:
        postprocessing_logger.sysexit(
            "Onefile mode is not yet available on %r." % getOS()
        )

    Plugins.onOnefileFinished(onefile_output_filename)
Esempio n. 6
0
def makeMacOSThinBinary(filename):
    file_output = executeToolChecked(
        logger=postprocessing_logger,
        command=("file", filename),
        absence_message=_file_usage,
    )

    if str is not bytes:
        file_output = file_output.decode("utf8")

    assert file_output.startswith(filename + ":")
    file_output = file_output[len(filename) + 1:].splitlines()[0].strip()

    macos_target_arch = Options.getMacOSTargetArch()

    if "universal" in file_output:
        executeToolChecked(
            logger=postprocessing_logger,
            command=(
                "lipo",
                "-thin",
                macos_target_arch,
                filename,
                "-o",
                filename + ".tmp",
            ),
            absence_message=_lipo_usage,
        )

        with withMadeWritableFileMode(filename):
            os.unlink(filename)
            os.rename(filename + ".tmp", filename)
    elif macos_target_arch not in file_output:
        postprocessing_logger.sysexit(
            "Error, cannot use file '%s' (%s) to build arch '%s' result" %
            (filename, file_output, macos_target_arch))
Esempio n. 7
0
def addWindowsIconFromIcons():
    icon_group = 1
    image_id = 1
    images = []

    result_filename = OutputDirectories.getResultFullpath()

    for icon_spec in Options.getIconPaths():
        if "#" in icon_spec:
            icon_path, icon_index = icon_spec.rsplit("#", 1)
            icon_index = int(icon_index)
        else:
            icon_path = icon_spec
            icon_index = None

        with open(icon_path, "rb") as icon_file:
            # Read header and icon entries.
            header = readFromFile(icon_file, IconDirectoryHeader)
            icons = [
                readFromFile(icon_file, IconDirectoryEntry)
                for _i in range(header.count)
            ]

            if icon_index is not None:
                if icon_index > len(icons):
                    postprocessing_logger.sysexit(
                        "Error, referenced icon index %d in file %r with only %d icons."
                        % (icon_index, icon_path, len(icons)))

                icons[:] = icons[icon_index:icon_index + 1]

            postprocessing_logger.info("Adding %d icon(s) from icon file %r." %
                                       (len(icons), icon_spec))

            # Image data are to be scanned from places specified icon entries
            for icon in icons:
                icon_file.seek(icon.image_offset, 0)
                images.append(icon_file.read(icon.image_size))

        parts = [convertStructureToBytes(header)]

        for icon in icons:
            parts.append(
                convertStructureToBytes(
                    IconGroupDirectoryEntry(
                        width=icon.width,
                        height=icon.height,
                        colors=icon.colors,
                        reserved=icon.reserved,
                        planes=icon.planes,
                        bit_count=icon.bit_count,
                        image_size=icon.image_size,
                        id=image_id,
                    )))

            image_id += 1

        addResourceToFile(
            target_filename=result_filename,
            data=b"".join(parts),
            resource_kind=RT_GROUP_ICON,
            lang_id=0,
            res_name=icon_group,
            logger=postprocessing_logger,
        )

    for count, image in enumerate(images, 1):
        addResourceToFile(
            target_filename=result_filename,
            data=image,
            resource_kind=RT_ICON,
            lang_id=0,
            res_name=count,
            logger=postprocessing_logger,
        )
Esempio n. 8
0
def executePostProcessing():
    # These is a bunch of stuff to consider, pylint: disable=too-many-branches

    result_filename = OutputDirectories.getResultFullpath()

    if not os.path.exists(result_filename):
        postprocessing_logger.sysexit(
            "Error, scons failed to create the expected file %r. " %
            result_filename)

    if isWin32Windows():
        if not Options.shallMakeModule():
            needs_manifest = False
            manifest = None

            if python_version < 0x300:
                # Copy the Windows manifest from the CPython binary to the created
                # executable, so it finds "MSCRT.DLL". This is needed for Python2
                # only, for Python3 newer MSVC doesn't hide the C runtime.
                manifest = getWindowsExecutableManifest(sys.executable)

                if manifest is not None:
                    needs_manifest = True

            if (Options.shallAskForWindowsAdminRights()
                    or Options.shallAskForWindowsUIAccessRights()):
                needs_manifest = True

                if manifest is None:
                    manifest = getDefaultWindowsExecutableManifest()

                if Options.shallAskForWindowsAdminRights():
                    manifest.addUacAdmin()

                if Options.shallAskForWindowsUIAccessRights():
                    manifest.addUacUiAccess()

            if needs_manifest:
                manifest.addResourceToFile(result_filename,
                                           logger=postprocessing_logger)

        if (Options.getWindowsVersionInfoStrings()
                or Options.getWindowsProductVersion()
                or Options.getWindowsFileVersion()):
            version_resources.update(
                addVersionInfoResource(
                    string_values=Options.getWindowsVersionInfoStrings(),
                    product_version=Options.getWindowsProductVersion(),
                    file_version=Options.getWindowsFileVersion(),
                    file_date=(0, 0),
                    is_exe=not Options.shallMakeModule(),
                    result_filename=result_filename,
                    logger=postprocessing_logger,
                ))

        source_dir = OutputDirectories.getSourceDirectoryPath()

        # Attach the binary blob as a Windows resource.
        addResourceToFile(
            target_filename=result_filename,
            data=getFileContents(getConstantBlobFilename(source_dir), "rb"),
            resource_kind=RT_RCDATA,
            res_name=3,
            lang_id=0,
            logger=postprocessing_logger,
        )

        # Attach icons from template file if given.
        template_exe = Options.getWindowsIconExecutablePath()
        if template_exe is not None:
            res_copied = copyResourcesFromFileToFile(
                template_exe,
                target_filename=result_filename,
                resource_kinds=(RT_ICON, RT_GROUP_ICON),
            )

            if res_copied == 0:
                postprocessing_logger.warning(
                    "The specified icon template executable %r didn't contain anything to copy."
                    % template_exe)
            else:
                postprocessing_logger.warning(
                    "Copied %d icon resources from %r." %
                    (res_copied, template_exe))
        else:
            addWindowsIconFromIcons()

    # On macOS, we update the executable path for searching the "libpython"
    # library.
    if (getOS() == "Darwin" and not Options.shallMakeModule()
            and not Options.shallUseStaticLibPython()):
        python_abi_version = python_version_str + getPythonABI()
        python_dll_filename = "libpython" + python_abi_version + ".dylib"
        python_lib_path = os.path.join(sys.prefix, "lib")

        # Note: For CPython and potentially others, the rpath for the Python
        # library needs to be set.

        callInstallNameTool(
            filename=result_filename,
            mapping=(
                (
                    python_dll_filename,
                    os.path.join(python_lib_path, python_dll_filename),
                ),
                (
                    "@rpath/Python3.framework/Versions/%s/Python3" %
                    python_version_str,
                    os.path.join(python_lib_path, python_dll_filename),
                ),
            ),
            rpath=python_lib_path,
        )

    # Modules should not be executable, but Scons creates them like it, fix
    # it up here.
    if not isWin32Windows() and Options.shallMakeModule():
        removeFileExecutablePermission(result_filename)

    if isWin32Windows() and Options.shallMakeModule():
        candidate = os.path.join(
            os.path.dirname(result_filename),
            "lib" + os.path.basename(result_filename)[:-4] + ".a",
        )

        if os.path.exists(candidate):
            os.unlink(candidate)

    if isWin32Windows() and Options.shallTreatUninstalledPython():
        shutil.copy(getTargetPythonDLLPath(),
                    os.path.dirname(result_filename) or ".")
Esempio n. 9
0
def packDistFolderToOnefileLinux(onefile_output_filename, dist_dir, binary_filename):
    """Pack to onefile binary on Linux.

    Notes: This is mostly a wrapper around AppImage, which does all the heavy
    lifting.
    """

    if not locateDLL("fuse"):
        postprocessing_logger.sysexit(
            """\
Error, the fuse library (libfuse.so.x from fuse2, *not* fuse3) must be installed
for onefile creation to work on Linux."""
        )

    # This might be possible to avoid being done with --runtime-file.
    apprun_filename = os.path.join(dist_dir, "AppRun")
    with open(apprun_filename, "w") as output_file:
        output_file.write(
            """\
#!/bin/bash
exec -a $ARGV0 $APPDIR/%s $@"""
            % os.path.basename(binary_filename)
        )

    addFileExecutablePermission(apprun_filename)

    binary_basename = os.path.basename(getResultBasepath())

    icon_paths = getIconPaths()

    assert icon_paths
    extension = os.path.splitext(icon_paths[0])[1].lower()

    shutil.copyfile(icon_paths[0], getResultBasepath() + extension)

    with open(getResultBasepath() + ".desktop", "w") as output_file:
        output_file.write(
            """\
[Desktop Entry]
Name=%(binary_basename)s
Exec=%(binary_filename)s
Icon=%(binary_basename)s
Type=Application
Categories=Utility;"""
            % {
                "binary_basename": binary_basename,
                "binary_filename": os.path.basename(binary_filename),
            }
        )

    postprocessing_logger.info(
        "Creating single file from dist folder, this may take a while."
    )

    stdout_filename = binary_filename + ".appimage.stdout.txt"
    stderr_filename = binary_filename + ".appimage.stderr.txt"

    stdout_file = open(stdout_filename, "wb")
    stderr_file = open(stderr_filename, "wb")

    # Starting the process while locked, so file handles are not duplicated.
    appimagetool_process = subprocess.Popen(
        (
            _getAppImageToolPath(
                for_operation=True, assume_yes_for_downloads=assumeYesForDownloads()
            ),
            dist_dir,
            "--comp",
            "xz",
            "-n",
            onefile_output_filename,
        ),
        shell=False,
        stdin=getNullInput(),
        stdout=stdout_file,
        stderr=stderr_file,
    )

    result = appimagetool_process.wait()

    stdout_file.close()
    stderr_file.close()

    if not os.path.exists(onefile_output_filename):
        postprocessing_logger.sysexit(
            "Error, expected output file %r not created by AppImage, check its outputs %r and %r."
            % (onefile_output_filename, stdout_filename, stderr_filename)
        )

    if result != 0:
        # Useless now.
        os.unlink(onefile_output_filename)

        if b"Text file busy" in getFileContents(stderr_filename, mode="rb"):
            postprocessing_logger.sysexit(
                "Error, error exit from AppImage because target file is locked."
            )

        postprocessing_logger.sysexit(
            "Error, error exit from AppImage, check its outputs %r and %r."
            % (stdout_filename, stderr_filename)
        )

    os.unlink(stdout_filename)
    os.unlink(stderr_filename)

    postprocessing_logger.info("Completed onefile creation.")
def packDistFolderToOnefileLinux(onefile_output_filename, dist_dir,
                                 binary_filename):
    """Pack to onefile binary on Linux.

    Notes: This is mostly a wrapper around AppImage, which does all the heavy
    lifting.
    """

    # This might be possible to avoid being done with --runtime-file.
    apprun_filename = os.path.join(dist_dir, "AppRun")
    with open(apprun_filename, "w") as output_file:
        output_file.write("""\
#!/bin/sh
exec $APPDIR/%s $@""" % os.path.basename(binary_filename))

    addFileExecutablePermission(apprun_filename)

    binary_basename = os.path.basename(getResultBasepath())

    icon_paths = getIconPaths()

    assert icon_paths
    extension = os.path.splitext(icon_paths[0])[1].lower()

    shutil.copyfile(icon_paths[0], getResultBasepath() + extension)

    with open(getResultBasepath() + ".desktop", "w") as output_file:
        output_file.write(
            """\
[Desktop Entry]
Name=%(binary_basename)s
Exec=%(binary_filename)s
Icon=%(binary_basename)s
Type=Application
Categories=Utility;""" % {
                "binary_basename": binary_basename,
                "binary_filename": os.path.basename(binary_filename),
            })

    postprocessing_logger.info(
        "Creating single file from dist folder, this may take a while.")

    # Starting the process while locked, so file handles are not duplicated.
    appimagetool_process = subprocess.Popen(
        (
            getAppImageToolPath(),
            dist_dir,
            "--comp",
            "xz",
            "-n",
            onefile_output_filename,
        ),
        shell=False,
        stderr=getNullOutput(),
        stdout=getNullOutput(),
    )

    # TODO: Exit code should be checked.
    result = appimagetool_process.wait()

    if not os.path.exists(onefile_output_filename):
        postprocessing_logger.sysexit(
            "Error, expected output file %s not created by AppImage." %
            onefile_output_filename)

    postprocessing_logger.info("Completed onefile creation.")

    assert result == 0, result
Esempio n. 11
0
def executePostProcessing():
    """Postprocessing of the resulting binary.

    These are in part required steps, not usable after failure.
    """

    result_filename = OutputDirectories.getResultFullpath(onefile=False)

    if not os.path.exists(result_filename):
        postprocessing_logger.sysexit(
            "Error, scons failed to create the expected file %r. " %
            result_filename)

    if isWin32Windows():
        if not Options.shallMakeModule():
            if python_version < 0x300:
                # Copy the Windows manifest from the CPython binary to the created
                # executable, so it finds "MSCRT.DLL". This is needed for Python2
                # only, for Python3 newer MSVC doesn't hide the C runtime.
                manifest = getWindowsExecutableManifest(sys.executable)
            else:
                manifest = None

            executePostProcessingResources(manifest=manifest, onefile=False)

        source_dir = OutputDirectories.getSourceDirectoryPath()

        # Attach the binary blob as a Windows resource.
        addResourceToFile(
            target_filename=result_filename,
            data=getFileContents(getConstantBlobFilename(source_dir), "rb"),
            resource_kind=RT_RCDATA,
            res_name=3,
            lang_id=0,
            logger=postprocessing_logger,
        )

    # On macOS, we update the executable path for searching the "libpython"
    # library.
    if (isMacOS() and not Options.shallMakeModule()
            and not Options.shallUseStaticLibPython()):
        python_abi_version = python_version_str + getPythonABI()
        python_dll_filename = "libpython" + python_abi_version + ".dylib"
        python_lib_path = os.path.join(sys.prefix, "lib")

        # Note: For CPython and potentially others, the rpath for the Python
        # library needs to be set.

        callInstallNameTool(
            filename=result_filename,
            mapping=(
                (
                    python_dll_filename,
                    os.path.join(python_lib_path, python_dll_filename),
                ),
                (
                    "@rpath/Python3.framework/Versions/%s/Python3" %
                    python_version_str,
                    os.path.join(python_lib_path, python_dll_filename),
                ),
            ),
            id_path=None,
            rpath=python_lib_path,
        )

    if Options.shallCreateAppBundle():
        createPlistInfoFile(logger=postprocessing_logger, onefile=False)

    # Modules should not be executable, but Scons creates them like it, fix
    # it up here.
    if not isWin32Windows() and Options.shallMakeModule():
        removeFileExecutablePermission(result_filename)

    if isWin32Windows() and Options.shallMakeModule():
        candidate = os.path.join(
            os.path.dirname(result_filename),
            "lib" + os.path.basename(result_filename)[:-4] + ".a",
        )

        if os.path.exists(candidate):
            os.unlink(candidate)

    # Might have to create a CMD file, potentially with debugger run.
    if Options.shallCreateCmdFileForExecution():
        dll_directory = getExternalUsePath(
            os.path.dirname(getTargetPythonDLLPath()))

        cmd_filename = OutputDirectories.getResultRunFilename(onefile=False)

        cmd_contents = """
@echo off
rem This script was created by Nuitka to execute '%(exe_filename)s' with Python DLL being found.
set PATH=%(dll_directory)s;%%PATH%%
set PYTHONHOME=%(dll_directory)s
%(debugger_call)s"%%~dp0.\\%(exe_filename)s" %%*
""" % {
            "debugger_call": (" ".join(wrapCommandForDebuggerForExec()) +
                              " ") if Options.shallRunInDebugger() else "",
            "dll_directory":
            dll_directory,
            "exe_filename":
            os.path.basename(result_filename),
        }

        putTextFileContents(cmd_filename, cmd_contents)

    # Create a ".pyi" file for created modules
    if Options.shallMakeModule() and Options.shallCreatePyiFile():
        pyi_filename = OutputDirectories.getResultBasepath() + ".pyi"

        putTextFileContents(
            filename=pyi_filename,
            contents="""\
# This file was generated by Nuitka and describes the types of the
# created shared library.

# At this time it lists only the imports made and can be used by the
# tools that bundle libraries, including Nuitka itself. For instance
# standalone mode usage of the created library will need it.

# In the future, this will also contain type information for values
# in the module, so IDEs will use this. Therefore please include it
# when you make software releases of the extension module that it
# describes.

%(imports)s

# This is not Python source even if it looks so. Make it clear for
# now. This was decided by PEP 484 designers.
__name__ = ...

""" % {
                "imports":
                "\n".join("import %s" % module_name
                          for module_name in getImportedNames())
            },
        )
Esempio n. 12
0
def _addWindowsIconFromIcons(onefile):
    # Relatively detailed handling, pylint: disable=too-many-locals

    icon_group = 1
    image_id = 1
    images = []

    result_filename = OutputDirectories.getResultFullpath(onefile=onefile)

    for icon_spec in Options.getIconPaths():
        if "#" in icon_spec:
            icon_path, icon_index = icon_spec.rsplit("#", 1)
            icon_index = int(icon_index)
        else:
            icon_path = icon_spec
            icon_index = None

        icon_path = os.path.normcase(icon_path)

        if not icon_path.endswith(".ico"):
            postprocessing_logger.info(
                "File '%s' is not in Windows icon format, converting to it." %
                icon_path)

            if icon_index is not None:
                postprocessing_logger.sysexit(
                    "Cannot specify indexes with non-ico format files in '%s'."
                    % icon_spec)

            icon_build_path = os.path.join(
                OutputDirectories.getSourceDirectoryPath(onefile=onefile),
                "icons",
            )
            makePath(icon_build_path)
            converted_icon_path = os.path.join(
                icon_build_path,
                "icon-%d.ico" % image_id,
            )

            convertImageToIconFormat(
                logger=postprocessing_logger,
                image_filename=icon_spec,
                icon_filename=converted_icon_path,
            )

            icon_path = converted_icon_path

        with open(icon_path, "rb") as icon_file:
            # Read header and icon entries.
            header = readFromFile(icon_file, IconDirectoryHeader)
            icons = [
                readFromFile(icon_file, IconDirectoryEntry)
                for _i in range(header.count)
            ]

            if icon_index is not None:
                if icon_index > len(icons):
                    postprocessing_logger.sysexit(
                        "Error, referenced icon index %d in file '%s' with only %d icons."
                        % (icon_index, icon_path, len(icons)))

                icons[:] = icons[icon_index:icon_index + 1]

            postprocessing_logger.info(
                "Adding %d icon(s) from icon file '%s'." %
                (len(icons), icon_spec))

            # Image data are to be scanned from places specified icon entries
            for icon in icons:
                icon_file.seek(icon.image_offset, 0)
                images.append(icon_file.read(icon.image_size))

        parts = [convertStructureToBytes(header)]

        for icon in icons:
            parts.append(
                convertStructureToBytes(
                    IconGroupDirectoryEntry(
                        width=icon.width,
                        height=icon.height,
                        colors=icon.colors,
                        reserved=icon.reserved,
                        planes=icon.planes,
                        bit_count=icon.bit_count,
                        image_size=icon.image_size,
                        id=image_id,
                    )))

            image_id += 1

        addResourceToFile(
            target_filename=result_filename,
            data=b"".join(parts),
            resource_kind=RT_GROUP_ICON,
            lang_id=0,
            res_name=icon_group,
            logger=postprocessing_logger,
        )

    for count, image in enumerate(images, 1):
        addResourceToFile(
            target_filename=result_filename,
            data=image,
            resource_kind=RT_ICON,
            lang_id=0,
            res_name=count,
            logger=postprocessing_logger,
        )
Esempio n. 13
0
def executePostProcessing():
    """Postprocessing of the resulting binary.

    These are in part required steps, not usable after failure.
    """

    result_filename = OutputDirectories.getResultFullpath(onefile=False)

    if not os.path.exists(result_filename):
        postprocessing_logger.sysexit(
            "Error, scons failed to create the expected file %r. " % result_filename
        )

    if isWin32Windows():
        if not Options.shallMakeModule():
            if python_version < 0x300:
                # Copy the Windows manifest from the CPython binary to the created
                # executable, so it finds "MSCRT.DLL". This is needed for Python2
                # only, for Python3 newer MSVC doesn't hide the C runtime.
                manifest = getWindowsExecutableManifest(sys.executable)
            else:
                manifest = None

            executePostProcessingResources(manifest=manifest, onefile=False)

        source_dir = OutputDirectories.getSourceDirectoryPath()

        # Attach the binary blob as a Windows resource.
        addResourceToFile(
            target_filename=result_filename,
            data=getFileContents(getConstantBlobFilename(source_dir), "rb"),
            resource_kind=RT_RCDATA,
            res_name=3,
            lang_id=0,
            logger=postprocessing_logger,
        )

    # On macOS, we update the executable path for searching the "libpython"
    # library.
    if (
        getOS() == "Darwin"
        and not Options.shallMakeModule()
        and not Options.shallUseStaticLibPython()
    ):
        python_abi_version = python_version_str + getPythonABI()
        python_dll_filename = "libpython" + python_abi_version + ".dylib"
        python_lib_path = os.path.join(sys.prefix, "lib")

        # Note: For CPython and potentially others, the rpath for the Python
        # library needs to be set.

        callInstallNameTool(
            filename=result_filename,
            mapping=(
                (
                    python_dll_filename,
                    os.path.join(python_lib_path, python_dll_filename),
                ),
                (
                    "@rpath/Python3.framework/Versions/%s/Python3" % python_version_str,
                    os.path.join(python_lib_path, python_dll_filename),
                ),
            ),
            rpath=python_lib_path,
        )

    # Modules should not be executable, but Scons creates them like it, fix
    # it up here.
    if not isWin32Windows() and Options.shallMakeModule():
        removeFileExecutablePermission(result_filename)

    if isWin32Windows() and Options.shallMakeModule():
        candidate = os.path.join(
            os.path.dirname(result_filename),
            "lib" + os.path.basename(result_filename)[:-4] + ".a",
        )

        if os.path.exists(candidate):
            os.unlink(candidate)

    if isWin32Windows() and Options.shallTreatUninstalledPython():
        shutil.copy(getTargetPythonDLLPath(), os.path.dirname(result_filename) or ".")
Esempio n. 14
0
def packDistFolderToOnefileLinux(onefile_output_filename, dist_dir,
                                 binary_filename):
    """Pack to onefile binary on Linux.

    Notes: This is mostly a wrapper around AppImage, which does all the heavy
    lifting.
    """

    if not locateDLL("fuse"):
        postprocessing_logger.sysexit("""\
Error, the fuse library (libfuse.so.x from fuse2, *not* fuse3) must be installed
for onefile creation to work on Linux.""")

    # This might be possible to avoid being done with --runtime-file.
    apprun_filename = os.path.join(dist_dir, "AppRun")
    putTextFileContents(
        apprun_filename,
        contents="""\
#!/bin/bash
exec -a $ARGV0 $APPDIR/%s \"$@\"""" % os.path.basename(binary_filename),
    )

    addFileExecutablePermission(apprun_filename)

    binary_basename = os.path.basename(getResultBasepath())

    icon_paths = getIconPaths()

    assert icon_paths
    extension = os.path.splitext(icon_paths[0])[1].lower()

    copyFile(icon_paths[0], getResultBasepath() + extension)

    putTextFileContents(
        getResultBasepath() + ".desktop",
        contents="""\
[Desktop Entry]
Name=%(binary_basename)s
Exec=%(binary_filename)s
Icon=%(binary_basename)s
Type=Application
Categories=Utility;""" % {
            "binary_basename": binary_basename,
            "binary_filename": os.path.basename(binary_filename),
        },
    )

    postprocessing_logger.info(
        "Creating single file from dist folder, this may take a while.")

    stdout_filename = binary_filename + ".appimage.stdout.txt"
    stderr_filename = binary_filename + ".appimage.stderr.txt"

    stdout_file = openTextFile(stdout_filename, "wb")
    stderr_file = openTextFile(stderr_filename, "wb")

    command = (
        _getAppImageToolPath(for_operation=True,
                             assume_yes_for_downloads=assumeYesForDownloads()),
        dist_dir,
        "--comp",
        getAppImageCompression(),
        "-n",
        onefile_output_filename,
    )

    stderr_file.write(b"Executed %r\n" % " ".join(command))

    # Starting the process while locked, so file handles are not duplicated, we
    # need fine grained control over process here, therefore we cannot use the
    # Execution.executeProcess() function without making it too complex and not
    # all Python versions allow using with, pylint: disable=consider-using-with
    # pylint: disable
    appimagetool_process = subprocess.Popen(
        command,
        shell=False,
        stdin=getNullInput(),
        stdout=stdout_file,
        stderr=stderr_file,
    )

    result = appimagetool_process.wait()

    stdout_file.close()
    stderr_file.close()

    if result != 0:
        # Useless result if there were errors, so now remove it.
        deleteFile(onefile_output_filename, must_exist=False)

        stderr = getFileContents(stderr_filename, mode="rb")

        if b"Text file busy" in stderr:
            postprocessing_logger.sysexit(
                "Error, error exit from AppImage because target file is locked."
            )

        if b"modprobe fuse" in stderr:
            postprocessing_logger.sysexit(
                "Error, error exit from AppImage because fuse kernel module was not loaded."
            )

        postprocessing_logger.sysexit(
            "Error, error exit from AppImage, check its outputs '%s' and '%s'."
            % (stdout_filename, stderr_filename))

    if not os.path.exists(onefile_output_filename):
        postprocessing_logger.sysexit(
            "Error, expected output file %r not created by AppImage, check its outputs '%s' and '%s'."
            % (onefile_output_filename, stdout_filename, stderr_filename))

    deleteFile(stdout_filename, must_exist=True)
    deleteFile(stderr_filename, must_exist=True)

    postprocessing_logger.info("Completed onefile creation.")