예제 #1
0
    def onModuleEncounter(self, module_filename, module_name, module_kind):
        # Make sure
        if module_name.isBelowNamespace("webview.platforms"):
            if isWin32Windows():
                result = module_name in (
                    "webview.platforms.winforms",
                    "webview.platforms.edgechromium",
                    "webview.platforms.edgehtml",
                    "webview.platforms.mshtml",
                    "webview.platforms.cef",
                )
                reason = "Platforms package of webview used on '%s'." % getOS()
            elif isMacOS():
                result = module_name == "webview.platforms.cocoa"
                reason = "Platforms package of webview used on '%s'." % getOS()
            elif getActiveQtPlugin() is not None:
                result = module_name = "webview.platforms.qt"
                reason = ("Platforms package of webview used due to '%s'." %
                          getActiveQtPlugin())
            else:
                result = module_name = "webview.platforms.gtk"
                reason = (
                    "Platforms package of webview used on '%s' without Qt plugin enabled."
                    % getOS())

            return result, reason
예제 #2
0
파일: Options.py 프로젝트: psydox/Nuitka
def _shallUseStaticLibPython():
    # return driven, pylint: disable=too-many-return-statements

    if shallMakeModule():
        return False, "not used in module mode"

    if options.static_libpython == "auto":
        # Nuitka-Python is good to to static linking.
        if isNuitkaPython():
            return True, "Nuitka-Python is broken."

        # Debian packages with are usable if the OS is new enough
        from nuitka.utils.StaticLibraries import (
            isDebianSuitableForStaticLinking, )

        if (isDebianBasedLinux() and isDebianPackagePython()
                and isDebianSuitableForStaticLinking()
                and not isPythonDebug()):
            return True, "Nuitka on Debian-Python needs package '%s' installed." % (
                "python2-dev" if str is bytes else "python3-dev")

        if isMSYS2MingwPython():
            return True, "Nuitka on MSYS2 needs package 'python-devel' installed."

        # For Anaconda default to trying static lib python library, which
        # normally is just not available or if it is even unusable.
        if isAnacondaPython() and not isMacOS() and not isWin32Windows():
            return True, "Nuitka on Anaconda needs package 'libpython' installed."

        if isPyenvPython():
            return True, "Nuitka on pyenv should not use '--enable-shared'."

    return options.static_libpython == "yes", None
예제 #3
0
파일: Onefile.py 프로젝트: psydox/Nuitka
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),
    )
예제 #4
0
def isApplePython():
    if not isMacOS():
        return False

    # Python2 on 10.15 or higher
    if "+internal-os" in sys.version:
        return True

    # Older macOS had that
    if isPathBelowOrSameAs(path="/usr/bin/", filename=getSystemPrefixPath()):
        return True
    # Newer macOS has that
    if isPathBelowOrSameAs(path="/Library/Developer/CommandLineTools/",
                           filename=getSystemPrefixPath()):
        return True

    # Xcode has that on macOS, we consider it an Apple Python for now, it might
    # be more usable than Apple Python, we but we delay that.
    if isPathBelowOrSameAs(
            path="/Applications/Xcode.app/Contents/Developer/",
            filename=getSystemPrefixPath(),
    ):
        return True

    return False
예제 #5
0
def isCPythonOfficialPackage():
    """Official CPython download, kind of hard to detect since self-compiled doesn't change much."""

    # For macOS however, it's very knowable.
    if isMacOS() and sys.executable.startswith(
            "/Library/Frameworks/Python.framework/Versions/"):
        return True

    return False
예제 #6
0
def isHomebrewPython():
    if not isMacOS():
        return False

    if "HOMEBREW_PREFIX" not in os.environ:
        return False

    if isPathBelowOrSameAs(path=os.environ["HOMEBREW_PREFIX"],
                           filename=getSystemPrefixPath()):
        return True
예제 #7
0
def _getCcacheGuessedPaths(python_prefix):
    if isWin32Windows():
        # Search the compiling Python, the Scons Python (likely the same, but not necessarily)
        # and then Anaconda, if an environment variable present from activated, or installed in
        # CI like Github actions.
        for python_dir in _getPythonDirCandidates(python_prefix):
            yield os.path.join(python_dir, "bin", "ccache.exe")
            yield os.path.join(python_dir, "scripts", "ccache.exe")

    elif isMacOS():
        # For macOS, we might find Homebrew ccache installed but not in PATH.
        yield "/usr/local/opt/ccache"
        yield "/opt/homebrew/bin/ccache"
