123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- #!/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',
- 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',
- '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',
- )
- 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['platform'] == 'linux' or env['platform'] == 'freebsd':
- if env['use_llvm']:
- env['CXX'] = 'clang++'
- env.Append(CCFLAGS=['-fPIC', '-std=c++14', '-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.'
- )
- 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"]])
- env.Append(CCFLAGS=['-std=c++14'])
- 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=[
- '-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++14', '-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++14', '-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["AS"] = toolchain + "/bin/" + arch_info['tool_path'] + "-as"
- env["LD"] = toolchain + "/bin/" + arch_info['tool_path'] + "-ld"
- env["STRIP"] = toolchain + "/bin/" + arch_info['tool_path'] + "-strip"
- env["RANLIB"] = toolchain + "/bin/" + arch_info['tool_path'] + "-ranlib"
- env.Append(CCFLAGS=['--target=' + arch_info['target'] + env['android_api_level'], '-march=' + arch_info['march'], '-fPIC'])
- 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',
- 'include/gen',
- 'include/core',
- ])
- # 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'], 'api.json')
- if env['generate_bindings'] == 'auto':
- # Check if generated files exist
- should_generate_bindings = not os.path.isfile(os.path.join(os.getcwd(), 'src', 'gen', '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/core', 'cpp')
- add_sources(sources, 'src/gen', 'cpp')
- arch_suffix = env['bits']
- if env['platform'] == 'android':
- arch_suffix = env['android_arch']
- elif env['platform'] == 'ios':
- arch_suffix = env['ios_arch']
- elif env['platform'] == 'osx':
- if env['macos_arch'] != 'universal':
- arch_suffix = env['macos_arch']
- elif 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)
|