Example #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)
Example #2
0
def runClCache(args, env):
    # pylint: disable=I0021,import-error,no-name-in-module,redefined-outer-name
    from clcache.caching import runClCache

    # No Python2 compatibility
    if str is bytes:
        scons_logger.sysexit("Error, cannot use Python2 for scons when using MSVC.")

    # The first argument is "<clcache>" and should not be used.
    return runClCache(
        os.environ["CLCACHE_CL"], [arg.strip('"') for arg in args[1:]], env
    )
Example #3
0
def switchFromGccToGpp(env):
    if not env.gcc_mode or env.clang_mode:
        env.gcc_version = None
        return

    env.gcc_version = myDetectVersion(env, env.the_compiler)

    if env.gcc_version is None:
        scons_logger.sysexit("""\
Error, failed to detect gcc version of backend compiler %r.
""" % env.the_compiler)

    if "++" in env.the_cc_name:
        scons_logger.sysexit("""\
Error, compiler %s is apparently a C++ compiler, specify a C compiler instead.
""" % env.the_cc_name)

    # Enforce the minimum version, selecting a potentially existing g++-4.5
    # binary if it's not high enough. This is esp. useful under Debian which
    # allows all compiler to exist next to each other and where g++ might not be
    # good enough, but g++-4.5 would be.
    if env.gcc_version < (4, 4):
        scons_logger.sysexit("""\
The gcc compiler %s (version %s) doesn't have the sufficient \
version (>= 4.4).""" % (env.the_compiler, env.gcc_version))

    # CondaCC or newer.
    if env.mingw_mode and env.gcc_version < (5, 3):
        scons_logger.sysexit("""\
The MinGW64 compiler %s (version %s) doesn't have the sufficient \
version (>= 5.3).""" % (env.the_compiler, env.gcc_version))

    if env.gcc_version < (5, ):
        scons_logger.info(
            "The provided gcc is too old, switching to its g++ instead.")

        # Switch to g++ from gcc then if possible, when C11 mode is false.
        the_gpp_compiler = os.path.join(
            os.path.dirname(env.the_compiler),
            os.path.basename(env.the_compiler).replace("gcc", "g++"),
        )

        if getExecutablePath(the_gpp_compiler, env=env):
            env.the_compiler = the_gpp_compiler
            env.the_cc_name = env.the_cc_name.replace("gcc", "g++")
        else:
            scons_logger.sysexit(
                "Error, your gcc is too old for C11 support, and no related g++ to workaround that is found."
            )
Example #4
0
def createEnvironment(mingw_mode, msvc_version, target_arch, experimental):
    from SCons.Script import Environment  # pylint: disable=I0021,import-error

    args = {}

    if msvc_version == "list":
        import SCons.Tool.MSCommon.vc  # pylint: disable=I0021,import-error

        scons_logger.sysexit(
            "Installed MSVC versions are %s." % ",".join(
                repr(v) for v in SCons.Tool.MSCommon.vc.get_installed_vcs()), )
    # If we are on Windows, and MinGW is not enforced, lets see if we can
    # find "cl.exe", and if we do, disable automatic scan.
    if (os.name == "nt" and not mingw_mode and msvc_version is None
            and msvc_version != "latest"
            and (getExecutablePath("cl", env=None) is not None)):
        args["MSVC_USE_SCRIPT"] = False

    if mingw_mode:
        # Force usage of MinGW64, not using MSVC tools.
        tools = ["mingw"]

        # This code would be running anyway, make it do not thing by monkey patching.
        import SCons.Tool.MSCommon.vc  # pylint: disable=I0021,import-error

        SCons.Tool.MSCommon.vc.msvc_setup_env = lambda *args: None
    else:
        # Everything else should use default, that is MSVC tools, but not MinGW64.
        tools = ["default"]

    env = Environment(
        # We want the outside environment to be passed through.
        ENV=os.environ,
        # Extra tools configuration for scons.
        tools=tools,
        # The shared libraries should not be named "lib...", because CPython
        # requires the filename "module_name.so" to load it.
        SHLIBPREFIX="",
        # Under windows, specify the target architecture is needed for Scons
        # to pick up MSVC.
        TARGET_ARCH=target_arch,
        # The MSVC version might be fixed by the user.
        MSVC_VERSION=msvc_version if msvc_version != "latest" else None,
        **args)

    _enableExperimentalSettings(env, experimental)

    return env