예제 #8
0
    def _getNumpyCoreBinaries(numpy_dir):
        """Return any binaries in numpy package.

        Notes:
            This covers the special cases like MKL binaries.

        Returns:
            tuple of abspaths of binaries.
        """
        numpy_core_dir = os.path.join(numpy_dir, "core")

        # first look in numpy/.libs for binaries
        libdir = os.path.join(numpy_dir, ".libs" if not isMacOS() else ".dylibs")
        if os.path.isdir(libdir):
            for full_path, filename in listDir(libdir):
                yield full_path, filename

        # Then look for libraries in numpy.core package path
        # should already return the MKL files in ordinary cases
        re_anylib = re.compile(r"\w+\.(?:dll|so|dylib)", re.IGNORECASE)

        for full_path, filename in listDir(numpy_core_dir):
            if not re_anylib.match(filename):
                continue

            yield full_path, filename

        # Also look for MKL libraries in folder "above" numpy.
        # This should meet the layout of Anaconda installs.
        base_prefix = getSystemPrefixPath()

        if isWin32Windows():
            lib_dir = os.path.join(base_prefix, "Library", "bin")
        else:
            lib_dir = os.path.join(base_prefix, "lib")

        if os.path.isdir(lib_dir):
            re_mkllib = re.compile(r"^(?:lib)?mkl\w+\.(?:dll|so|dylib)", re.IGNORECASE)

            for full_path, filename in listDir(lib_dir):
                if isWin32Windows():
                    if not (
                        filename.startswith(("libi", "libm", "mkl"))
                        and filename.endswith(".dll")
                    ):
                        continue
                else:
                    if not re_mkllib.match(filename):
                        continue

                yield full_path, filename
예제 #9
0
 def _getResourcesTargetDir(self):
     """Where does the Qt bindings package expect the resources files."""
     if isMacOS():
         return "Content/Resources"
     elif isWin32Windows():
         if self.binding_name in ("PySide2", "PyQt5"):
             return "resources"
         else:
             # While PyQt6/PySide6 complains about these, they are not working
             # return os.path.join(self.binding_name, "resources")
             return "."
     else:
         if self.binding_name in ("PySide2", "PySide6", "PyQt6"):
             return "."
         elif self.binding_name == "PyQt5":
             return "resources"
         else:
             assert False
예제 #10
0
 def _getTranslationsTargetDir(self):
     """Where does the Qt bindings package expect the translation files."""
     if isMacOS():
         return "Content/Resources"
     elif isWin32Windows():
         if self.binding_name in ("PySide2", "PyQt5"):
             return "translations"
         elif self.binding_name == "PyQt6":
             # TODO: PyQt6 is complaining about not being in "translations", but ignores it there.
             return "."
         else:
             return os.path.join(self.binding_name, "translations")
     else:
         if self.binding_name in ("PySide2", "PySide6", "PyQt6"):
             return "."
         elif self.binding_name == "PyQt5":
             return "translations"
         else:
             assert False
예제 #11
0
def _enableDebugSystemSettings(env, job_count):
    if env.unstriped_mode:
        # Use debug format, so we get good tracebacks from it.
        if env.gcc_mode:
            env.Append(LINKFLAGS=["-g"])
            env.Append(CCFLAGS=["-g"])

            if not env.clang_mode:
                env.Append(CCFLAGS=["-feliminate-unused-debug-types"])
        elif env.msvc_mode:
            env.Append(CCFLAGS=["/Z7"])

            # Higher MSVC versions need this for parallel compilation
            if job_count > 1 and getMsvcVersion(env) >= 11:
                env.Append(CCFLAGS=["/FS"])

            env.Append(LINKFLAGS=["/DEBUG"])
    else:
        if env.gcc_mode:
            if isMacOS():
                env.Append(LINKFLAGS=["-Wno-deprecated-declarations"])
            elif not env.clang_mode:
                env.Append(LINKFLAGS=["-s"])
예제 #12
0
파일: GlfwPlugin.py 프로젝트: psydox/Nuitka
    def getImplicitImports(self, module):
        # Dealing with OpenGL is a bit detailed, pylint: disable=too-many-branches
        if module.getFullName() == "OpenGL":
            opengl_infos = self.queryRuntimeInformationSingle(
                setup_codes="import OpenGL.plugins",
                value=
                "[(f.name, f.import_path) for f in OpenGL.plugins.FormatHandler.all()]",
            )

            # TODO: Filter by name.
            for _name, import_path in opengl_infos:
                yield ModuleName(import_path).getPackageName()

            for line in getFileContentByLine(module.getCompileTimeFilename()):
                if line.startswith("PlatformPlugin("):
                    os_part, plugin_name_part = line[15:-1].split(",")
                    os_part = os_part.strip("' ")
                    plugin_name_part = plugin_name_part.strip(") '")
                    plugin_name_part = plugin_name_part[:plugin_name_part.
                                                        rfind(".")]

                    if os_part == "nt":
                        if getOS() == "Windows":
                            yield plugin_name_part
                    elif os_part.startswith("linux"):
                        if isLinux():
                            yield plugin_name_part
                    elif os_part.startswith("darwin"):
                        if isMacOS():
                            yield plugin_name_part
                    elif os_part.startswith(("posix", "osmesa", "egl")):
                        if getOS() != "Windows":
                            yield plugin_name_part
                    else:
                        self.sysexit(
                            "Undetected OS, please report bug for '%s'." %
                            os_part)
예제 #13
0
파일: Options.py 프로젝트: psydox/Nuitka
def mayDisableConsoleWindow():
    """:returns: bool derived from platform support of disabling the console,"""

    # TODO: What about MSYS2?
    return isWin32Windows() or isMacOS()
