diff options
Diffstat (limited to 'platform/javascript/detect.py')
-rw-r--r-- | platform/javascript/detect.py | 284 |
1 files changed, 186 insertions, 98 deletions
diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index 7bf3e1bc1d..4297088c09 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -1,4 +1,16 @@ import os +import sys + +from emscripten_helpers import ( + run_closure_compiler, + create_engine_file, + add_js_libraries, + add_js_pre, + add_js_externs, + get_build_version, +) +from methods import get_compiler_version +from SCons.Util import WhereIs def is_active(): @@ -6,150 +18,226 @@ def is_active(): def get_name(): - return 'JavaScript' + return "JavaScript" def can_build(): - return 'EM_CONFIG' in os.environ or os.path.exists(os.path.expanduser('~/.emscripten')) + return WhereIs("emcc") is not None def get_opts(): from SCons.Variables import BoolVariable + return [ + ("initial_memory", "Initial WASM memory (in MiB)", 16), + BoolVariable("use_assertions", "Use Emscripten runtime assertions", False), + BoolVariable("use_thinlto", "Use ThinLTO", False), + BoolVariable("use_ubsan", "Use Emscripten undefined behavior sanitizer (UBSAN)", False), + BoolVariable("use_asan", "Use Emscripten address sanitizer (ASAN)", False), + BoolVariable("use_lsan", "Use Emscripten leak sanitizer (LSAN)", False), + BoolVariable("use_safe_heap", "Use Emscripten SAFE_HEAP sanitizer", False), # eval() can be a security concern, so it can be disabled. - BoolVariable('javascript_eval', 'Enable JavaScript eval interface', True), + BoolVariable("javascript_eval", "Enable JavaScript eval interface", True), + BoolVariable("threads_enabled", "Enable WebAssembly Threads support (limited browser support)", False), + BoolVariable("gdnative_enabled", "Enable WebAssembly GDNative support (produces bigger binaries)", False), + BoolVariable("use_closure_compiler", "Use closure compiler to minimize JavaScript code", False), ] def get_flags(): return [ - ('tools', False), - ('builtin_pcre2_with_jit', False), + ("tools", False), + ("builtin_pcre2_with_jit", False), # Disabling the mbedtls module reduces file size. # The module has little use due to the limited networking functionality # in this platform. For the available networking methods, the browser # manages TLS. - ('module_mbedtls_enabled', False), + ("module_mbedtls_enabled", False), ] def configure(env): + try: + env["initial_memory"] = int(env["initial_memory"]) + except Exception: + print("Initial memory must be a valid integer") + sys.exit(255) ## Build type - - if env['target'] != 'debug': + if env["target"] == "release": # Use -Os to prioritize optimizing for reduced file size. This is # particularly valuable for the web platform because it directly # decreases download time. # -Os reduces file size by around 5 MiB over -O3. -Oz only saves about # 100 KiB over -Os, which does not justify the negative impact on # run-time performance. - env.Append(CCFLAGS=['-Os']) - env.Append(LINKFLAGS=['-Os']) - if env['target'] == 'release_debug': - env.Append(CPPDEFINES=['DEBUG_ENABLED']) - # Retain function names for backtraces at the cost of file size. - env.Append(LINKFLAGS=['--profiling-funcs']) + env.Append(CCFLAGS=["-Os"]) + env.Append(LINKFLAGS=["-Os"]) + elif env["target"] == "release_debug": + env.Append(CCFLAGS=["-Os"]) + env.Append(LINKFLAGS=["-Os"]) + env.Append(CPPDEFINES=["DEBUG_ENABLED"]) + # Retain function names for backtraces at the cost of file size. + env.Append(LINKFLAGS=["--profiling-funcs"]) + else: # "debug" + env.Append(CPPDEFINES=["DEBUG_ENABLED"]) + env.Append(CCFLAGS=["-O1", "-g"]) + env.Append(LINKFLAGS=["-O1", "-g"]) + env["use_assertions"] = True + + if env["use_assertions"]: + env.Append(LINKFLAGS=["-s", "ASSERTIONS=1"]) + + if env["tools"]: + if not env["threads_enabled"]: + print("Threads must be enabled to build the editor. Please add the 'threads_enabled=yes' option") + sys.exit(255) + if env["initial_memory"] < 64: + print("Editor build requires at least 64MiB of initial memory. Forcing it.") + env["initial_memory"] = 64 + elif env["builtin_icu"]: + env.Append(CCFLAGS=["-frtti"]) else: - env.Append(CPPDEFINES=['DEBUG_ENABLED']) - env.Append(CCFLAGS=['-O1', '-g']) - env.Append(LINKFLAGS=['-O1', '-g']) - env.Append(LINKFLAGS=['-s', 'ASSERTIONS=1']) - - ## Compiler configuration - - env['ENV'] = os.environ - - em_config_file = os.getenv('EM_CONFIG') or os.path.expanduser('~/.emscripten') - if not os.path.exists(em_config_file): - raise RuntimeError("Emscripten configuration file '%s' does not exist" % em_config_file) - with open(em_config_file) as f: - em_config = {} - try: - # Emscripten configuration file is a Python file with simple assignments. - exec(f.read(), em_config) - except StandardError as e: - raise RuntimeError("Emscripten configuration file '%s' is invalid:\n%s" % (em_config_file, e)) - if 'BINARYEN_ROOT' in em_config and os.path.isdir(os.path.join(em_config.get('BINARYEN_ROOT'), 'emscripten')): - # New style, emscripten path as a subfolder of BINARYEN_ROOT - env.PrependENVPath('PATH', os.path.join(em_config.get('BINARYEN_ROOT'), 'emscripten')) - elif 'EMSCRIPTEN_ROOT' in em_config: - # Old style (but can be there as a result from previous activation, so do last) - env.PrependENVPath('PATH', em_config.get('EMSCRIPTEN_ROOT')) - else: - raise RuntimeError("'BINARYEN_ROOT' or 'EMSCRIPTEN_ROOT' missing in Emscripten configuration file '%s'" % em_config_file) - - env['CC'] = 'emcc' - env['CXX'] = 'em++' - env['LINK'] = 'emcc' - - env['AR'] = 'emar' - env['RANLIB'] = 'emranlib' + # Disable exceptions and rtti on non-tools (template) builds + # These flags help keep the file size down. + env.Append(CCFLAGS=["-fno-exceptions", "-fno-rtti"]) + # Don't use dynamic_cast, necessary with no-rtti. + env.Append(CPPDEFINES=["NO_SAFE_CAST"]) + + env.Append(LINKFLAGS=["-s", "INITIAL_MEMORY=%sMB" % env["initial_memory"]]) + + ## Copy env variables. + env["ENV"] = os.environ + + # LTO + if env["use_thinlto"]: + env.Append(CCFLAGS=["-flto=thin"]) + env.Append(LINKFLAGS=["-flto=thin"]) + elif env["use_lto"]: + env.Append(CCFLAGS=["-flto=full"]) + env.Append(LINKFLAGS=["-flto=full"]) + + # Sanitizers + if env["use_ubsan"]: + env.Append(CCFLAGS=["-fsanitize=undefined"]) + env.Append(LINKFLAGS=["-fsanitize=undefined"]) + if env["use_asan"]: + env.Append(CCFLAGS=["-fsanitize=address"]) + env.Append(LINKFLAGS=["-fsanitize=address"]) + if env["use_lsan"]: + env.Append(CCFLAGS=["-fsanitize=leak"]) + env.Append(LINKFLAGS=["-fsanitize=leak"]) + if env["use_safe_heap"]: + env.Append(CCFLAGS=["-s", "SAFE_HEAP=1"]) + env.Append(LINKFLAGS=["-s", "SAFE_HEAP=1"]) + + # Closure compiler + if env["use_closure_compiler"]: + # For emscripten support code. + env.Append(LINKFLAGS=["--closure", "1"]) + # Register builder for our Engine files + jscc = env.Builder(generator=run_closure_compiler, suffix=".cc.js", src_suffix=".js") + env.Append(BUILDERS={"BuildJS": jscc}) + + # Add helper method for adding libraries, externs, pre-js. + env["JS_LIBS"] = [] + env["JS_PRE"] = [] + env["JS_EXTERNS"] = [] + env.AddMethod(add_js_libraries, "AddJSLibraries") + env.AddMethod(add_js_pre, "AddJSPre") + env.AddMethod(add_js_externs, "AddJSExterns") + + # Add method for getting build version string. + env.AddMethod(get_build_version, "GetBuildVersion") + + # Add method that joins/compiles our Engine files. + env.AddMethod(create_engine_file, "CreateEngineFile") + + # Closure compiler extern and support for ecmascript specs (const, let, etc). + env["ENV"]["EMCC_CLOSURE_ARGS"] = "--language_in ECMASCRIPT6" + + env["CC"] = "emcc" + env["CXX"] = "em++" + + env["AR"] = "emar" + env["RANLIB"] = "emranlib" # 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)}' + 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'] = '' + 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'] = '.bc' - env['LIBPREFIXES'] = ['$LIBPREFIX'] - env['LIBSUFFIXES'] = ['$LIBSUFFIX'] - - ## Compile flags - - env.Prepend(CPPPATH=['#platform/javascript']) - env.Append(CPPDEFINES=['JAVASCRIPT_ENABLED', 'UNIX_ENABLED']) - - # No multi-threading (SharedArrayBuffer) available yet, - # once feasible also consider memory buffer size issues. - env.Append(CPPDEFINES=['NO_THREADS']) - - # Disable exceptions and rtti on non-tools (template) builds - if not env['tools']: - # These flags help keep the file size down. - env.Append(CCFLAGS=['-fno-exceptions', '-fno-rtti']) - # Don't use dynamic_cast, necessary with no-rtti. - env.Append(CPPDEFINES=['NO_SAFE_CAST']) - - if env['javascript_eval']: - env.Append(CPPDEFINES=['JAVASCRIPT_EVAL_ENABLED']) - - ## Link flags + env["PROGSUFFIX"] = "" + env["LIBPREFIX"] = "lib" + env["LIBSUFFIX"] = ".bc" + env["LIBPREFIXES"] = ["$LIBPREFIX"] + env["LIBSUFFIXES"] = ["$LIBSUFFIX"] + + env.Prepend(CPPPATH=["#platform/javascript"]) + env.Append(CPPDEFINES=["JAVASCRIPT_ENABLED", "UNIX_ENABLED"]) + + if env["javascript_eval"]: + env.Append(CPPDEFINES=["JAVASCRIPT_EVAL_ENABLED"]) + + if env["threads_enabled"] and env["gdnative_enabled"]: + print("Threads and GDNative support can't be both enabled due to WebAssembly limitations") + sys.exit(255) + + # Thread support (via SharedArrayBuffer). + if env["threads_enabled"]: + env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"]) + env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"]) + env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"]) + env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"]) + env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"]) + env.extra_suffix = ".threads" + env.extra_suffix + else: + env.Append(CPPDEFINES=["NO_THREADS"]) - # We use IDBFS in javascript_main.cpp. Since Emscripten 1.39.1 it needs to - # be linked explicitly. - env.Append(LIBS=['idbfs.js']) + if env["gdnative_enabled"]: + major, minor, patch = get_compiler_version(env) + if major < 2 or (major == 2 and minor == 0 and patch < 10): + print("GDNative support requires emscripten >= 2.0.10, detected: %s.%s.%s" % (major, minor, patch)) + sys.exit(255) + env.Append(CCFLAGS=["-s", "RELOCATABLE=1"]) + env.Append(LINKFLAGS=["-s", "RELOCATABLE=1"]) + env.extra_suffix = ".gdnative" + env.extra_suffix - env.Append(LINKFLAGS=['-s', 'BINARYEN=1']) + # Reduce code size by generating less support code (e.g. skip NodeJS support). + env.Append(LINKFLAGS=["-s", "ENVIRONMENT=web,worker"]) - # This needs to be defined for Emscripten using 'fastcomp' (default pre-1.39.0) - # and undefined if using 'upstream'. And to make things simple, earlier - # Emscripten versions didn't include 'fastcomp' in their path, so we check - # against the presence of 'upstream' to conditionally add the flag. - if not "upstream" in em_config['EMSCRIPTEN_ROOT']: - env.Append(LINKFLAGS=['-s', 'BINARYEN_TRAP_MODE=\'clamp\'']) + # Wrap the JavaScript support code around a closure named Godot. + env.Append(LINKFLAGS=["-s", "MODULARIZE=1", "-s", "EXPORT_NAME='Godot'"]) # Allow increasing memory buffer size during runtime. This is efficient # when using WebAssembly (in comparison to asm.js) and works well for # us since we don't know requirements at compile-time. - env.Append(LINKFLAGS=['-s', 'ALLOW_MEMORY_GROWTH=1']) + env.Append(LINKFLAGS=["-s", "ALLOW_MEMORY_GROWTH=1"]) # This setting just makes WebGL 2 APIs available, it does NOT disable WebGL 1. - env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1']) + env.Append(LINKFLAGS=["-s", "USE_WEBGL2=1"]) + + # Do not call main immediately when the support code is ready. + env.Append(LINKFLAGS=["-s", "INVOKE_RUN=0"]) + + # Allow use to take control of swapping WebGL buffers. + env.Append(LINKFLAGS=["-s", "OFFSCREEN_FRAMEBUFFER=1"]) - env.Append(LINKFLAGS=['-s', 'INVOKE_RUN=0']) + # callMain for manual start. + env.Append(LINKFLAGS=["-s", "EXTRA_EXPORTED_RUNTIME_METHODS=['callMain','cwrap']"]) - # TODO: Reevaluate usage of this setting now that engine.js manages engine runtime. - env.Append(LINKFLAGS=['-s', 'NO_EXIT_RUNTIME=1']) + # Add code that allow exiting runtime. + env.Append(LINKFLAGS=["-s", "EXIT_RUNTIME=1"]) - #adding flag due to issue with emscripten 1.38.41 callMain method https://github.com/emscripten-core/emscripten/blob/incoming/ChangeLog.md#v13841-08072019 - env.Append(LINKFLAGS=['-s', 'EXTRA_EXPORTED_RUNTIME_METHODS=["callMain"]']) + # TODO remove once we have GLES support back (temporary fix undefined symbols due to dead code elimination). + env.Append( + LINKFLAGS=[ + "-s", + "EXPORTED_FUNCTIONS=['_main', '_emscripten_webgl_get_current_context', '_emscripten_webgl_commit_frame', '_emscripten_webgl_create_context']", + ] + ) |