def enableClcache(the_compiler, env, source_dir):
    importFromInlineCopy("atomicwrites", must_exist=True)
    importFromInlineCopy("clcache", must_exist=True)

    cl_binary = getExecutablePath(the_compiler, env)

    # The compiler is passed via environment.
    setEnvironmentVariable(env, "CLCACHE_CL", cl_binary)
    env["CXX"] = env["CC"] = "<clcache>"

    setEnvironmentVariable(env, "CLCACHE_HIDE_OUTPUTS", "1")

    # The clcache stats filename needs absolute path, otherwise it will not work.
    clcache_stats_filename = os.path.abspath(
        os.path.join(source_dir, "clcache-stats.%d.txt" % os.getpid()))

    setEnvironmentVariable(env, "CLCACHE_STATS", clcache_stats_filename)
    env["CLCACHE_STATS"] = clcache_stats_filename

    # Unless asked to do otherwise, store ccache files in our own directory.
    if "CLCACHE_DIR" not in os.environ:
        clcache_dir = os.path.join(getCacheDir(), "clcache")
        makePath(clcache_dir)
        clcache_dir = getExternalUsePath(clcache_dir)
        setEnvironmentVariable(env, "CLCACHE_DIR", clcache_dir)
        env["CLCACHE_DIR"] = clcache_dir

    scons_details_logger.info(
        "Using inline copy of clcache with %r cl binary." % cl_binary)

    # Do not consider scons cache anymore.
    return True
예제 #2
0
def enableClcache(the_compiler, env, source_dir):
    importFromInlineCopy("atomicwrites", must_exist=True)
    importFromInlineCopy("clcache", must_exist=True)

    # Avoid importing this in threads, triggers CPython 3.9 importing bugs at least,
    # do it now, so it's not a race issue.
    import concurrent.futures.thread  # pylint: disable=I0021,unused-import,unused-variable

    cl_binary = getExecutablePath(the_compiler, env)

    # The compiler is passed via environment.
    setEnvironmentVariable(env, "CLCACHE_CL", cl_binary)
    env["CXX"] = env["CC"] = "<clcache>"

    setEnvironmentVariable(env, "CLCACHE_HIDE_OUTPUTS", "1")

    # The clcache stats filename needs absolute path, otherwise it will not work.
    clcache_stats_filename = os.path.abspath(
        os.path.join(source_dir, "clcache-stats.%d.txt" % os.getpid())
    )

    setEnvironmentVariable(env, "CLCACHE_STATS", clcache_stats_filename)
    env["CLCACHE_STATS"] = clcache_stats_filename

    # Unless asked to do otherwise, store ccache files in our own directory.
    if "CLCACHE_DIR" not in os.environ:
        clcache_dir = os.path.join(getCacheDir(), "clcache")
        makePath(clcache_dir)
        clcache_dir = getExternalUsePath(clcache_dir)
        setEnvironmentVariable(env, "CLCACHE_DIR", clcache_dir)
        env["CLCACHE_DIR"] = clcache_dir

    scons_details_logger.info(
        "Using inline copy of clcache with %r cl binary." % cl_binary
    )
def myDetectVersion(env, cc):
    """Return the version of the GNU compiler, or None if it is not a GNU compiler."""
    cc = env.subst(cc)
    if not cc:
        return None
    if "++" in os.path.basename(cc):
        return None

    version = None

    clvar = tuple(SCons.Util.CLVar(cc))

    if clvar in v_cache:
        return v_cache[clvar]

    clvar0 = os.path.basename(clvar[0])

    if isGccName(clvar0) or "clang" in clvar0:
        command = list(clvar) + ["-dumpversion"]
    else:
        command = list(clvar) + ["--version"]

    # pipe = SCons.Action._subproc(env, SCons.Util.CLVar(cc) + ['-dumpversion'],
    pipe = SCons.Action._subproc(  # pylint: disable=protected-access
        env, command, stdin="devnull", stderr="devnull", stdout=subprocess.PIPE
    )

    line = pipe.stdout.readline()
    # Non-GNU compiler's output (like AIX xlc's) may exceed the stdout buffer:
    # So continue with reading to let the child process actually terminate.
    while pipe.stdout.readline():
        pass

    ret = pipe.wait()
    if ret != 0:
        return None

    if str is not bytes and type(line) is bytes:
        line = decodeData(line)

    line = line.strip()

    match = re.findall(r"[0-9]+(?:\.[0-9]+)+", line)
    if match:
        version = match[0]
    else:
        # gcc 8 or higher
        version = line.strip()

    version = tuple(int(part) for part in version.split("."))

    scons_details_logger.info(
        "CC %r version check gives %r from %r" % (cc, version, line)
    )

    v_cache[clvar] = version
    return version