예제 #14
0
def setupCCompiler(env, lto_mode, pgo_mode, job_count):
    # This is driven by many branches on purpose and has a lot of things
    # to deal with for LTO checks and flags, pylint: disable=too-many-branches,too-many-statements

    # Enable LTO for compiler.
    _enableLtoSettings(
        env=env,
        lto_mode=lto_mode,
        pgo_mode=pgo_mode,
        job_count=job_count,
    )

    _detectWindowsSDK(env)
    _enableC11Settings(env)

    if env.gcc_mode:
        # Support for gcc and clang, restricting visibility as much as possible.
        env.Append(CCFLAGS=["-fvisibility=hidden"])

        if not env.c11_mode:
            env.Append(CXXFLAGS=["-fvisibility-inlines-hidden"])

        if isWin32Windows():
            # On Windows, exporting to DLL need to be controlled.
            env.Append(LINKFLAGS=["-Wl,--exclude-all-symbols"])

            # Make sure we handle import library on our own and put it into the
            # build directory.
            env.Append(LINKFLAGS=[
                "-Wl,--out-implib,%s" %
                os.path.join(env.source_dir, "import.lib")
            ])

        # Make it clear how to handle integer overflows, namely by wrapping around
        # to negative values.
        env.Append(CCFLAGS=["-fwrapv"])

        if not env.low_memory:
            # Avoid IO for compilation as much as possible, this should make the
            # compilation more memory hungry, but also faster.
            env.Append(CCFLAGS="-pipe")

    # Support for clang.
    if "clang" in env.the_cc_name:
        env.Append(CCFLAGS=["-w"])
        env.Append(CPPDEFINES=["_XOPEN_SOURCE"])

        # Don't export anything by default, this should create smaller executables.
        env.Append(
            CCFLAGS=["-fvisibility=hidden", "-fvisibility-inlines-hidden"])

        if env.debug_mode:
            env.Append(CCFLAGS=["-Wunused-but-set-variable"])

    # Support for macOS standalone backporting.
    if isMacOS():
        setEnvironmentVariable(env, "MACOSX_DEPLOYMENT_TARGET",
                               env.macos_min_version)

        target_flag = "--target=%s-apple-macos%s" % (
            env.macos_target_arch,
            env.macos_min_version,
        )

        env.Append(CCFLAGS=[target_flag])
        env.Append(LINKFLAGS=[target_flag])

    # The 32 bits MinGW does not default for API level properly, so help it.
    if env.mingw_mode:
        # Windows XP
        env.Append(CPPDEFINES=["_WIN32_WINNT=0x0501"])

    # Unicode entry points for programs.
    if env.mingw_mode:
        env.Append(LINKFLAGS=["-municode"])

    # Detect the gcc version
    if env.gcc_version is None and env.gcc_mode and not env.clang_mode:
        env.gcc_version = myDetectVersion(env, env.the_compiler)

    # Older g++ complains about aliasing with Py_True and Py_False, but we don't
    # care.
    if env.gcc_mode and not env.clang_mode and env.gcc_version < (4, 5):
        env.Append(CCFLAGS=["-fno-strict-aliasing"])

    # For gcc 4.6 or higher, there are some new interesting functions.
    if env.gcc_mode and not env.clang_mode and env.gcc_version >= (4, 6):
        env.Append(CCFLAGS=["-fpartial-inlining"])

        if env.debug_mode:
            env.Append(CCFLAGS=["-Wunused-but-set-variable"])

    # Save some memory for gcc by not tracing macro code locations at all.
    if (not env.debug_mode and env.gcc_mode and not env.clang_mode
            and env.gcc_version >= (5, )):
        env.Append(CCFLAGS=["-ftrack-macro-expansion=0"])

    # We don't care about deprecations.
    if env.gcc_mode and not env.clang_mode:
        env.Append(CCFLAGS=["-Wno-deprecated-declarations"])

    # The var-tracking does not scale, disable it. Should we really need it, we
    # can enable it. TODO: Does this cause a performance loss?
    if env.gcc_mode and not env.clang_mode:
        env.Append(CCFLAGS=["-fno-var-tracking"])

    # For large files, these can issue warnings about disabling
    # itself, while we do not need it really.
    if env.gcc_mode and not env.clang_mode and env.gcc_version >= (6, ):
        env.Append(CCFLAGS=["-Wno-misleading-indentation"])

    # Disable output of notes, e.g. on struct alignment layout changes for
    # some arches, we don't care.
    if env.gcc_mode and not env.clang_mode:
        env.Append(CCFLAGS=["-fcompare-debug-second"])

    # Prevent using LTO when told not to use it, causes errors with some
    # static link libraries.
    if (env.gcc_mode and not env.clang_mode and env.static_libpython
            and not env.lto_mode):
        env.Append(CCFLAGS=["-fno-lto"])
        env.Append(LINKFLAGS=["-fno-lto"])

    # Set optimization level for gcc and clang in LTO mode
    if env.gcc_mode and env.lto_mode:
        if env.debug_mode:
            env.Append(LINKFLAGS=["-Og"])
        else:
            # For LTO with static libpython combined, there are crashes with Python core
            # being inlined, so we must refrain from that. On Windows there is no such
            # thing, and Nuitka-Python is not affected.
            env.Append(LINKFLAGS=[
                "-O3" if env.nuitka_python or os.name == "nt"
                or not env.static_libpython else "-O2"
            ])

    # When debugging, optimize less than when optimizing, when not remove
    # assertions.
    if env.debug_mode:
        if env.clang_mode or (env.gcc_mode and env.gcc_version >= (4, 8)):
            env.Append(CCFLAGS=["-Og"])
        elif env.gcc_mode:
            env.Append(CCFLAGS=["-O1"])
        elif env.msvc_mode:
            env.Append(CCFLAGS=["-O2"])
    else:
        if env.gcc_mode:
            env.Append(CCFLAGS=[
                "-O3" if env.nuitka_python or os.name == "nt"
                or not env.static_libpython else "-O2"
            ])
        elif env.msvc_mode:
            env.Append(CCFLAGS=[
                "/Ox",  # Enable most speed optimization
                "/GF",  # Eliminate duplicate strings.
                "/Gy",  # Function level object storage, to allow removing unused ones
            ])

        env.Append(CPPDEFINES=["__NUITKA_NO_ASSERT__"])

    _enableDebugSystemSettings(env, job_count=job_count)

    if env.gcc_mode and not env.noelf_mode:
        env.Append(LINKFLAGS=["-z", "noexecstack"])
