godot-cpp/SConstruct

526 lines
18 KiB
Python
Raw Normal View History

#!/usr/bin/env python
import os
import sys
2019-11-26 19:26:06 +00:00
import subprocess
from binding_generator import scons_generate_bindings, scons_emit_files
2019-11-26 19:26:06 +00:00
if sys.version_info < (3,):
2019-11-26 19:26:06 +00:00
def decode_utf8(x):
return x
2019-11-26 19:26:06 +00:00
else:
import codecs
2019-11-26 19:26:06 +00:00
def decode_utf8(x):
return codecs.utf_8_decode(x)[0]
2019-08-21 08:03:49 +00:00
# Workaround for MinGW. See:
# http://www.scons.org/wiki/LongCmdLinesOnWin32
if os.name == "nt":
2019-08-21 08:03:49 +00:00
import subprocess
2019-12-15 22:13:19 +00:00
def mySubProcess(cmdline, env):
# print "SPAWNED : " + cmdline
2019-08-21 08:03:49 +00:00
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,
)
2019-08-21 08:03:49 +00:00
data, err = proc.communicate()
rv = proc.wait()
if rv:
print("=====")
print(err.decode("utf-8"))
print("=====")
return rv
2019-12-15 22:13:19 +00:00
2019-08-21 08:03:49 +00:00
def mySpawn(sh, escape, cmd, args, env):
2019-12-15 22:13:19 +00:00
newargs = " ".join(args[1:])
2019-08-21 08:03:49 +00:00
cmdline = cmd + " " + newargs
2019-12-15 22:13:19 +00:00
rv = 0
if len(cmdline) > 32000 and cmd.endswith("ar"):
2019-08-21 08:03:49 +00:00
cmdline = cmd + " " + args[1] + " " + args[2] + " "
for i in range(3, len(args)):
rv = mySubProcess(cmdline + args[i], env)
if rv:
2019-12-15 22:13:19 +00:00
break
else:
rv = mySubProcess(cmdline, env)
2019-12-15 22:13:19 +00:00
2019-08-21 08:03:49 +00:00
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)
# Default num_jobs to local cpu count if not user specified.
# SCons has a peculiarity where user-specified options won't be overridden
# by SetOption, so we can rely on this to know if we should use our default.
initial_num_jobs = env.GetOption("num_jobs")
altered_num_jobs = initial_num_jobs + 1
env.SetOption("num_jobs", altered_num_jobs)
# os.cpu_count() requires Python 3.4+.
if hasattr(os, "cpu_count") and env.GetOption("num_jobs") == altered_num_jobs:
cpu_count = os.cpu_count()
if cpu_count is None:
print("Couldn't auto-detect CPU count to configure build parallelism. Specify it with the -j argument.")
else:
safer_cpu_count = cpu_count if cpu_count <= 4 else cpu_count - 1
print(
"Auto-detected %d CPU cores available for build parallelism. Using %d cores by default. You can override it with the -j argument."
% (cpu_count, safer_cpu_count)
)
env.SetOption("num_jobs", safer_cpu_count)
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",
PathVariable.PathIsDir,
)
)
opts.Add(PathVariable("custom_api_file", "Path to a custom JSON API file", None, PathVariable.PathIsFile))
opts.Add(BoolVariable("generate_bindings", "Force GDNative API bindings generation.", False))
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", "universal", ["universal", "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",
)
2019-11-26 19:26:06 +00:00
opts.Add(
"android_api_level",
"Target Android API level",
"18" if ARGUMENTS.get("android_arch", "armv7") in ["armv7", "x86"] else "21",
2019-11-26 19:26:06 +00:00
)
2019-08-21 08:03:49 +00:00
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),
2019-08-21 08:03:49 +00:00
)
opts.Add(
BoolVariable(
"generate_template_get_node",
"Generate a template version of the Node class's get_node.",
True,
)
2019-08-21 08:03:49 +00:00
)
opts.Add(BoolVariable("build_library", "Build the godot-cpp library.", True))
opts.Update(env)
Help(opts.GenerateHelpText(env))
# Detect and print a warning listing unknown SCons variables to ease troubleshooting.
unknown = opts.UnknownVariables()
if unknown:
print("WARNING: Unknown SCons variables were passed and will be ignored:")
for item in unknown.items():
print(" " + item[0] + "=" + item[1])
# 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)
# Require C++14
if host_platform == "windows" and env["platform"] == "windows" and not env["use_mingw"]:
# MSVC
env.Append(CCFLAGS=["/std:c++14"])
else:
env.Append(CCFLAGS=["-std=c++14"])
if env["platform"] == "linux" or env["platform"] == "freebsd":
if env["use_llvm"]:
env["CXX"] = "clang++"
2018-02-25 16:03:55 +00:00
env.Append(CCFLAGS=["-fPIC", "-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.")
2021-10-01 17:24:48 +00:00
if env["macos_arch"] == "universal":
env.Append(LINKFLAGS=["-arch", "x86_64", "-arch", "arm64"])
env.Append(CCFLAGS=["-arch", "x86_64", "-arch", "arm64"])
else:
env.Append(LINKFLAGS=["-arch", env["macos_arch"]])
env.Append(CCFLAGS=["-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=["-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"])
2019-11-26 19:26:06 +00:00
else:
sdk_name = "iphoneos"
env.Append(CCFLAGS=["-miphoneos-version-min=10.0"])
2019-11-26 19:26:06 +00:00
try:
sdk_path = decode_utf8(subprocess.check_output(["xcrun", "--sdk", sdk_name, "--show-sdk-path"]).strip())
2019-11-26 19:26:06 +00:00
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["SHLIBSUFFIX"] = ".dylib"
env.Append(CCFLAGS=["-arch", env["ios_arch"], "-isysroot", sdk_path])
env.Append(
LINKFLAGS=[
"-arch",
env["ios_arch"],
"-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)
# Still need to use C++14.
env.Append(CCFLAGS=["-std=c++14"])
# Don't want lib prefixes
env["IMPLIBPREFIX"] = ""
env["SHLIBPREFIX"] = ""
2019-12-15 22:13:19 +00:00
env["SPAWN"] = mySpawn
env.Replace(ARFLAGS=["q"])
# 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", "-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)
# Long line hack. Use custom spawn, quick AR append (to avoid files with the same names to override each other).
2019-08-21 08:03:49 +00:00
env["SPAWN"] = mySpawn
env.Replace(ARFLAGS=["q"])
2019-12-15 22:13:19 +00:00
2019-08-21 08:03:49 +00:00
# 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."
)
2019-12-15 22:13:19 +00:00
2019-08-21 08:03:49 +00:00
# Validate API level
api_level = int(env["android_api_level"])
if env["android_arch"] in ["x86_64", "arm64v8"] and api_level < 21:
2019-08-21 08:03:49 +00:00
print("WARN: 64-bit Android architectures require an API level of at least 21; setting android_api_level=21")
env["android_api_level"] = "21"
2019-08-21 08:03:49 +00:00
api_level = 21
2019-12-15 22:13:19 +00:00
2019-08-21 08:03:49 +00:00
# Setup toolchain
toolchain = env["ANDROID_NDK_ROOT"] + "/toolchains/llvm/prebuilt/"
2019-08-21 08:03:49 +00:00
if host_platform == "windows":
toolchain += "windows"
import platform as pltfm
2019-08-21 08:03:49 +00:00
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
2019-08-21 08:03:49 +00:00
# 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": [],
},
2019-08-21 08:03:49 +00:00
}
arch_info = arch_info_table[env["android_arch"]]
2019-08-21 08:03:49 +00:00
# Setup tools
env["CC"] = toolchain + "/bin/clang"
env["CXX"] = toolchain + "/bin/clang++"
env["AR"] = toolchain + "/bin/llvm-ar"
env["AS"] = toolchain + "/bin/llvm-as"
env["LD"] = toolchain + "/bin/llvm-ld"
env["STRIP"] = toolchain + "/bin/llvm-strip"
env["RANLIB"] = toolchain + "/bin/llvm-ranlib"
env["SHLIBSUFFIX"] = ".so"
env.Append(
CCFLAGS=[
"--target=" + arch_info["target"] + env["android_api_level"],
"-march=" + arch_info["march"],
"-fPIC",
]
)
env.Append(CCFLAGS=arch_info["ccflags"])
env.Append(LINKFLAGS=["--target=" + arch_info["target"] + env["android_api_level"], "-march=" + arch_info["march"]])
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"])
# Cache
scons_cache_path = os.environ.get("SCONS_CACHE")
if scons_cache_path is not None:
CacheDir(scons_cache_path)
Decider("MD5")
Nativescript 1.1 implemented instance binding data usage This commit changes the way C++ wrapper classes work. Previously, wrapper classes were merely wrapper *interfaces*. They used the `this` pointer to store the actual foreign Godot Object. With the NativeScript 1.1 extension it is now possible to have low-overhead language binding data attached to Objects. The C++ bindings use that feature to implement *proper* wrappers and enable regular C++ inheritance usage that way. Some things might still be buggy and untested, but the C++ SimpleDemo works with those changes. new and free change, custom free will crash engine, be wary fix exporting of non-object types fix free() crash with custom resources added type tags and safe object casting fix global type registration order fix cast_to changed build system to be more self contained updated .gitignore use typeid() for type tags now fix indentation in bindings generator remove accidentally added files fix gitignore Fixed up registering tool and updated godot_headers Fix crash when calling String::split/split_floats Was casting to the wrong object type. Also adds parse_ints function to String with the same logic Better warning/error macros Change gitignore so we get our gen folders New documentation based on nativescript 1.1 Fixed GODOT_SUBCLASS macro Preventing crash when function returned null ptr Adds needed include <typeinfo> Solves this issue #168 due to not having the include of typeinfo Fix compile error of 'WARN_PRINT' and 'ERR_PRINT'. cannot pass non-trivial object of type 'godot::String' to variadic function; expected type from format string was 'char *' [-Wnon-pod-varargs] update vector3::distance_to Remove godot_api.json as its now in the godot_headers submodule (api.json)
2018-02-11 14:50:01 +00:00
# Generate bindings
env.Append(BUILDERS={"GenerateBindings": Builder(action=scons_generate_bindings, emitter=scons_emit_files)})
json_api_file = ""
if "custom_api_file" in env:
json_api_file = env["custom_api_file"]
2018-05-16 00:05:41 +00:00
else:
json_api_file = os.path.join(os.getcwd(), env["headers_dir"], "api.json")
2017-03-06 07:49:24 +00:00
bindings = env.GenerateBindings(
env.Dir("."), [json_api_file, "binding_generator.py"]
)
# Forces bindings regeneration.
if env["generate_bindings"]:
AlwaysBuild(bindings)
NoCache(bindings)
# Includes
env.Append(CPPPATH=[[env.Dir(d) for d in [".", env["headers_dir"], "include", "include/gen", "include/core"]]])
2017-03-06 07:49:24 +00:00
# Sources to compile
sources = []
add_sources(sources, "src/core", "cpp")
sources.extend(f for f in bindings if str(f).endswith(".cpp"))
arch_suffix = env["bits"]
if env["platform"] == "android":
arch_suffix = env["android_arch"]
elif env["platform"] == "ios":
arch_suffix = env["ios_arch"]
if env["ios_simulator"]:
arch_suffix += ".simulator"
elif env["platform"] == "osx":
if env["macos_arch"] != "universal":
arch_suffix = env["macos_arch"]
elif env["platform"] == "javascript":
arch_suffix = "wasm"
# Expose it to projects that import this env.
env["arch_suffix"] = arch_suffix
2019-11-26 19:26:06 +00:00
library = None
env["OBJSUFFIX"] = ".{}.{}.{}{}".format(env["platform"], env["target"], arch_suffix, env["OBJSUFFIX"])
library_name = "libgodot-cpp.{}.{}.{}{}".format(env["platform"], env["target"], arch_suffix, env["LIBSUFFIX"])
if env["build_library"]:
library = env.StaticLibrary(target=env.File("bin/%s" % library_name), source=sources)
Default(library)
env.Append(LIBPATH=[env.Dir("bin")])
env.Append(LIBS=library_name)
Return("env")