예제 #4
0
파일: SconsHacks.py 프로젝트: psydox/Nuitka
def myDetectVersion(env, cc):
    """Return the version of the GNU compiler, or None if it is not a GNU compiler."""
    cc = env.subst(cc)
    if not cc:
        return None
    if "++" in os.path.basename(cc):
        return None

    # Make path absolute, to improve cache hit rate.
    cc = getExecutablePath(cc, env)
    if cc is None:
        return None

    if cc not in v_cache:
        v_cache[cc] = _myDetectVersion(env, (cc,))

        scons_details_logger.info("CC %r version check gives %r" % (cc, v_cache[cc]))

    return v_cache[cc]
예제 #5
0
def _myDetectVersion(env, clvar):
    clvar0 = os.path.basename(clvar[0])

    if isGccName(clvar0) or "clang" in clvar0:
        command = clvar + ("-dumpversion", )
    else:
        command = clvar + ("--version", )

    # pipe = SCons.Action._subproc(env, SCons.Util.CLVar(cc) + ['-dumpversion'],
    pipe = SCons.Action._subproc(  # pylint: disable=protected-access
        env,
        command,
        stdin="devnull",
        stderr="devnull",
        stdout=subprocess.PIPE)

    line = pipe.stdout.readline()
    # Non-GNU compiler's output (like AIX xlc's) may exceed the stdout buffer:
    # So continue with reading to let the child process actually terminate.
    while pipe.stdout.readline():
        pass

    ret = pipe.wait()
    if ret != 0:
        scons_details_logger.info("Error, error exit from %r (%d) gave %r." %
                                  (command, ret, pipe.stderr.read()))
        return None

    if str is not bytes and type(line) is bytes:
        line = decodeData(line)

    line = line.strip()

    match = re.findall(r"[0-9]+(?:\.[0-9]+)+", line)
    if match:
        version = match[0]
    else:
        # gcc 8 or higher
        version = line.strip()

    version = tuple(int(part) for part in version.split("."))

    return version
예제 #6
0
def enableCcache(
    the_compiler,
    env,
    source_dir,
    python_prefix,
    assume_yes_for_downloads,
):
    # The ccache needs absolute path, otherwise it will not work.
    ccache_logfile = os.path.abspath(
        os.path.join(source_dir, "ccache-%d.txt" % os.getpid())
    )

    setEnvironmentVariable(env, "CCACHE_LOGFILE", ccache_logfile)
    env["CCACHE_LOGFILE"] = ccache_logfile

    # Unless asked to do otherwise, store ccache files in our own directory.
    if "CCACHE_DIR" not in os.environ:
        ccache_dir = os.path.join(getCacheDir(), "ccache")
        makePath(ccache_dir)
        ccache_dir = getExternalUsePath(ccache_dir)
        setEnvironmentVariable(env, "CCACHE_DIR", ccache_dir)
        env["CCACHE_DIR"] = ccache_dir

    # First check if it's not already supposed to be a ccache, then do nothing.
    cc_path = getExecutablePath(the_compiler, env=env)

    cc_is_link, cc_link_path = getLinkTarget(cc_path)
    if cc_is_link and os.path.basename(cc_link_path) == "ccache":
        scons_details_logger.info(
            "Chosen compiler %s is pointing to ccache %s already."
            % (cc_path, cc_link_path)
        )

        return True

    return _injectCcache(
        the_compiler=the_compiler,
        cc_path=cc_path,
        env=env,
        python_prefix=python_prefix,
        assume_yes_for_downloads=assume_yes_for_downloads,
    )
