123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 |
- #!/usr/bin/env python
- import os
- import sys
- import subprocess
- if sys.version_info < (3,):
- def decode_utf8(x):
- return x
- else:
- import codecs
- def decode_utf8(x):
- return codecs.utf_8_decode(x)[0]
- # Workaround for MinGW. See:
- # http://www.scons.org/wiki/LongCmdLinesOnWin32
- if os.name == "nt":
- import subprocess
- def mySubProcess(cmdline, env):
- # print "SPAWNED : " + cmdline
- startupinfo = subprocess.STARTUPINFO()
- startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
- proc = subprocess.Popen(
- cmdline,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- startupinfo=startupinfo,
- shell=False,
- env=env,
- )
- data, err = proc.communicate()
- rv = proc.wait()
- if rv:
- print("=====")
- print(err.decode("utf-8"))
- print("=====")
- return rv
- def mySpawn(sh, escape, cmd, args, env):
- newargs = " ".join(args[1:])
- cmdline = cmd + " " + newargs
- rv = 0
- if len(cmdline) > 32000 and cmd.endswith("ar"):
- cmdline = cmd + " " + args[1] + " " + args[2] + " "
- for i in range(3, len(args)):
- rv = mySubProcess(cmdline + args[i], env)
- if rv:
- break
- else:
- rv = mySubProcess(cmdline, env)
- return rv
- def add_sources(sources, dir, extension):
- for f in os.listdir(dir):
- if f.endswith("." + extension):
- sources.append(dir + "/" + f)
- # Try to detect the host platform automatically.
- # This is used if no `platform` argument is passed
- if sys.platform.startswith("linux"):
- host_platform = "linux"
- elif sys.platform.startswith("freebsd"):
- host_platform = "freebsd"
- elif sys.platform == "darwin":
- host_platform = "osx"
- elif sys.platform == "win32" or sys.platform == "msys":
- host_platform = "windows"
- else:
- raise ValueError("Could not detect platform automatically, please specify with " "platform=<platform>")
- env = Environment(ENV=os.environ)
- is64 = sys.maxsize > 2 ** 32
- if (
- env["TARGET_ARCH"] == "amd64"
- or env["TARGET_ARCH"] == "emt64"
- or env["TARGET_ARCH"] == "x86_64"
- or env["TARGET_ARCH"] == "arm64-v8a"
- ):
- is64 = True
- opts = Variables([], ARGUMENTS)
- opts.Add(
- EnumVariable(
- "platform",
- "Target platform",
- host_platform,
- allowed_values=("linux", "freebsd", "osx", "windows", "android", "ios", "javascript"),
- ignorecase=2,
- )
- )
- opts.Add(EnumVariable("bits", "Target platform bits", "64" if is64 else "32", ("32", "64")))
- opts.Add(BoolVariable("use_llvm", "Use the LLVM compiler - only effective when targeting Linux or FreeBSD", False))
- opts.Add(BoolVariable("use_mingw", "Use the MinGW compiler instead of MSVC - only effective on Windows", False))
- # Must be the same setting as used for cpp_bindings
- opts.Add(EnumVariable("target", "Compilation target", "debug", allowed_values=("debug", "release"), ignorecase=2))
- opts.Add(
- PathVariable(
- "headers_dir", "Path to the directory containing Godot headers", "godot-headers-temp", PathVariable.PathIsDir
- )
- )
- opts.Add(PathVariable("custom_api_file", "Path to a custom JSON API file", None, PathVariable.PathIsFile))
- opts.Add(
- EnumVariable(
- "generate_bindings",
- "Generate GDNative API bindings",
- "auto",
- allowed_values=["yes", "no", "auto", "true"],
- ignorecase=2,
- )
- )
- opts.Add(EnumVariable("android_arch", "Target Android architecture", "armv7", ["armv7", "arm64v8", "x86", "x86_64"]))
- opts.Add("macos_deployment_target", "macOS deployment target", "default")
- opts.Add("macos_sdk_path", "macOS SDK path", "")
- opts.Add(EnumVariable("macos_arch", "Target macOS architecture", "x86_64", ["x86_64", "arm64"]))
- opts.Add(EnumVariable("ios_arch", "Target iOS architecture", "arm64", ["armv7", "arm64", "x86_64"]))
- opts.Add(BoolVariable("ios_simulator", "Target iOS Simulator", False))
- opts.Add(
- "IPHONEPATH",
- "Path to iPhone toolchain",
- "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain",
- )
- opts.Add(
- "android_api_level",
- "Target Android API level",
- "18" if ARGUMENTS.get("android_arch", "armv7") in ["armv7", "x86"] else "21",
- )
- opts.Add(
- "ANDROID_NDK_ROOT",
- "Path to your Android NDK installation. By default, uses ANDROID_NDK_ROOT from your defined environment variables.",
- os.environ.get("ANDROID_NDK_ROOT", None),
- )
- opts.Add(BoolVariable("generate_template_get_node", "Generate a template version of the Node class's get_node.", True))
- opts.Update(env)
- Help(opts.GenerateHelpText(env))
- # This makes sure to keep the session environment variables on Windows.
- # This way, you can run SCons in a Visual Studio 2017 prompt and it will find
- # all the required tools
- if host_platform == "windows" and env["platform"] != "android":
- if env["bits"] == "64":
- env = Environment(TARGET_ARCH="amd64")
- elif env["bits"] == "32":
- env = Environment(TARGET_ARCH="x86")
- opts.Update(env)
- if env["target"] == "debug":
- env.Append(CPPDEFINES=["DEBUG_ENABLED", "DEBUG_METHODS_ENABLED"])
- if env["platform"] == "linux" or env["platform"] == "freebsd":
- if env["use_llvm"]:
- env["CXX"] = "clang++"
- env.Append(CCFLAGS=["-fPIC", "-std=c++17", "-Wwrite-strings"])
- env.Append(LINKFLAGS=["-Wl,-R,'$$ORIGIN'"])
- if env["target"] == "debug":
- env.Append(CCFLAGS=["-Og", "-g"])
- elif env["target"] == "release":
- env.Append(CCFLAGS=["-O3"])
- if env["bits"] == "64":
- env.Append(CCFLAGS=["-m64"])
- env.Append(LINKFLAGS=["-m64"])
- elif env["bits"] == "32":
- env.Append(CCFLAGS=["-m32"])
- env.Append(LINKFLAGS=["-m32"])
- elif env["platform"] == "osx":
- # Use Clang on macOS by default
- env["CXX"] = "clang++"
- if env["bits"] == "32":
- raise ValueError("Only 64-bit builds are supported for the macOS target.")
- env.Append(CCFLAGS=["-std=c++17", "-arch", env["macos_arch"]])
- if env["macos_deployment_target"] != "default":
- env.Append(CCFLAGS=["-mmacosx-version-min=" + env["macos_deployment_target"]])
- env.Append(LINKFLAGS=["-mmacosx-version-min=" + env["macos_deployment_target"]])
- if env["macos_sdk_path"]:
- env.Append(CCFLAGS=["-isysroot", env["macos_sdk_path"]])
- env.Append(LINKFLAGS=["-isysroot", env["macos_sdk_path"]])
- env.Append(
- LINKFLAGS=[
- "-arch",
- env["macos_arch"],
- "-framework",
- "Cocoa",
- "-Wl,-undefined,dynamic_lookup",
- ]
- )
- if env["target"] == "debug":
- env.Append(CCFLAGS=["-Og", "-g"])
- elif env["target"] == "release":
- env.Append(CCFLAGS=["-O3"])
- elif env["platform"] == "ios":
- if env["ios_simulator"]:
- sdk_name = "iphonesimulator"
- env.Append(CCFLAGS=["-mios-simulator-version-min=10.0"])
- env["LIBSUFFIX"] = ".simulator" + env["LIBSUFFIX"]
- else:
- sdk_name = "iphoneos"
- env.Append(CCFLAGS=["-miphoneos-version-min=10.0"])
- try:
- sdk_path = decode_utf8(subprocess.check_output(["xcrun", "--sdk", sdk_name, "--show-sdk-path"]).strip())
- except (subprocess.CalledProcessError, OSError):
- raise ValueError("Failed to find SDK path while running xcrun --sdk {} --show-sdk-path.".format(sdk_name))
- compiler_path = env["IPHONEPATH"] + "/usr/bin/"
- env["ENV"]["PATH"] = env["IPHONEPATH"] + "/Developer/usr/bin/:" + env["ENV"]["PATH"]
- env["CC"] = compiler_path + "clang"
- env["CXX"] = compiler_path + "clang++"
- env["AR"] = compiler_path + "ar"
- env["RANLIB"] = compiler_path + "ranlib"
- env.Append(CCFLAGS=["-std=c++17", "-arch", env["ios_arch"], "-isysroot", sdk_path])
- env.Append(
- LINKFLAGS=[
- "-arch",
- env["ios_arch"],
- "-framework",
- "Cocoa",
- "-Wl,-undefined,dynamic_lookup",
- "-isysroot",
- sdk_path,
- "-F" + sdk_path,
- ]
- )
- if env["target"] == "debug":
- env.Append(CCFLAGS=["-Og", "-g"])
- elif env["target"] == "release":
- env.Append(CCFLAGS=["-O3"])
- elif env["platform"] == "windows":
- if host_platform == "windows" and not env["use_mingw"]:
- # MSVC
- env.Append(LINKFLAGS=["/WX"])
- if env["target"] == "debug":
- env.Append(CCFLAGS=["/Z7", "/Od", "/EHsc", "/D_DEBUG", "/MDd"])
- elif env["target"] == "release":
- env.Append(CCFLAGS=["/O2", "/EHsc", "/DNDEBUG", "/MD"])
- elif host_platform == "linux" or host_platform == "freebsd" or host_platform == "osx":
- # Cross-compilation using MinGW
- if env["bits"] == "64":
- env["CXX"] = "x86_64-w64-mingw32-g++"
- env["AR"] = "x86_64-w64-mingw32-ar"
- env["RANLIB"] = "x86_64-w64-mingw32-ranlib"
- env["LINK"] = "x86_64-w64-mingw32-g++"
- elif env["bits"] == "32":
- env["CXX"] = "i686-w64-mingw32-g++"
- env["AR"] = "i686-w64-mingw32-ar"
- env["RANLIB"] = "i686-w64-mingw32-ranlib"
- env["LINK"] = "i686-w64-mingw32-g++"
- elif host_platform == "windows" and env["use_mingw"]:
- # Don't Clone the environment. Because otherwise, SCons will pick up msvc stuff.
- env = Environment(ENV=os.environ, tools=["mingw"])
- opts.Update(env)
- # env = env.Clone(tools=['mingw'])
- env["SPAWN"] = mySpawn
- # Native or cross-compilation using MinGW
- if host_platform == "linux" or host_platform == "freebsd" or host_platform == "osx" or env["use_mingw"]:
- # These options are for a release build even using target=debug
- env.Append(CCFLAGS=["-O3", "-std=c++17", "-Wwrite-strings"])
- env.Append(
- LINKFLAGS=[
- "--static",
- "-Wl,--no-undefined",
- "-static-libgcc",
- "-static-libstdc++",
- ]
- )
- elif env["platform"] == "android":
- if host_platform == "windows":
- # Don't Clone the environment. Because otherwise, SCons will pick up msvc stuff.
- env = Environment(ENV=os.environ, tools=["mingw"])
- opts.Update(env)
- # env = env.Clone(tools=['mingw'])
- env["SPAWN"] = mySpawn
- # Verify NDK root
- if not "ANDROID_NDK_ROOT" in env:
- raise ValueError(
- "To build for Android, ANDROID_NDK_ROOT must be defined. Please set ANDROID_NDK_ROOT to the root folder of your Android NDK installation."
- )
- # Validate API level
- api_level = int(env["android_api_level"])
- if env["android_arch"] in ["x86_64", "arm64v8"] and api_level < 21:
- print("WARN: 64-bit Android architectures require an API level of at least 21; setting android_api_level=21")
- env["android_api_level"] = "21"
- api_level = 21
- # Setup toolchain
- toolchain = env["ANDROID_NDK_ROOT"] + "/toolchains/llvm/prebuilt/"
- if host_platform == "windows":
- toolchain += "windows"
- import platform as pltfm
- if pltfm.machine().endswith("64"):
- toolchain += "-x86_64"
- elif host_platform == "linux":
- toolchain += "linux-x86_64"
- elif host_platform == "osx":
- toolchain += "darwin-x86_64"
- env.PrependENVPath("PATH", toolchain + "/bin") # This does nothing half of the time, but we'll put it here anyways
- # Get architecture info
- arch_info_table = {
- "armv7": {
- "march": "armv7-a",
- "target": "armv7a-linux-androideabi",
- "tool_path": "arm-linux-androideabi",
- "compiler_path": "armv7a-linux-androideabi",
- "ccflags": ["-mfpu=neon"],
- },
- "arm64v8": {
- "march": "armv8-a",
- "target": "aarch64-linux-android",
- "tool_path": "aarch64-linux-android",
- "compiler_path": "aarch64-linux-android",
- "ccflags": [],
- },
- "x86": {
- "march": "i686",
- "target": "i686-linux-android",
- "tool_path": "i686-linux-android",
- "compiler_path": "i686-linux-android",
- "ccflags": ["-mstackrealign"],
- },
- "x86_64": {
- "march": "x86-64",
- "target": "x86_64-linux-android",
- "tool_path": "x86_64-linux-android",
- "compiler_path": "x86_64-linux-android",
- "ccflags": [],
- },
- }
- arch_info = arch_info_table[env["android_arch"]]
- # Setup tools
- env["CC"] = toolchain + "/bin/clang"
- env["CXX"] = toolchain + "/bin/clang++"
- env["AR"] = toolchain + "/bin/" + arch_info["tool_path"] + "-ar"
- env.Append(
- CCFLAGS=["--target=" + arch_info["target"] + env["android_api_level"], "-march=" + arch_info["march"], "-fPIC"]
- ) # , '-fPIE', '-fno-addrsig', '-Oz'])
- env.Append(CCFLAGS=arch_info["ccflags"])
- if env["target"] == "debug":
- env.Append(CCFLAGS=["-Og", "-g"])
- elif env["target"] == "release":
- env.Append(CCFLAGS=["-O3"])
- elif env["platform"] == "javascript":
- env["ENV"] = os.environ
- env["CC"] = "emcc"
- env["CXX"] = "em++"
- env["AR"] = "emar"
- env["RANLIB"] = "emranlib"
- env.Append(CPPFLAGS=["-s", "SIDE_MODULE=1"])
- env.Append(LINKFLAGS=["-s", "SIDE_MODULE=1"])
- env["SHOBJSUFFIX"] = ".bc"
- env["SHLIBSUFFIX"] = ".wasm"
- # Use TempFileMunge since some AR invocations are too long for cmd.exe.
- # Use POSIX-style paths, required with TempFileMunge.
- env["ARCOM_POSIX"] = env["ARCOM"].replace("$TARGET", "$TARGET.posix").replace("$SOURCES", "$SOURCES.posix")
- env["ARCOM"] = "${TEMPFILE(ARCOM_POSIX)}"
- # All intermediate files are just LLVM bitcode.
- env["OBJPREFIX"] = ""
- env["OBJSUFFIX"] = ".bc"
- env["PROGPREFIX"] = ""
- # Program() output consists of multiple files, so specify suffixes manually at builder.
- env["PROGSUFFIX"] = ""
- env["LIBPREFIX"] = "lib"
- env["LIBSUFFIX"] = ".a"
- env["LIBPREFIXES"] = ["$LIBPREFIX"]
- env["LIBSUFFIXES"] = ["$LIBSUFFIX"]
- env.Replace(SHLINKFLAGS="$LINKFLAGS")
- env.Replace(SHLINKFLAGS="$LINKFLAGS")
- if env["target"] == "debug":
- env.Append(CCFLAGS=["-O0", "-g"])
- elif env["target"] == "release":
- env.Append(CCFLAGS=["-O3"])
- env.Append(
- CPPPATH=[
- ".",
- env["headers_dir"],
- "#include",
- "#gen/include",
- ]
- )
- # Generate bindings?
- json_api_file = ""
- if "custom_api_file" in env:
- json_api_file = env["custom_api_file"]
- else:
- json_api_file = os.path.join(os.getcwd(), env["headers_dir"], "extension_api.json")
- if env["generate_bindings"] == "auto":
- # Check if generated files exist
- should_generate_bindings = not os.path.isfile(os.path.join(os.getcwd(), "gen", "src", "classes", "object.cpp"))
- else:
- should_generate_bindings = env["generate_bindings"] in ["yes", "true"]
- if should_generate_bindings:
- # Actually create the bindings here
- import binding_generator
- binding_generator.generate_bindings(json_api_file, env["generate_template_get_node"])
- # Sources to compile
- sources = []
- add_sources(sources, "src", "cpp")
- add_sources(sources, "src/core", "cpp")
- add_sources(sources, "src/variant", "cpp")
- add_sources(sources, "gen/src/variant", "cpp")
- add_sources(sources, "gen/src/classes", "cpp")
- arch_suffix = env["bits"]
- if env["platform"] == "android":
- arch_suffix = env["android_arch"]
- if env["platform"] == "ios":
- arch_suffix = env["ios_arch"]
- if env["platform"] == "javascript":
- arch_suffix = "wasm"
- library = env.StaticLibrary(
- target="bin/" + "libgodot-cpp.{}.{}.{}{}".format(env["platform"], env["target"], arch_suffix, env["LIBSUFFIX"]),
- source=sources,
- )
- Default(library)
|