Example #5
0
def raiseNoCompilerFoundErrorExit():
    if os.name == "nt":
        scons_logger.sysexit("""\
Error, cannot locate suitable C compiler. You have the following options:

a) If a suitable Visual Studio version is installed, it will be located
   automatically via registry. But not if you activate the wrong prompt.

b) Using --mingw64 lets Nuitka download MinGW64 for you.

Note: Only MinGW64 will work! MinGW64 does *not* mean 64 bits, just better
Windows compatibility, it is available for 32 and 64 bits. Cygwin based gcc
will not work. MSYS2 based gcc will only work if you know what you are doing.

Note: The clang-cl will only work if Visual Studio already works for you.
""")
    else:
        scons_logger.sysexit("Error, cannot locate suitable C compiler.")
Example #6
0
def _enablePgoSettings(env, pgo_mode):
    if pgo_mode == "no":
        env.progressbar_name = "Backend"
    elif pgo_mode == "python":
        env.progressbar_name = "Python Profile"
        env.Append(CPPDEFINES=["_NUITKA_PGO_PYTHON"])
    elif pgo_mode == "generate":
        env.progressbar_name = "Profile"
        env.Append(CPPDEFINES=["_NUITKA_PGO_GENERATE"])

        if env.gcc_mode:
            env.Append(CCFLAGS=["-fprofile-generate"])
            env.Append(LINKFLAGS=["-fprofile-generate"])
        elif env.msvc_mode:
            env.Append(CCFLAGS=["/GL"])
            env.Append(LINKFLAGS=["/GENPROFILE:EXACT"])
            if not env.clangcl_mode:
                env.Append(LINKFLAGS=["/LTCG"])

        else:
            scons_logger.sysexit(
                "Error, PGO not supported for '%s' compiler." %
                env.the_cc_name)
    elif pgo_mode == "use":
        env.progressbar_name = "Backend"

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

        if env.gcc_mode:
            env.Append(CCFLAGS=["-fprofile-use"])
            env.Append(LINKFLAGS=["-fprofile-use"])
        elif env.msvc_mode:
            env.Append(CCFLAGS=["/GL"])
            env.Append(LINKFLAGS=[
                "/USEPROFILE",
            ])
        else:
            scons_logger.sysexit(
                "Error, PGO not supported for '%s' compiler." %
                env.the_cc_name)
    else:
        assert False, env.pgo_mode

    env.pgo_mode = pgo_mode
Example #7
0
def switchFromGccToGpp(gcc_version, the_compiler, the_cc_name, env):
    if gcc_version is not None and gcc_version < (5,):
        scons_logger.info("The provided gcc is too old, switching to g++ instead.")

        # Switch to g++ from gcc then if possible, when C11 mode is false.
        the_gpp_compiler = os.path.join(
            os.path.dirname(the_compiler),
            os.path.basename(the_compiler).replace("gcc", "g++"),
        )

        if getExecutablePath(the_gpp_compiler, env=env):
            the_compiler = the_gpp_compiler
            the_cc_name = the_cc_name.replace("gcc", "g++")
        else:
            scons_logger.sysexit(
                "Error, your gcc is too old for C11 support, and no related g++ to workaround that is found."
            )

    return the_compiler, the_cc_name
Example #8
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("."))
Example #9
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)
Example #10
0
    def spawnCommand(sh, escape, cmd, args, _env):
        # signature needed towards Scons core, pylint: disable=unused-argument

        # Avoid using ccache on binary constants blob, not useful and not working
        # with old ccache.
        if '"__constants_data.o"' in args or '"__constants_data.os"' in args:
            _env = dict(_env)
            _env["CCACHE_DISABLE"] = "1"

        result, exception = runSpawnMonitored(sh, cmd, args, _env)

        if exception:
            closeSconsProgressBar()

            raise exception

        # Segmentation fault should give a clear error.
        if result == -11:
            scons_logger.sysexit(
                "Error, the C compiler '%s' crashed with segfault. Consider upgrading it or using --clang option."
                % env.the_compiler)

        return result
Example #11
0
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
Example #12
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)
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)
Example #14
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)