예제 #7
0
def _detectWindowsSDK(env):
    # Check if there is a WindowsSDK installed.
    if env.msvc_mode or env.clangcl_mode:
        if "WindowsSDKVersion" not in env:
            if "WindowsSDKVersion" in os.environ:
                windows_sdk_version = os.environ["WindowsSDKVersion"].rstrip(
                    "\\")
            else:
                windows_sdk_version = None
        else:
            windows_sdk_version = env["WindowsSDKVersion"]

        if not windows_sdk_version:
            scons_logger.sysexit(
                "Error, the Windows SDK must be installed in Visual Studio.")

        scons_details_logger.info("Using Windows SDK '%s'." %
                                  windows_sdk_version)

        env.windows_sdk_version = tuple(
            int(x) for x in windows_sdk_version.split("."))
예제 #8
0
def addClangClPathFromMSVC(env, target_arch):
    cl_exe = getExecutablePath("cl", env=env)

    if cl_exe is None:
        scons_logger.sysexit(
            "Error, Visual Studio required for using ClangCL on Windows.")

    clang_dir = cl_exe = os.path.join(cl_exe[:cl_exe.lower().rfind("msvc")],
                                      "Llvm")

    if target_arch == "x86_64":
        clang_dir = os.path.join(clang_dir, "x64", "bin")
    else:
        clang_dir = os.path.join(clang_dir, "bin")

    if os.path.exists(clang_dir):
        scons_details_logger.info(
            "Adding MSVC directory %r for Clang to PATH." % clang_dir)

        addToPATH(env, clang_dir, prefix=True)
    else:
        scons_details_logger.info("No Clang component for MSVC found." %
                                  clang_dir)
예제 #9
0
def _enableC11Settings(env):
    """Decide if C11 mode can be used and enable the C compile flags for it.

    Args:
        env - scons environment with compiler information

    Returns:
        bool - c11_mode flag
    """

    if env.clangcl_mode:
        c11_mode = True
    elif env.msvc_mode:
        # TODO: Make this experimental mode the default.
        c11_mode = (env.windows_sdk_version >= (10, 0, 19041, 0)
                    and "msvc_c11" in env.experimental_flags)
    elif env.clang_mode:
        c11_mode = True
    elif env.gcc_mode and env.gcc_version >= (5, ):
        c11_mode = True
    else:
        c11_mode = False

    if c11_mode:
        if env.gcc_mode:
            env.Append(CCFLAGS=["-std=c11"])
        elif env.msvc_mode:
            env.Append(CCFLAGS=["/std:c11"])

    if env.msvc_mode and c11_mode:
        # Windows SDK shows this even in non-debug mode in C11 mode.
        env.Append(CCFLAGS=["/wd5105"])

    scons_details_logger.info("Using C11 mode: %s" % c11_mode)

    env.c11_mode = c11_mode
예제 #10
0
파일: SconsUtils.py 프로젝트: psydox/Nuitka
def addClangClPathFromMSVC(env):
    cl_exe = getExecutablePath("cl", env=env)

    if cl_exe is None:
        scons_logger.sysexit(
            "Error, Visual Studio required for using ClangCL on Windows.")

    clang_dir = os.path.join(cl_exe[:cl_exe.lower().rfind("msvc")], "Llvm")

    if (getCompilerArch(mingw_mode=False,
                        msvc_mode=True,
                        the_cc_name="cl.exe",
                        compiler_path=cl_exe) == "pei-x86-64"):
        clang_dir = os.path.join(clang_dir, "x64", "bin")
    else:
        clang_dir = os.path.join(clang_dir, "bin")

    if not os.path.exists(clang_dir):
        scons_details_logger.sysexit(
            "Visual Studio has no Clang component found at '%s'." % clang_dir)

    scons_details_logger.info(
        "Adding Visual Studio directory '%s' for Clang to PATH." % clang_dir)

    addToPATH(env, clang_dir, prefix=True)

    clangcl_path = getExecutablePath("clang-cl", env=env)

    if clangcl_path is None:
        scons_details_logger.sysexit(
            "Visual Studio has no Clang component found at '%s'." % clang_dir)

    env["CC"] = "clang-cl"
    env["LINK"] = "lld-link"

    env["CCVERSION"] = None
