Ejemplo n.º 1
0
def _runOnefileScons(quiet):

    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),
        "unstripped_mode": asBoolStr(Options.isUnstripped()),
        "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),
    }

    SconsInterface.setCommonOptions(options)

    onefile_env_values = {}

    if Options.isWindowsOnefileTempDirMode() or getOS() != "Windows":
        onefile_env_values["ONEFILE_TEMP_SPEC"] = Options.getWindowsOnefileTempDirSpec(
            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 withEnvironmentVarsOverriden(onefile_env_values):
        result = SconsInterface.runScons(
            options=options, quiet=quiet, scons_filename="WindowsOnefile.scons"
        )

    # Exit if compilation failed.
    if not result:
        scons_logger.sysexit("Error, one file bootstrap build for Windows failed.")

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

        removeDirectory(path=source_dir, ignore_errors=False)
        assert not os.path.exists(source_dir)
    else:
        general.info("Keeping onefile build directory %r." % source_dir)
Ejemplo n.º 2
0
    def _createTriggerLoadedModule(module, trigger_name, code):
        """Create a "trigger" for a module to be imported.

        Notes:
            The trigger will incorpaorate the code to be prepended / appended.
            Called by @onModuleDiscovered.

        Args:
            module: the module object (serves as dict key)
            trigger_name: string ("-preload"/"-postload")
            code: the code string

        Returns
            trigger_module
        """
        from nuitka.nodes.ModuleNodes import CompiledPythonModule
        from nuitka.tree.Building import createModuleTree

        from .Plugins import Plugins

        module_name = ModuleName(module.getFullName() + trigger_name)
        source_ref = fromFilename(module.getCompileTimeFilename() +
                                  trigger_name)

        mode = Plugins.decideCompilation(module_name, source_ref)

        trigger_module = CompiledPythonModule(
            module_name=module_name,
            is_top=False,
            mode=mode,
            future_spec=None,
            source_ref=source_ref,
        )

        createModuleTree(
            module=trigger_module,
            source_ref=module.getSourceReference(),
            source_code=code,
            is_main=False,
        )

        if mode == "bytecode":
            trigger_module.setSourceCode(code)

        if Options.isDebug():
            source_path = os.path.join(
                OutputDirectories.getSourceDirectoryPath(),
                module_name + ".py")

            with open(source_path, "w") as output:
                output.write(code)

        return trigger_module
Ejemplo n.º 3
0
    def _createTriggerLoadedModule(module, trigger_name, code, flags):
        """Create a "trigger" for a module to be imported.

        Notes:
            The trigger will incorpaorate the code to be prepended / appended.
            Called by @onModuleDiscovered.

        Args:
            module: the module object (serves as dict key)
            trigger_name: string ("-preload"/"-postload")
            code: the code string

        Returns
            trigger_module
        """
        from nuitka.nodes.ModuleNodes import CompiledPythonModule
        from nuitka.tree.Building import createModuleTree

        from .Plugins import Plugins

        module_name = ModuleName(module.getFullName() + trigger_name)
        source_ref = fromFilename(module.getCompileTimeFilename() +
                                  trigger_name)

        mode = Plugins.decideCompilation(module_name, source_ref)

        trigger_module = CompiledPythonModule(
            module_name=module_name,
            is_top=False,
            mode=mode,
            future_spec=None,
            source_ref=source_ref,
        )

        createModuleTree(
            module=trigger_module,
            source_ref=module.getSourceReference(),
            source_code=code,
            is_main=False,
        )

        if mode == "bytecode":
            trigger_module.setSourceCode(code)

        # In debug mode, put the files in the build folder, so they can be looked up easily.
        if Options.is_debug and "HIDE_SOURCE" not in flags:
            source_path = os.path.join(
                OutputDirectories.getSourceDirectoryPath(),
                module_name + ".py")

            putTextFileContents(filename=source_path, contents=code)

        return trigger_module
Ejemplo n.º 4
0
    def _createTriggerLoadedModule(cls, module, trigger_name, code, flags):
        """Create a "trigger" for a module to be imported.

        Notes:
            The trigger will incorporate the code to be prepended / appended.
            Called by @onModuleDiscovered.

        Args:
            module: the module object (serves as dict key)
            trigger_name: string ("preload"/"postload")
            code: the code string

        Returns
            trigger_module
        """

        from nuitka.tree.Building import buildModule

        module_name = makeTriggerModuleName(module.getFullName(), trigger_name)

        # In debug mode, put the files in the build folder, so they can be looked up easily.
        if Options.is_debug and "HIDE_SOURCE" not in flags:
            source_path = os.path.join(
                OutputDirectories.getSourceDirectoryPath(),
                module_name + ".py")

            putTextFileContents(filename=source_path, contents=code)

        try:
            trigger_module, _added = buildModule(
                module_filename=os.path.join(
                    os.path.dirname(module.getCompileTimeFilename()),
                    module_name.asPath() + ".py",
                ),
                module_name=module_name,
                source_code=code,
                is_top=False,
                is_main=False,
                is_extension=False,
                is_fake=module_name,
                hide_syntax_error=False,
            )
        except SyntaxError:
            plugins_logger.sysexit(
                "SyntaxError in plugin provided source code for '%s'." %
                module_name)

        if trigger_module.getCompilationMode() == "bytecode":
            trigger_module.setSourceCode(code)

        return trigger_module
Ejemplo n.º 5
0
    def __init__(self, filename):
        self.count = 0

        filename = os.path.join(OutputDirectories.getSourceDirectoryPath(),
                                filename)
        self.file = open(filename, "wb")
        if python_version < 300:
            self.pickle = pickle.Pickler(self.file, -1)
        else:
            self.pickle = pickle._Pickler(  # pylint: disable=I0021,protected-access
                self.file, -1)

        self.pickle.dispatch[type] = _pickleAnonValues
        self.pickle.dispatch[type(Ellipsis)] = _pickleAnonValues
        self.pickle.dispatch[type(NotImplemented)] = _pickleAnonValues
Ejemplo n.º 6
0
    def __init__(self, filename):
        self.count = 0

        filename = os.path.join(OutputDirectories.getSourceDirectoryPath(),
                                filename)
        self.file = openTextFile(filename, "wb")
        if python_version < 0x300:
            self.pickle = pickle.Pickler(self.file, -1)
        else:
            self.pickle = pickle._Pickler(  # pylint: disable=I0021,protected-access
                self.file, -1)

        self.pickle.dispatch[type] = _pickleAnonValues
        self.pickle.dispatch[type(Ellipsis)] = _pickleAnonValues
        self.pickle.dispatch[type(NotImplemented)] = _pickleAnonValues

        if type(sys.version_info) is not tuple:
            self.pickle.dispatch[type(sys.version_info)] = _pickleAnonValues

        # Standard pickling doesn't work with our necessary wrappers.
        if python_version >= 0x3A0:
            self.pickle.dispatch[UnionType] = _pickeUnionType
Ejemplo n.º 7
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 ".")
def _runOnefileScons(quiet):
    # Scons gets transported many details, that we express as variables, and
    # have checks for them, leading to many branches and statements,
    # pylint: disable=too-many-branches,too-many-statements

    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),
        "unstripped_mode": asBoolStr(Options.isUnstripped()),
        "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),
    }

    # Ask Scons to cache on Windows, except where the directory is thrown
    # away. On non-Windows you can should use ccache instead.
    if not Options.isRemoveBuildDir() and getOS() == "Windows":
        options["cache_mode"] = "true"

    if Options.isLto():
        options["lto_mode"] = "true"

    if Options.shallDisableConsoleWindow():
        options["win_disable_console"] = "true"

    if Options.isShowScons():
        options["show_scons"] = "true"

    if Options.isMingw64():
        options["mingw_mode"] = "true"

    if Options.getMsvcVersion():
        msvc_version = Options.getMsvcVersion()

        msvc_version = msvc_version.replace("exp", "Exp")
        if "." not in msvc_version:
            msvc_version += ".0"

        options["msvc_version"] = msvc_version

    if getOS() == "Windows":
        options["noelf_mode"] = "true"

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

    cpp_defines = Plugins.getPreprocessorSymbols()
    if cpp_defines:
        options["cpp_defines"] = ",".join(
            "%s%s%s" % (key, "=" if value else "", value or "")
            for key, value in cpp_defines.items())

    link_libraries = Plugins.getExtraLinkLibraries()
    if link_libraries:
        options["link_libraries"] = ",".join(link_libraries)

    if Options.shallRunInDebugger():
        options["full_names"] = "true"

    if Options.assumeYesForDownloads():
        options["assume_yes_for_downloads"] = "true"

    onefile_env_values = {}

    if not Options.isWindowsOnefileTempDirMode():
        # Merge version information if necessary, 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 withEnvironmentVarsOverriden(onefile_env_values):
        result = SconsInterface.runScons(options=options,
                                         quiet=quiet,
                                         scons_filename="WindowsOnefile.scons")

    # Exit if compilation failed.
    if not result:
        scons_logger.sysexit(
            "Error, one file bootstrap build for Windows failed.")

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

        removeDirectory(path=source_dir, ignore_errors=False)
        assert not os.path.exists(source_dir)
    else:
        general.info("Keeping onefile build directory %r." % source_dir)