예제 #15
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())
            },
        )
예제 #16
0
파일: Options.py 프로젝트: psydox/Nuitka
def isClang():
    """:returns: bool derived from ``--clang`` or enforced by platform, e.g. macOS or FreeBSD some targets."""

    return (options.clang or isMacOS() or isOpenBSD()
            or (isFreeBSD() and getArchitecture() != "powerpc"))
예제 #17
0
 def _getWebEngineTargetDir():
     """Where does the Qt bindings package expect the web process executable."""
     return "Helpers" if isMacOS() else "."
예제 #18
0
파일: Options.py 프로젝트: psydox/Nuitka
def parseArgs():
    """Parse the command line arguments

    :meta private:
    """
    # singleton with many cases checking the options right away.
    # pylint: disable=global-statement,too-many-branches,too-many-locals,too-many-statements
    global is_nuitka_run, options, positional_args, extra_args, is_debug, is_nondebug
    global is_fullcompat, is_report_missing, is_verbose

    if os.name == "nt":
        # Windows store Python's don't allow looking at the python, catch that.
        try:
            with openTextFile(sys.executable, "rb"):
                pass
        except OSError:
            Tracing.general.sysexit(
                "Error, the Python from Windows store is not supported, check the User Manual of Nuitka ."
            )

    is_nuitka_run, options, positional_args, extra_args = parseOptions(
        logger=Tracing.options_logger)

    is_debug = _isDebug()
    is_nondebug = not is_debug
    is_fullcompat = _isFullCompat()

    # TODO: Have dedicated option for it.
    is_report_missing = is_debug

    if options.quiet or int(os.environ.get("NUITKA_QUIET", "0")):
        Tracing.setQuiet()

    if not shallDumpBuiltTreeXML():
        Tracing.options_logger.info("Used command line options: %s" %
                                    " ".join(sys.argv[1:]))

    if os.environ.get("NUITKA_REEXECUTION") and not isAllowedToReexecute():
        Tracing.general.sysexit(
            "Error, not allowed to re-execute, but that has happened.")

    if options.progress_bar:
        Progress.enableProgressBar()

    if options.verbose_output:
        Tracing.optimization_logger.setFileHandle(
            # Can only have unbuffered binary IO in Python3, therefore not disabling buffering here.
            openTextFile(options.verbose_output, "w", encoding="utf8"))

        options.verbose = True

    is_verbose = options.verbose

    Tracing.optimization_logger.is_quiet = not options.verbose

    if options.show_inclusion_output:
        Tracing.inclusion_logger.setFileHandle(
            # Can only have unbuffered binary IO in Python3, therefore not disabling buffering here.
            openTextFile(options.show_inclusion_output, "w", encoding="utf8"))

        options.show_inclusion = True

    Tracing.progress_logger.is_quiet = not options.show_progress

    # Onefile implies standalone build.
    if options.is_onefile:
        options.is_standalone = True

    # Standalone implies no_site build
    if options.is_standalone:
        options.python_flags.insert(0, "no_site")

    # Provide a tempdir spec implies onefile tempdir, even on Linux.
    if options.onefile_tempdir_spec:
        options.is_onefile_tempdir = True

        if os.path.normpath(options.onefile_tempdir_spec) == ".":
            Tracing.options_logger.sysexit("""\
Error, using '.' as a value for '--onefile-tempdir-spec' is not supported,
you cannot unpack the onefile payload into the same directory as the binary,
as that would overwrite it and cause locking issues as well.""")

        if options.onefile_tempdir_spec.count("%") % 2 != 0:
            Tracing.options_logger.warning(
                """Unmatched '%%' is suspicious for '--onefile-tempdir-spec' and may
not do what you want it to do: '%s'""" % options.onefile_tempdir_spec)

        if options.onefile_tempdir_spec.count("%") == 0:
            Tracing.options_logger.warning(
                """Not using any variables for '--onefile-tempdir-spec' should only be
done if your program absolutely needs to be in the same path always: '%s'""" %
                options.onefile_tempdir_spec)

        if os.path.isabs(options.onefile_tempdir_spec):
            Tracing.options_logger.warning(
                """Using an absolute path should be avoided unless you are targeting a
very well known environment: '%s'""" % options.onefile_tempdir_spec)
        elif relpath(options.onefile_tempdir_spec):
            Tracing.options_logger.warning(
                """Using an relative path above the executable should be avoided unless you are targeting a
very well known environment: '%s'""" % options.onefile_tempdir_spec)

    # Standalone mode implies an executable, not importing "site" module, which is
    # only for this machine, recursing to all modules, and even including the
    # standard library.
    if options.is_standalone:
        if options.module_mode:
            Tracing.options_logger.sysexit("""\
Error, conflicting options, cannot make standalone module, only executable.

Modules are supposed to be imported to an existing Python installation, therefore it
makes no sense to include a Python runtime.""")

    for any_case_module in getShallFollowModules():
        if any_case_module.startswith("."):
            bad = True
        else:
            for char in "/\\:":
                if char in any_case_module:
                    bad = True
                    break
            else:
                bad = False

        if bad:
            Tracing.options_logger.sysexit("""\
Error, '--follow-import-to' takes only module names or patterns, not directory path '%s'."""
                                           % any_case_module)

    for no_case_module in getShallFollowInNoCase():
        if no_case_module.startswith("."):
            bad = True
        else:
            for char in "/\\:":
                if char in no_case_module:
                    bad = True
                    break
            else:
                bad = False

        if bad:
            Tracing.options_logger.sysexit("""\
Error, '--nofollow-import-to' takes only module names or patterns, not directory path '%s'."""
                                           % no_case_module)

    scons_python = getPythonPathForScons()

    if scons_python is not None and not os.path.isfile(scons_python):
        Tracing.options_logger.sysexit(
            "Error, no such Python binary %r, should be full path." %
            scons_python)

    if options.output_filename is not None and (
        (isStandaloneMode() and not isOnefileMode()) or shallMakeModule()):
        Tracing.options_logger.sysexit("""\
Error, may only specify output filename for acceleration and onefile mode,
but not for module mode where filenames are mandatory, and not for
standalone where there is a sane default used inside the dist folder.""")

    if isLinux():
        if len(getIconPaths()) > 1:
            Tracing.options_logger.sysexit(
                "Error, can only use one icon file on Linux.")

    if isMacOS():
        if len(getIconPaths()) > 1:
            Tracing.options_logger.sysexit(
                "Error, can only use one icon file on macOS.")

    for icon_path in getIconPaths():
        if "#" in icon_path and isWin32Windows():
            icon_path, icon_index = icon_path.rsplit("#", 1)

            if not icon_index.isdigit() or int(icon_index) < 0:
                Tracing.options_logger.sysexit(
                    "Error, icon number in %r not valid." %
                    (icon_path + "#" + icon_index))

        if not os.path.exists(icon_path):
            Tracing.options_logger.sysexit(
                "Error, icon path %r does not exist." % icon_path)

        if getWindowsIconExecutablePath():
            Tracing.options_logger.sysexit(
                "Error, can only use icons from template executable or from icon files, but not both."
            )

    icon_exe_path = getWindowsIconExecutablePath()
    if icon_exe_path is not None and not os.path.exists(icon_exe_path):
        Tracing.options_logger.sysexit("Error, icon path %r does not exist." %
                                       icon_exe_path)

    try:
        file_version = getWindowsFileVersion()
    except Exception:  # Catch all the things, don't want any interface, pylint: disable=broad-except
        Tracing.options_logger.sysexit(
            "Error, file version must be a tuple of up to 4 integer values.")

    try:
        product_version = getWindowsProductVersion()
    except Exception:  # Catch all the things, don't want any interface, pylint: disable=broad-except
        Tracing.options_logger.sysexit(
            "Error, product version must be a tuple of up to 4 integer values."
        )

    if getWindowsCompanyName() == "":
        Tracing.options_logger.sysexit(
            """Error, empty string is not an acceptable company name.""")

    if getWindowsProductName() == "":
        Tracing.options_logger.sysexit(
            """Error, empty string is not an acceptable product name.""")

    splash_screen_filename = getWindowsSplashScreen()

    if splash_screen_filename is not None:
        if not os.path.isfile(splash_screen_filename):
            Tracing.options_logger.sysexit(
                "Error, specified splash screen image '%s' does not exist." %
                splash_screen_filename)

    if file_version or product_version or getWindowsVersionInfoStrings():
        if not (file_version or product_version) and getWindowsCompanyName():
            Tracing.options_logger.sysexit(
                "Error, company name and file or product version need to be given when any version information is given."
            )

    if isOnefileMode() and not hasOnefileSupportedOS():
        Tracing.options_logger.sysexit("Error, unsupported OS for onefile %r" %
                                       getOS())

    if options.follow_none and options.follow_all:
        Tracing.options_logger.sysexit(
            "Conflicting options '--follow-imports' and '--nofollow-imports' given."
        )

    for module_pattern in getShallIncludePackageData():
        if (module_pattern.startswith("-") or "/" in module_pattern
                or "\\" in module_pattern):
            Tracing.options_logger.sysexit(
                "Error, '--include-package-data' needs module name or pattern as an argument, not %r."
                % module_pattern)

    for module_pattern in getShallFollowModules():
        if (module_pattern.startswith("-") or "/" in module_pattern
                or "\\" in module_pattern):
            Tracing.options_logger.sysexit(
                "Error, '--follow-import-to' options needs module name or pattern as an argument, not %r."
                % module_pattern)
    for module_pattern in getShallFollowInNoCase():
        if (module_pattern.startswith("-") or "/" in module_pattern
                or "\\" in module_pattern):
            Tracing.options_logger.sysexit(
                "Error, '--nofollow-import-to' options needs module name or pattern as an argument, not %r."
                % module_pattern)

    for data_file in options.data_files:
        if "=" not in data_file:
            Tracing.options_logger.sysexit(
                "Error, malformed data file description, must specify relative target path separated with '='."
            )

        if data_file.count("=") == 1:
            src, dst = data_file.split("=", 1)

            filenames = resolveShellPatternToFilenames(src)

            if len(filenames) > 1 and not dst.endswith(("/", os.path.sep)):
                Tracing.options_logger.sysexit(
                    "Error, pattern '%s' matches more than one file, but target has no trailing slash, not a directory."
                    % src)
        else:
            src, dst, pattern = data_file.split("=", 2)

            filenames = resolveShellPatternToFilenames(
                os.path.join(src, pattern))

        if not filenames:
            Tracing.options_logger.sysexit(
                "Error, '%s' does not match any files." % src)

        if os.path.isabs(dst):
            Tracing.options_logger.sysexit(
                "Error, must specify relative target path for data file, not absolute path '%s'."
                % data_file)

    for data_dir in options.data_dirs:
        if "=" not in data_dir:
            Tracing.options_logger.sysexit(
                "Error, malformed data dir description, must specify relative target path with '=' separating it."
            )

        src, dst = data_dir.split("=", 1)

        if os.path.isabs(dst):
            Tracing.options_logger.sysexit(
                "Error, must specify relative target path for data dir, not %r as in %r."
                % (dst, data_dir))

        if not os.path.isdir(src):
            Tracing.options_logger.sysexit(
                "Error, must specify existing source data directory, not %r as in %r."
                % (dst, data_dir))

    for pattern in getShallFollowExtraFilePatterns():
        if os.path.isdir(pattern):
            Tracing.options_logger.sysexit(
                "Error, pattern %r given to '--include-plugin-files' cannot be a directory name."
                % pattern)

    if options.static_libpython == "yes" and getSystemStaticLibPythonPath(
    ) is None:
        Tracing.options_logger.sysexit(
            "Error, static libpython is not found or not supported for this Python installation."
        )

    if shallUseStaticLibPython() and getSystemStaticLibPythonPath() is None:
        Tracing.options_logger.sysexit(
            """Error, usable static libpython is not found for this Python installation. You \
might be missing required packages. Disable with --static-libpython=no" if you don't \
want to install it.""")

    if isApplePython():
        if isStandaloneMode():
            Tracing.options_logger.sysexit(
                "Error, for standalone mode, Apple Python from macOS is not supported, use e.g. CPython instead."
            )

        if str is bytes:
            Tracing.options_logger.sysexit(
                "Error, Apple Python 2.7 from macOS is not usable as per Apple decision, use e.g. CPython 2.7 instead."
            )

    if isStandaloneMode() and isLinux(
    ) and getExecutablePath("patchelf") is None:
        Tracing.options_logger.sysexit(
            "Error, standalone mode on Linux requires 'patchelf' to be installed. Use 'apt/dnf/yum install patchelf' first."
        )

    pgo_executable = getPgoExecutable()
    if pgo_executable and not isPathExecutable(pgo_executable):
        Tracing.options_logger.sysexit(
            "Error, path '%s' to binary to use for PGO is not executable." %
            pgo_executable)