예제 #11
0
def addConstantBlobFile(env, resource_mode, source_dir, c11_mode, mingw_mode,
                        target_arch):

    constants_bin_filename = getConstantBlobFilename(source_dir)

    scons_details_logger.info("Using resource mode: %r." % resource_mode)

    if resource_mode == "win_resource":
        # On Windows constants can be accessed as a resource by Nuitka runtime afterwards.
        env.Append(CPPDEFINES=["_NUITKA_CONSTANTS_FROM_RESOURCE"])
    elif resource_mode == "incbin":
        env.Append(CPPDEFINES=["_NUITKA_CONSTANTS_FROM_INCBIN"])

        constants_generated_filename = os.path.join(source_dir,
                                                    "__constants_data.c")

        with open(constants_generated_filename, "w") as output:
            output.write("""
#define INCBIN_PREFIX
#define INCBIN_STYLE INCBIN_STYLE_SNAKE
#define INCBIN_LOCAL

#include "nuitka/incbin.h"

INCBIN(constant_bin, "__constants.bin");

unsigned char const *getConstantsBlobData() {
    return constant_bin_data;
}
""")

    elif resource_mode == "linker":
        # On Windows constants are accesses as a resource by Nuitka afterwards.
        env.Append(CPPDEFINES=["_NUITKA_CONSTANTS_FROM_LINKER"])

        env.Append(LINKFLAGS=[
            "-Wl,-b",
            "-Wl,binary",
            "-Wl,%s" % constants_bin_filename,
            "-Wl,-b",
            "-Wl,%s" %
            getLinkerArch(target_arch=target_arch, mingw_mode=mingw_mode),
            "-Wl,-defsym",
            "-Wl,%sconstant_bin=_binary_%s___constants_bin_start" % (
                "_" if mingw_mode else "",
                "".join(re.sub("[^a-zA-Z0-9_]", "_", c) for c in source_dir),
            ),
        ])
    elif resource_mode == "code":
        constants_generated_filename = os.path.join(source_dir,
                                                    "__constants_data.c")

        def writeConstantsDataSource():
            with open(constants_generated_filename, "w") as output:
                if not c11_mode:
                    output.write('extern "C" ')

                output.write("const unsigned char constant_bin[] =\n{\n")

                with open(constants_bin_filename, "rb") as f:
                    content = f.read()
                for count, stream_byte in enumerate(content):
                    if count % 16 == 0:
                        if count > 0:
                            output.write("\n")

                        output.write("   ")

                    if str is bytes:
                        stream_byte = ord(stream_byte)

                    output.write(" 0x%02x," % stream_byte)

                output.write("\n};\n")

        writeConstantsDataSource()
    else:
        scons_logger.sysexit("Error, illegal resource mode %r specified" %
                             resource_mode)