Ejemplo n.º 9
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())
            },
        )
Ejemplo n.º 10
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,
        )
Ejemplo 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 (
        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 ".")
Ejemplo n.º 12
0
def executePostProcessing():
    result_filename = OutputDirectories.getResultFullpath()

    if not os.path.exists(result_filename):
        sys.exit("Error, scons failed to create the expected file '%s'. " %
                 result_filename)

    if isWin32Windows():
        # 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.
        if python_version < 300 and not Options.shallMakeModule():
            copyResourcesFromFileToFile(
                sys.executable,
                target_filename=result_filename,
                resource_kind=RT_MANIFEST,
            )

        assert os.path.exists(result_filename)

        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,
        )

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

        if os.path.exists(os.path.join(sys.prefix, "conda-meta")):
            callInstallNameToolAddRPath(result_filename, python_lib_path)

        callInstallNameTool(
            filename=result_filename,
            mapping=((
                python_dll_filename,
                os.path.join(python_lib_path, python_dll_filename),
            ), ),
        )

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

        mode = old_stat.st_mode
        mode &= ~(stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)

        if mode != old_stat.st_mode:
            os.chmod(result_filename, mode)

    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 ".")
Ejemplo n.º 13
0
def createPlistInfoFile(logger, onefile):
    # Many details, pylint: disable=too-many-locals

    import plistlib

    if Options.isStandaloneMode():
        bundle_dir = os.path.dirname(
            OutputDirectories.getStandaloneDirectoryPath())
    else:
        bundle_dir = os.path.dirname(
            OutputDirectories.getResultRunFilename(onefile=onefile))

    result_filename = OutputDirectories.getResultFullpath(onefile=onefile)
    app_name = Options.getMacOSAppName() or os.path.basename(result_filename)

    signed_app_name = Options.getMacOSSignedAppName() or app_name
    app_version = Options.getMacOSAppVersion() or "1.0"

    # TODO: We want an OrderedDict probably for stability.
    infos = OrderedDict([
        ("CFBundleDisplayName", app_name),
        ("CFBundleName", app_name),
        ("CFBundleIdentifier", signed_app_name),
        ("CFBundleExecutable", app_name),
        ("CFBundleInfoDictionaryVersion", "6.0"),
        ("CFBundlePackageType", "APPL"),
        ("CFBundleShortVersionString", app_version),
    ])

    icon_paths = Options.getIconPaths()

    if icon_paths:
        assert len(icon_paths) == 1
        icon_path = icon_paths[0]

        # Convert to single macOS .icns file if necessary
        if not icon_path.endswith(".icns"):
            logger.info(
                "File '%s' is not in macOS icon format, converting to it." %
                icon_path)

            icon_build_path = os.path.join(
                OutputDirectories.getSourceDirectoryPath(onefile=onefile),
                "icons",
            )
            makePath(icon_build_path)
            converted_icon_path = os.path.join(
                icon_build_path,
                "Icons.icns",
            )

            convertImageToIconFormat(
                logger=logger,
                image_filename=icon_path,
                icon_filename=converted_icon_path,
            )
            icon_path = converted_icon_path

        icon_name = os.path.basename(icon_path)
        resources_dir = os.path.join(bundle_dir, "Resources")
        makePath(resources_dir)

        copyFile(icon_path, os.path.join(resources_dir, icon_name))

        infos["CFBundleIconFile"] = icon_name

    # Console mode, which is why we have to use bundle in the first place typically.
    if Options.shallDisableConsoleWindow():
        infos["NSHighResolutionCapable"] = True
    else:
        infos["LSBackgroundOnly"] = True

    for resource_name, resource_desc in Options.getMacOSAppProtectedResourcesAccesses(
    ):
        if resource_name in infos:
            logger.sysexit("Duplicate value for '%s' is not allowed." %
                           resource_name)

        infos[resource_name] = resource_desc

    filename = os.path.join(bundle_dir, "Info.plist")

    if str is bytes:
        plist_contents = plistlib.writePlistToString(infos)
    else:
        plist_contents = plistlib.dumps(infos)

    with openTextFile(filename=filename, mode="wb") as plist_file:
        plist_file.write(plist_contents)