예제 #19
0
파일: Options.py 프로젝트: psydox/Nuitka
def shallCreateAppBundle():
    """*bool* shall create an application bundle, derived from ``--macos-create-app-bundle`` value"""
    return options.macos_create_bundle and isMacOS()
예제 #20
0
    def considerDataFiles(self, module):
        full_name = module.getFullName()

        if full_name == self.binding_name and (
                "qml" in self.getQtPluginsSelected()
                or "all" in self.getQtPluginsSelected()):
            qml_plugin_dir = self._getQmlDirectory()
            qml_target_dir = self._getQmlTargetDir()

            self.info("Including Qt plugins 'qml' below '%s'." %
                      qml_target_dir)

            for filename in self._getQmlFileList(dlls=False):
                filename_relative = os.path.relpath(filename, qml_plugin_dir)

                yield self.makeIncludedDataFile(
                    source_path=filename,
                    dest_path=os.path.join(
                        qml_target_dir,
                        filename_relative,
                    ),
                    reason="Qt QML datafile",
                    tags="qml",
                )
        elif self.isQtWebEngineModule(
                full_name) and not self.webengine_done_data:
            self.webengine_done_data = True

            # TODO: This is probably wrong/not needed on macOS
            if not isMacOS():
                yield self.makeIncludedGeneratedDataFile(
                    data="""\
[Paths]
Prefix = .
""",
                    dest_path="qt6.conf"
                    if "6" in self.binding_name else "qt.conf",
                    reason="QtWebEngine needs Qt configuration file",
                )

            resources_dir = self._getResourcesPath()

            for filename, filename_relative in listDir(resources_dir):
                yield self.makeIncludedDataFile(
                    source_path=filename,
                    dest_path=os.path.join(self._getResourcesTargetDir(),
                                           filename_relative),
                    reason="Qt resources",
                )

            if not self.no_qt_translations:
                translations_path = self._getTranslationsPath()

                for filename in getFileList(translations_path):
                    filename_relative = os.path.relpath(
                        filename, translations_path)
                    dest_path = self._getTranslationsTargetDir()

                    yield self.makeIncludedDataFile(
                        source_path=filename,
                        dest_path=os.path.join(dest_path, filename_relative),
                        reason="Qt translation",
                        tags="translation",
                    )