예제 #12
0
def _injectCcache(the_compiler, cc_path, env, python_prefix, 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 and os.name == "nt":
            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,
                specifity=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,
            )

    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 getExecutablePath(os.path.basename(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
        )
    else:
        if isWin32Windows():
            scons_logger.warning(
                "Didn't find ccache for C level caching, follow Nuitka user manual description."
            )
예제 #13
0
def addConstantBlobFile(env, resource_desc, source_dir, target_arch):
    resource_mode, reason = resource_desc

    constants_bin_filename = getConstantBlobFilename(source_dir)

    scons_details_logger.info("Using resource mode: '%s' (%s)." %
                              (resource_mode, reason))

    if resource_mode == "win_resource":
        # On Windows constants can be accessed as a resource by Nuitka runtime afterwards.
        env.Append(CPPDEFINES=["_NUITKA_CONSTANTS_FROM_RESOURCE"])
    elif resource_mode == "incbin":
        env.Append(CPPDEFINES=["_NUITKA_CONSTANTS_FROM_INCBIN"])

        constants_generated_filename = os.path.join(source_dir,
                                                    "__constants_data.c")

        putTextFileContents(
            constants_generated_filename,
            contents=r"""
#define INCBIN_PREFIX
#define INCBIN_STYLE INCBIN_STYLE_SNAKE
#define INCBIN_LOCAL
#ifdef _NUITKA_EXPERIMENTAL_WRITEABLE_CONSTANTS
#define INCBIN_OUTPUT_SECTION ".data"
#endif

#include "nuitka/incbin.h"

INCBIN(constant_bin, "%(constants_bin_filename)s");

unsigned char const *getConstantsBlobData(void) {
    return constant_bin_data;
}
""" % {"constants_bin_filename":
        os.path.join(source_dir, "__constants.bin")},
        )

    elif resource_mode == "linker":
        # Indicate "linker" resource mode.
        env.Append(CPPDEFINES=["_NUITKA_CONSTANTS_FROM_LINKER"])

        env.Append(LINKFLAGS=[
            "-Wl,-b",
            "-Wl,binary",
            "-Wl,%s" % constants_bin_filename,
            "-Wl,-b",
            "-Wl,%s" %
            getLinkerArch(target_arch=target_arch, mingw_mode=env.mingw_mode),
            "-Wl,-defsym",
            "-Wl,%sconstant_bin_data=_binary_%s___constants_bin_start" % (
                "_" if env.mingw_mode else "",
                "".join(re.sub("[^a-zA-Z0-9_]", "_", c) for c in source_dir),
            ),
        ])
    elif resource_mode == "code":
        # Indicate "code" resource mode.
        env.Append(CPPDEFINES=["_NUITKA_CONSTANTS_FROM_CODE"])

        constants_generated_filename = os.path.join(source_dir,
                                                    "__constants_data.c")

        def writeConstantsDataSource():
            with openTextFile(constants_generated_filename, "w") as output:
                if not env.c11_mode:
                    output.write('extern "C" {')

                output.write("""
// Constant data for the program.
#if !defined(_NUITKA_EXPERIMENTAL_WRITEABLE_CONSTANTS)
const
#endif
unsigned char constant_bin_data[] =\n{\n
""")

                with open(constants_bin_filename, "rb") as f:
                    content = f.read()
                for count, stream_byte in enumerate(content):
                    if count % 16 == 0:
                        if count > 0:
                            output.write("\n")

                        output.write("   ")

                    if str is bytes:
                        stream_byte = ord(stream_byte)

                    output.write(" 0x%02x," % stream_byte)

                output.write("\n};\n")

                if not env.c11_mode:
                    output.write("}")

        writeConstantsDataSource()
    else:
        scons_logger.sysexit("Error, illegal resource mode %r specified" %
                             resource_mode)
예제 #14
0
def checkWindowsCompilerFound(env, target_arch, clang_mode, msvc_version,
                              assume_yes_for_downloads):
    """Remove compiler of wrong arch or too old gcc and replace with downloaded winlibs gcc."""

    if os.name == "nt":
        # On Windows, in case MSVC was not found and not previously forced, use the
        # winlibs MinGW64 as a download, and use it as a fallback.
        compiler_path = getExecutablePath(env["CC"], env=env)

        scons_details_logger.info("Checking usability of %r from %r" %
                                  (compiler_path, env["CC"]))

        # Drop wrong arch compiler, most often found by scans. There might be wrong gcc or cl on the PATH.
        if compiler_path is not None:
            the_cc_name = os.path.basename(compiler_path)

            decision, linker_arch, compiler_arch = decideArchMismatch(
                target_arch=target_arch,
                mingw_mode=isGccName(the_cc_name),
                msvc_mode=not isGccName(the_cc_name),
                the_cc_name=the_cc_name,
                compiler_path=compiler_path,
            )

            if decision:
                scons_logger.info(
                    "Mismatch between Python binary (%r -> %r) and C compiler (%r -> %r) arches, that compiler is ignored!"
                    % (
                        os.environ["NUITKA_PYTHON_EXE_PATH"],
                        linker_arch,
                        compiler_path,
                        compiler_arch,
                    ))

                # This will trigger using it to use our own gcc in branch below.
                compiler_path = None
                env["CC"] = None

        if compiler_path is not None and msvc_version is not None:
            if msvc_version == "latest":
                scons_logger.info("MSVC version resolved to %s." %
                                  getMsvcVersionString(env))
            # Requested a specific MSVC version, check if that worked.
            elif msvc_version != getMsvcVersionString(env):
                scons_logger.info(
                    "Failed to find requested MSVC version (%r != %r)." %
                    (msvc_version, getMsvcVersionString(env)))

                # This will trigger error exit in branch below.
                compiler_path = None
                env["CC"] = None

        if compiler_path is not None:
            the_cc_name = os.path.basename(compiler_path)

            if isGccName(the_cc_name):
                gcc_version = myDetectVersion(env, compiler_path)

                min_version = (11, 2)
                if gcc_version is not None and (gcc_version < min_version
                                                or "force-winlibs-gcc"
                                                in env.experimental_flags):
                    scons_logger.info(
                        "Too old gcc %r (%r < %r) ignored!" %
                        (compiler_path, gcc_version, min_version))

                    # This also will trigger using it to use our own gcc in branch below.
                    compiler_path = None
                    env["CC"] = None

        if compiler_path is None and msvc_version is None:
            scons_details_logger.info(
                "No usable C compiler, attempt fallback to winlibs gcc.")

            # This will download "gcc.exe" (and "clang.exe") when all others have been
            # rejected and MSVC is not enforced.
            compiler_path = getCachedDownloadedMinGW64(
                target_arch=target_arch,
                assume_yes_for_downloads=assume_yes_for_downloads,
            )
            addToPATH(env, os.path.dirname(compiler_path), prefix=True)

            env = createEnvironment(
                mingw_mode=True,
                msvc_version=None,
                target_arch=target_arch,
                experimental=env.experimental_flags,
            )

            if clang_mode:
                env["CC"] = os.path.join(os.path.dirname(compiler_path),
                                         "clang.exe")

        if env["CC"] is None:
            raiseNoCompilerFoundErrorExit()

    return env
예제 #15
0
def _enableLtoSettings(
    env,
    lto_mode,
    pgo_mode,
    job_count,
):
    # This is driven by branches on purpose and pylint: disable=too-many-branches,too-many-statements

    orig_lto_mode = lto_mode

    if lto_mode == "no":
        lto_mode = False
        reason = "disabled"
    elif lto_mode == "yes":
        lto_mode = True
        reason = "enabled"
    elif pgo_mode in ("use", "generate"):
        lto_mode = True
        reason = "PGO implies LTO"
    elif env.msvc_mode and getMsvcVersion(env) >= 14:
        lto_mode = True
        reason = "known to be supported"
    elif env.nuitka_python:
        lto_mode = True
        reason = "known to be supported (Nuitka-Python)"
    elif (env.debian_python and env.gcc_mode and not env.clang_mode
          and env.gcc_version >= (6, )):
        lto_mode = True
        reason = "known to be supported (Debian)"
    elif env.gcc_mode and env.the_cc_name == "gnu-cc":
        lto_mode = True
        reason = "known to be supported (CondaCC)"
    elif env.mingw_mode and env.clang_mode:
        lto_mode = False
        reason = "known to not be supported (new MinGW64 Clang)"
    elif env.gcc_mode and env.mingw_mode and env.gcc_version >= (11, 2):
        lto_mode = True
        reason = "known to be supported (new MinGW64)"
    else:
        lto_mode = False
        reason = "not known to be supported"

    if lto_mode and env.gcc_mode and not env.clang_mode and env.gcc_version < (
            4, 6):
        scons_logger.warning("""\
The gcc compiler %s (version %s) doesn't have the sufficient \
version for lto mode (>= 4.6). Disabled.""" % (env["CXX"], env["CXXVERSION"]))

        lto_mode = False
        reason = "gcc 4.6 is doesn't have good enough LTO support"

    if env.gcc_mode and lto_mode:
        env.Append(CCFLAGS=["-flto"])

        if env.clang_mode:
            env.Append(LINKFLAGS=["-flto"])
        else:
            env.Append(CCFLAGS=["-fuse-linker-plugin", "-fno-fat-lto-objects"])
            env.Append(LINKFLAGS=["-fuse-linker-plugin"])

            env.Append(LINKFLAGS=["-flto=%d" % job_count])

            # Need to tell the linker these things are OK.
            env.Append(LINKFLAGS=["-fpartial-inlining", "-freorder-functions"])

    # Tell compiler to use link time optimization for MSVC
    if env.msvc_mode and lto_mode:
        env.Append(CCFLAGS=["/GL"])

        if not env.clangcl_mode:
            env.Append(LINKFLAGS=["/LTCG"])

    if orig_lto_mode == "auto":
        scons_details_logger.info(
            "LTO mode auto was resolved to mode: '%s' (%s)." %
            ("yes" if lto_mode else "no", reason))

    env.lto_mode = lto_mode

    # PGO configuration
    _enablePgoSettings(env, pgo_mode)
예제 #16
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)