예제 #21
0
            # For relative import, that is OK, we will still try absolute.
            pass
        else:
            if _debug_module_finding:
                my_print(
                    "findModule: Found absolute imported module '%s' in filename '%s':"
                    % (module_name, module_filename)
                )

            return package_name, module_filename, "absolute"

    return None, None, "not-found"


# Some platforms are case insensitive.
case_sensitive = not isMacOS() and getOS() != "Windows"

ImportScanFinding = collections.namedtuple(
    "ImportScanFinding", ("found_in", "priority", "full_path", "search_order")
)

# We put here things that are not worth it (Cython is not really used by
# anything really, or where it's know to not have a big # impact, e.g. lxml.

unworthy_namespaces = ("Cython", "lxml")


def _reportCandidates(package_name, module_name, candidate, candidates):
    module_name = (
        package_name.getChildNamed(module_name)
        if package_name is not None
예제 #22
0
def shallCreateAppBundle():
    """*bool* shall create an application bundle"""
    return options.macos_create_bundle and isMacOS()
예제 #23
0
def _injectCcache(env, cc_path, python_prefix, target_arch,
                  assume_yes_for_downloads):
    ccache_binary = os.environ.get("NUITKA_CCACHE_BINARY")

    # If not provided, search it in PATH and guessed directories.
    if ccache_binary is None:
        ccache_binary = getExecutablePath("ccache", env=env)

        if ccache_binary is None:
            for candidate in _getCcacheGuessedPaths(python_prefix):
                scons_details_logger.info(
                    "Checking if ccache is at '%s' guessed path." % candidate)

                if os.path.exists(candidate):
                    ccache_binary = candidate

                    scons_details_logger.info(
                        "Using ccache '%s' from guessed path." % ccache_binary)

                    break

        if ccache_binary is None:
            if isWin32Windows():
                url = "https://github.com/ccache/ccache/releases/download/v3.7.12/ccache-3.7.12-windows-32.zip"
                ccache_binary = getCachedDownload(
                    url=url,
                    is_arch_specific=False,
                    specificity=url.rsplit("/", 2)[1],
                    flatten=True,
                    binary="ccache.exe",
                    message=
                    "Nuitka will make use of ccache to speed up repeated compilation.",
                    reject=None,
                    assume_yes_for_downloads=assume_yes_for_downloads,
                )
            elif isMacOS():
                # TODO: Do not yet have M1 access to create one and 10.14 is minimum
                # we managed to compile ccache for.
                if target_arch != "arm64" and tuple(
                        int(d)
                        for d in platform.release().split(".")) >= (18, 2):
                    url = "https://nuitka.net/ccache/v4.2.1/ccache-4.2.1.zip"

                    ccache_binary = getCachedDownload(
                        url=url,
                        is_arch_specific=False,
                        specificity=url.rsplit("/", 2)[1],
                        flatten=True,
                        binary="ccache",
                        message=
                        "Nuitka will make use of ccache to speed up repeated compilation.",
                        reject=None,
                        assume_yes_for_downloads=assume_yes_for_downloads,
                    )

    else:
        scons_details_logger.info(
            "Using ccache '%s' from NUITKA_CCACHE_BINARY environment variable."
            % ccache_binary)

    if ccache_binary is not None and os.path.exists(ccache_binary):
        # Make sure the
        # In case we are on Windows, make sure the Anaconda form runs outside of Anaconda
        # environment, by adding DLL folder to PATH.
        assert areSamePaths(
            getExecutablePath(os.path.basename(env.the_compiler), env=env),
            cc_path)

        # We use absolute paths for CC, pass it like this, as ccache does not like absolute.
        env["CXX"] = env["CC"] = '"%s" "%s"' % (ccache_binary, cc_path)

        # Spare ccache the detection of the compiler, seems it will also misbehave when it's
        # prefixed with "ccache" on old gcc versions in terms of detecting need for C++ linkage.
        env["LINK"] = cc_path

        scons_details_logger.info(
            "Found ccache '%s' to cache C compilation result." % ccache_binary)
        scons_details_logger.info(
            "Providing real CC path '%s' via PATH extension." % cc_path)
예제 #24
0
def main():
    """Main program flow of Nuitka

    At this point, options will be parsed already, Nuitka will be executing
    in the desired version of Python with desired flags, and we just get
    to execute the task assigned.

    We might be asked to only re-compile generated C, dump only an XML
    representation of the internal node tree after optimization, etc.
    """

    # Main has to fulfill many options, leading to many branches and statements
    # to deal with them.  pylint: disable=too-many-branches,too-many-statements

    # In case we are in a PGO run, we read its information first, so it becomes
    # available for later parts.
    pgo_filename = getPythonPgoInput()
    if pgo_filename is not None:
        readPGOInputFile(pgo_filename)

    if not Options.shallDumpBuiltTreeXML():
        general.info(
            "Starting Python compilation with Nuitka %r on Python %r commercial %r."
            % (getNuitkaVersion(), python_version_str, getCommercialVersion())
        )

    filename = Options.getPositionalArgs()[0]

    # Inform the importing layer about the main script directory, so it can use
    # it when attempting to follow imports.
    Importing.setMainScriptDirectory(
        main_dir=os.path.dirname(os.path.abspath(filename))
    )

    addIncludedDataFilesFromFileOptions()

    # Turn that source code into a node tree structure.
    try:
        main_module = _createNodeTree(filename=filename)
    except (SyntaxError, IndentationError) as e:
        handleSyntaxError(e)

    addIncludedDataFilesFromPackageOptions()

    if Options.shallDumpBuiltTreeXML():
        # XML output only.
        for module in ModuleRegistry.getDoneModules():
            dumpTreeXML(module)
    else:
        # Make the actual compilation.
        result, options = compileTree()

        # Exit if compilation failed.
        if not result:
            sys.exit(1)

        # Relaunch in case of Python PGO input to be produced.
        if Options.shallCreatePgoInput():
            # Will not return.
            pgo_filename = OutputDirectories.getPgoRunInputFilename()
            general.info(
                "Restarting compilation using collected information from '%s'."
                % pgo_filename
            )
            reExecuteNuitka(pgo_filename=pgo_filename)

        if Options.shallNotDoExecCCompilerCall():
            if Options.isShowMemory():
                MemoryUsage.showMemoryTrace()

            sys.exit(0)

        executePostProcessing()

        copyDataFiles()

        if Options.isStandaloneMode():
            binary_filename = options["result_exe"]

            setMainEntryPoint(binary_filename)

            dist_dir = OutputDirectories.getStandaloneDirectoryPath()

            for module in ModuleRegistry.getDoneModules():
                addIncludedEntryPoints(Plugins.considerExtraDlls(dist_dir, module))

            copyDllsUsed(
                source_dir=OutputDirectories.getSourceDirectoryPath(),
                dist_dir=dist_dir,
                standalone_entry_points=getStandaloneEntryPoints(),
            )

            Plugins.onStandaloneDistributionFinished(dist_dir)

            if Options.isOnefileMode():
                packDistFolderToOnefile(dist_dir, binary_filename)

                if Options.isRemoveBuildDir():
                    general.info("Removing dist folder %r." % dist_dir)

                    removeDirectory(path=dist_dir, ignore_errors=False)
                else:
                    general.info(
                        "Keeping dist folder %r for inspection, no need to use it."
                        % dist_dir
                    )

        # Remove the source directory (now build directory too) if asked to.
        source_dir = OutputDirectories.getSourceDirectoryPath()

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

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

        final_filename = OutputDirectories.getResultFullpath(
            onefile=Options.isOnefileMode()
        )

        if Options.isStandaloneMode() and isMacOS():
            general.info(
                "Created binary that runs on macOS %s (%s) or higher."
                % (options["macos_min_version"], options["macos_target_arch"])
            )

        Plugins.onFinalResult(final_filename)

        general.info("Successfully created %r." % final_filename)

        report_filename = Options.getCompilationReportFilename()

        if report_filename:
            writeCompilationReport(report_filename)

        # Execute the module immediately if option was given.
        if Options.shallExecuteImmediately():
            run_filename = OutputDirectories.getResultRunFilename(
                onefile=Options.isOnefileMode()
            )

            general.info("Launching %r." % run_filename)

            if Options.shallMakeModule():
                executeModule(
                    tree=main_module,
                    clean_path=Options.shallClearPythonPathEnvironment(),
                )
            else:
                executeMain(
                    binary_filename=run_filename,
                    clean_path=Options.shallClearPythonPathEnvironment(),
                )
예제 #25
0
def setCommonOptions(options):
    # 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

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

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

    if not Options.shallUseProgressBar():
        options["progress_bar"] = "false"

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

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

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

    if Options.getMsvcVersion():
        options["msvc_version"] = Options.getMsvcVersion()

    if Options.shallDisableCCacheUsage():
        options["disable_ccache"] = asBoolStr(True)

    if Options.shallDisableConsoleWindow():
        options["disable_console"] = asBoolStr(True)

    if Options.getLtoMode() != "auto":
        options["lto_mode"] = Options.getLtoMode()

    if getOS() == "Windows" or isMacOS():
        options["noelf_mode"] = asBoolStr(True)

    if Options.isUnstriped():
        options["unstriped_mode"] = asBoolStr(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())

    cpp_include_dirs = Plugins.getExtraIncludeDirectories()
    if cpp_include_dirs:
        options["cpp_include_dirs"] = ",".join(cpp_include_dirs)

    link_dirs = Plugins.getExtraLinkDirectories()
    if link_dirs:
        options["link_dirs"] = ",".join(link_dirs)

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

    if Utils.isMacOS():
        macos_min_version = detectBinaryMinMacOS(sys.executable)

        if macos_min_version is None:
            Tracing.general.sysexit(
                "Could not detect minimum macOS version for %r." %
                sys.executable)

        options["macos_min_version"] = macos_min_version

        macos_target_arch = Options.getMacOSTargetArch()

        if macos_target_arch == "universal":
            Tracing.general.sysexit(
                "Cannot create universal macOS binaries (yet), please pick an arch and create two binaries."
            )

        options["macos_target_arch"] = macos_target_arch