From 7c453973afa2c84b6a3108a75ab5ce33e032c7e0 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Sun, 3 May 2026 21:56:32 +0200 Subject: [PATCH] icu: bump Emscripten transport to 5.0.6, drop PatchEmscripten MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - eng/Versions.props: 3.1.56 → 5.0.6 property names, version 11.0.0-preview.4.26253.2 - eng/icu.proj: package refs 3.1.56 → 5.0.6, remove PatchEmscripten target - eng/patches/emcc.py: removed (no longer needed at Emscripten 5.0.6) - NuGet.config: add local-emsdk source for local build --- NuGet.config | 1 + eng/Versions.props | 6 +- eng/icu.proj | 19 +- eng/patches/emcc.py | 4423 --------------------------- icu/icu4c/source/data/curr/root.txt | 317 +- 5 files changed, 165 insertions(+), 4601 deletions(-) delete mode 100644 eng/patches/emcc.py diff --git a/NuGet.config b/NuGet.config index d7af36761e9..fbbe286b3f2 100644 --- a/NuGet.config +++ b/NuGet.config @@ -5,6 +5,7 @@ + diff --git a/eng/Versions.props b/eng/Versions.props index 165c6ae6a77..352bb554ff6 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -7,9 +7,9 @@ - 11.0.0-preview.5.26230.101 - 11.0.0-preview.5.26230.101 - 11.0.0-preview.5.26230.101 + 11.0.0-preview.4.26253.2 + 11.0.0-preview.4.26253.2 + 11.0.0-preview.4.26253.2 11.0.0-beta.26230.101 diff --git a/eng/icu.proj b/eng/icu.proj index 0d4a83796bd..9727f1ca423 100644 --- a/eng/icu.proj +++ b/eng/icu.proj @@ -1,8 +1,8 @@ - - - + + + @@ -12,10 +12,13 @@ + + + - + @@ -46,14 +49,6 @@ - - - - = 3 or settings.SHRINK_LEVEL >= 1 - - -def create_reproduce_file(name, args): - def make_relative(filename): - filename = os.path.normpath(os.path.abspath(filename)) - filename = os.path.splitdrive(filename)[1] - filename = filename[1:] - return filename - - root = unsuffixed_basename(name) - with tarfile.open(name, 'w') as reproduce_file: - reproduce_file.add(shared.path_from_root('emscripten-version.txt'), os.path.join(root, 'version.txt')) - - with shared.get_temp_files().get_file(suffix='.tar') as rsp_name: - with open(rsp_name, 'w') as rsp: - ignore_next = False - output_arg = None - - for arg in args: - ignore = ignore_next - ignore_next = False - if arg.startswith('--reproduce='): - continue - - if arg.startswith('-o='): - rsp.write('-o\n') - arg = arg[3:] - output_arg = True - ignore = True - - if output_arg: - # If -o path contains directories, "emcc @response.txt" will likely - # fail because the archive we are creating doesn't contain empty - # directories for the output path (-o doesn't create directories). - # Strip directories to prevent the issue. - arg = os.path.basename(arg) - output_arg = False - - if not arg.startswith('-') and not ignore: - relpath = make_relative(arg) - rsp.write(relpath + '\n') - reproduce_file.add(arg, os.path.join(root, relpath)) - else: - rsp.write(arg + '\n') - - if ignore: - continue - - if arg in ('-MT', '-MF', '-MJ', '-MQ', '-D', '-U', '-o', '-x', - '-Xpreprocessor', '-include', '-imacros', '-idirafter', - '-iprefix', '-iwithprefix', '-iwithprefixbefore', - '-isysroot', '-imultilib', '-A', '-isystem', '-iquote', - '-install_name', '-compatibility_version', - '-current_version', '-I', '-L', '-include-pch', - '-Xlinker', '-Xclang'): - ignore_next = True - - if arg == '-o': - output_arg = True - - reproduce_file.add(rsp_name, os.path.join(root, 'response.txt')) - - -def setup_environment_settings(): - # Environment setting based on user input - environments = settings.ENVIRONMENT.split(',') - if any([x for x in environments if x not in VALID_ENVIRONMENTS]): - exit_with_error(f'Invalid environment specified in "ENVIRONMENT": {settings.ENVIRONMENT}. Should be one of: {",".join(VALID_ENVIRONMENTS)}') - - settings.ENVIRONMENT_MAY_BE_WEB = not settings.ENVIRONMENT or 'web' in environments - settings.ENVIRONMENT_MAY_BE_WEBVIEW = not settings.ENVIRONMENT or 'webview' in environments - settings.ENVIRONMENT_MAY_BE_NODE = not settings.ENVIRONMENT or 'node' in environments - settings.ENVIRONMENT_MAY_BE_SHELL = not settings.ENVIRONMENT or 'shell' in environments - - # The worker case also includes Node.js workers when pthreads are - # enabled and Node.js is one of the supported environments for the build to - # run on. Node.js workers are detected as a combination of - # ENVIRONMENT_IS_WORKER and ENVIRONMENT_IS_NODE. - settings.ENVIRONMENT_MAY_BE_WORKER = \ - not settings.ENVIRONMENT or \ - 'worker' in environments or \ - (settings.ENVIRONMENT_MAY_BE_NODE and settings.PTHREADS) - - if not settings.ENVIRONMENT_MAY_BE_WORKER and settings.PROXY_TO_WORKER: - exit_with_error('If you specify --proxy-to-worker and specify a "-sENVIRONMENT=" directive, it must include "worker" as a target! (Try e.g. -sENVIRONMENT=web,worker)') - - if not settings.ENVIRONMENT_MAY_BE_WORKER and settings.SHARED_MEMORY: - exit_with_error('When building with multithreading enabled and a "-sENVIRONMENT=" directive is specified, it must include "worker" as a target! (Try e.g. -sENVIRONMENT=web,worker)') - - -def minify_whitespace(): - return settings.OPT_LEVEL >= 2 and settings.DEBUG_LEVEL == 0 - - -def embed_memfile(options): - return (settings.SINGLE_FILE or - (settings.WASM2JS and not options.memory_init_file and - (not settings.MAIN_MODULE and - not settings.SIDE_MODULE and - not settings.GENERATE_SOURCE_MAP))) - - -def expand_byte_size_suffixes(value): - """Given a string with KB/MB size suffixes, such as "32MB", computes how - many bytes that is and returns it as an integer. - """ - value = value.strip() - match = re.match(r'^(\d+)\s*([kmgt]?b)?$', value, re.I) - if not match: - exit_with_error("invalid byte size `%s`. Valid suffixes are: kb, mb, gb, tb" % value) - value, suffix = match.groups() - value = int(value) - if suffix: - size_suffixes = {suffix: 1024 ** i for i, suffix in enumerate(['b', 'kb', 'mb', 'gb', 'tb'])} - value *= size_suffixes[suffix.lower()] - return value - - -def default_setting(name, new_default): - if name not in user_settings: - setattr(settings, name, new_default) - - -def apply_user_settings(): - """Take a map of users settings {NAME: VALUE} and apply them to the global - settings object. - """ - - # Stash a copy of all available incoming APIs before the user can potentially override it - settings.ALL_INCOMING_MODULE_JS_API = settings.INCOMING_MODULE_JS_API + EXTRA_INCOMING_JS_API - - for key, value in user_settings.items(): - if key in settings.internal_settings: - exit_with_error('%s is an internal setting and cannot be set from command line', key) - - # map legacy settings which have aliases to the new names - # but keep the original key so errors are correctly reported via the `setattr` below - user_key = key - if key in settings.legacy_settings and key in settings.alt_names: - key = settings.alt_names[key] - - # In those settings fields that represent amount of memory, translate suffixes to multiples of 1024. - if key in MEM_SIZE_SETTINGS: - value = str(expand_byte_size_suffixes(value)) - - filename = None - if value and value[0] == '@': - filename = strip_prefix(value, '@') - if not os.path.exists(filename): - exit_with_error('%s: file not found parsing argument: %s=%s' % (filename, key, value)) - value = read_file(filename).strip() - else: - value = value.replace('\\', '\\\\') - - expected_type = settings.types.get(key) - - if filename and expected_type == list and value.strip()[0] != '[': - # Prefer simpler one-line-per value parser - value = parse_symbol_list_file(value) - else: - try: - value = parse_value(value, expected_type) - except Exception as e: - exit_with_error('a problem occurred in evaluating the content after a "-s", specifically "%s=%s": %s', key, value, str(e)) - - setattr(settings, user_key, value) - - if key == 'EXPORTED_FUNCTIONS': - # used for warnings in emscripten.py - settings.USER_EXPORTED_FUNCTIONS = settings.EXPORTED_FUNCTIONS.copy() - - # TODO(sbc): Remove this legacy way. - if key == 'WASM_OBJECT_FILES': - settings.LTO = 0 if value else 'full' - - -def is_ar_file_with_missing_index(archive_file): - # We parse the archive header outselves because llvm-nm --print-armap is slower and less - # reliable. - # See: https://github.com/emscripten-core/emscripten/issues/10195 - archive_header = b'!\n' - file_header_size = 60 - - with open(archive_file, 'rb') as f: - header = f.read(len(archive_header)) - if header != archive_header: - # This is not even an ar file - return False - file_header = f.read(file_header_size) - if len(file_header) != file_header_size: - # We don't have any file entires at all so we don't consider the index missing - return False - - name = file_header[:16].strip() - # If '/' is the name of the first file we have an index - return name != b'/' - - -def ensure_archive_index(archive_file): - # Fastcomp linking works without archive indexes. - if not settings.AUTO_ARCHIVE_INDEXES: - return - if is_ar_file_with_missing_index(archive_file): - diagnostics.warning('emcc', '%s: archive is missing an index; Use emar when creating libraries to ensure an index is created', archive_file) - diagnostics.warning('emcc', '%s: adding index', archive_file) - run_process([shared.LLVM_RANLIB, archive_file]) - - -def generate_js_symbols(): - # Runs the js compiler to generate a list of all symbols available in the JS - # libraries. This must be done separately for each linker invokation since the - # list of symbols depends on what settings are used. - # TODO(sbc): Find a way to optimize this. Potentially we could add a super-set - # mode of the js compiler that would generate a list of all possible symbols - # that could be checked in. - emscripten.generate_struct_info() - _, forwarded_data = emscripten.compile_javascript(symbols_only=True) - # When running in symbols_only mode compiler.js outputs a flat list of C symbols. - return json.loads(forwarded_data) - - -@ToolchainProfiler.profile_block('JS symbol generation') -def get_all_js_syms(): - # Avoiding using the cache when generating struct info since - # this step is performed while the cache is locked. - if DEBUG or settings.BOOTSTRAPPING_STRUCT_INFO or config.FROZEN_CACHE: - return generate_js_symbols() - - # We define a cache hit as when the settings and `--js-library` contents are - # identical. - # Ignore certain settings that can are no relevant to library deps. Here we - # skip PRE_JS_FILES/POST_JS_FILES which don't effect the library symbol list - # and can contain full paths to temporary files. - skip_settings = {'PRE_JS_FILES', 'POST_JS_FILES'} - input_files = [json.dumps(settings.external_dict(skip_keys=skip_settings), sort_keys=True, indent=2)] - for jslib in sorted(glob.glob(utils.path_from_root('src') + '/library*.js')): - input_files.append(read_file(jslib)) - for jslib in settings.JS_LIBRARIES: - if not os.path.isabs(jslib): - jslib = utils.path_from_root('src', jslib) - input_files.append(read_file(jslib)) - content = '\n'.join(input_files) - content_hash = hashlib.sha1(content.encode('utf-8')).hexdigest() - - def build_symbol_list(filename): - """Only called when there is no existing symbol list for a given content hash. - """ - library_syms = generate_js_symbols() - lines = [] - - for name, deps in library_syms.items(): - if deps: - lines.append('%s: %s' % (name, ','.join(deps))) - else: - lines.append(name) - write_file(filename, '\n'.join(lines) + '\n') - - # We need to use a separate lock here for symbol lists because, unlike with system libraries, - # it's normally for these file to get pruned as part of normal operation. This means that it - # can be deleted between the `cache.get()` then the `read_file`. - with filelock.FileLock(cache.get_path(cache.get_path('symbol_lists.lock'))): - filename = cache.get(f'symbol_lists/{content_hash}.txt', build_symbol_list) - lines = read_file(filename).splitlines() - library_syms = {} - for line in lines: - if ':' in line: - name, deps = line.split(':') - library_syms[name] = deps.strip().split(',') - else: - library_syms[line] = [] - - # Limit of the overall size of the cache to 100 files. - # This code will get test coverage since a full test run of `other` or `core` - # generates ~1000 unique symbol lists. - cache_limit = 500 - root = cache.get_path('symbol_lists') - if len(os.listdir(root)) > cache_limit: - files = [] - for f in os.listdir(root): - f = os.path.join(root, f) - files.append((f, os.path.getmtime(f))) - files.sort(key=lambda x: x[1]) - # Delete all but the newest N files - for f, _ in files[:-cache_limit]: - delete_file(f) - - return library_syms - - -def filter_link_flags(flags, using_lld): - def is_supported(f): - if using_lld: - for flag, takes_arg in UNSUPPORTED_LLD_FLAGS.items(): - # lld allows various flags to have either a single -foo or double --foo - if f.startswith(flag) or f.startswith('-' + flag): - diagnostics.warning('linkflags', 'ignoring unsupported linker flag: `%s`', f) - # Skip the next argument if this linker flag takes and argument and that - # argument was not specified as a separately (i.e. it was specified as - # single arg containing an `=` char.) - skip_next = takes_arg and '=' not in f - return False, skip_next - return True, False - else: - if f in SUPPORTED_LINKER_FLAGS: - return True, False - # Silently ignore -l/-L flags when not using lld. If using lld allow - # them to pass through the linker - if f.startswith('-l') or f.startswith('-L'): - return False, False - diagnostics.warning('linkflags', 'ignoring unsupported linker flag: `%s`', f) - return False, False - - results = [] - skip_next = False - for f in flags: - if skip_next: - skip_next = False - continue - keep, skip_next = is_supported(f[1]) - if keep: - results.append(f) - - return results - - -def fix_windows_newlines(text): - # Avoid duplicating \r\n to \r\r\n when writing out text. - if WINDOWS: - text = text.replace('\r\n', '\n') - return text - - -def read_js_files(files): - contents = '\n'.join(read_file(f) for f in files) - return fix_windows_newlines(contents) - - -def cxx_to_c_compiler(cxx): - # Convert C++ compiler name into C compiler name - dirname, basename = os.path.split(cxx) - basename = basename.replace('clang++', 'clang').replace('g++', 'gcc').replace('em++', 'emcc') - return os.path.join(dirname, basename) - - -def should_run_binaryen_optimizer(): - # run the binaryen optimizer in -O2+. in -O0 we don't need it obviously, while - # in -O1 we don't run it as the LLVM optimizer has been run, and it does the - # great majority of the work; not running the binaryen optimizer in that case - # keeps -O1 mostly-optimized while compiling quickly and without rewriting - # DWARF etc. - return settings.OPT_LEVEL >= 2 - - -def get_binaryen_passes(): - passes = [] - optimizing = should_run_binaryen_optimizer() - # safe heap must run before post-emscripten, so post-emscripten can apply the sbrk ptr - if settings.SAFE_HEAP: - passes += ['--safe-heap'] - if settings.MEMORY64 == 2: - passes += ['--memory64-lowering'] - # sign-ext is enabled by default by llvm. If the target browser settings don't support - # this we lower it away here using a binaryen pass. - if not feature_matrix.caniuse(feature_matrix.Feature.SIGN_EXT): - logger.debug('lowering sign-ext feature due to incompatiable target browser engines') - passes += ['--signext-lowering'] - if optimizing: - passes += ['--post-emscripten'] - if settings.SIDE_MODULE: - passes += ['--pass-arg=post-emscripten-side-module'] - if optimizing: - passes += [building.opt_level_to_str(settings.OPT_LEVEL, settings.SHRINK_LEVEL)] - # when optimizing, use the fact that low memory is never used (1024 is a - # hardcoded value in the binaryen pass) - if optimizing and settings.GLOBAL_BASE >= 1024: - passes += ['--low-memory-unused'] - if settings.AUTODEBUG: - # adding '--flatten' here may make these even more effective - passes += ['--instrument-locals'] - passes += ['--log-execution'] - passes += ['--instrument-memory'] - if settings.LEGALIZE_JS_FFI: - # legalize it again now, as the instrumentation may need it - passes += ['--legalize-js-interface'] - passes += building.js_legalization_pass_flags() - if settings.EMULATE_FUNCTION_POINTER_CASTS: - # note that this pass must run before asyncify, as if it runs afterwards we only - # generate the byn$fpcast_emu functions after asyncify runs, and so we wouldn't - # be able to further process them. - passes += ['--fpcast-emu'] - if settings.ASYNCIFY == 1: - passes += ['--asyncify'] - if settings.MAIN_MODULE or settings.SIDE_MODULE: - passes += ['--pass-arg=asyncify-relocatable'] - if settings.ASSERTIONS: - passes += ['--pass-arg=asyncify-asserts'] - if settings.ASYNCIFY_ADVISE: - passes += ['--pass-arg=asyncify-verbose'] - if settings.ASYNCIFY_IGNORE_INDIRECT: - passes += ['--pass-arg=asyncify-ignore-indirect'] - passes += ['--pass-arg=asyncify-imports@%s' % ','.join(settings.ASYNCIFY_IMPORTS)] - - # shell escaping can be confusing; try to emit useful warnings - def check_human_readable_list(items): - for item in items: - if item.count('(') != item.count(')'): - logger.warning('emcc: ASYNCIFY list contains an item without balanced parentheses ("(", ")"):') - logger.warning(' ' + item) - logger.warning('This may indicate improper escaping that led to splitting inside your names.') - logger.warning('Try using a response file. e.g: -sASYNCIFY_ONLY=@funcs.txt. The format is a simple') - logger.warning('text file, one line per function.') - break - - if settings.ASYNCIFY_REMOVE: - check_human_readable_list(settings.ASYNCIFY_REMOVE) - passes += ['--pass-arg=asyncify-removelist@%s' % ','.join(settings.ASYNCIFY_REMOVE)] - if settings.ASYNCIFY_ADD: - check_human_readable_list(settings.ASYNCIFY_ADD) - passes += ['--pass-arg=asyncify-addlist@%s' % ','.join(settings.ASYNCIFY_ADD)] - if settings.ASYNCIFY_ONLY: - check_human_readable_list(settings.ASYNCIFY_ONLY) - passes += ['--pass-arg=asyncify-onlylist@%s' % ','.join(settings.ASYNCIFY_ONLY)] - elif settings.ASYNCIFY == 2: - passes += ['--jspi'] - passes += ['--pass-arg=jspi-imports@%s' % ','.join(settings.ASYNCIFY_IMPORTS)] - passes += ['--pass-arg=jspi-exports@%s' % ','.join(settings.ASYNCIFY_EXPORTS)] - if settings.SPLIT_MODULE: - passes += ['--pass-arg=jspi-split-module'] - - if settings.BINARYEN_IGNORE_IMPLICIT_TRAPS: - passes += ['--ignore-implicit-traps'] - # normally we can assume the memory, if imported, has not been modified - # beforehand (in fact, in most cases the memory is not even imported anyhow, - # but it is still safe to pass the flag), and is therefore filled with zeros. - # the one exception is dynamic linking of a side module: the main module is ok - # as it is loaded first, but the side module may be assigned memory that was - # previously used. - if optimizing and not settings.SIDE_MODULE: - passes += ['--zero-filled-memory'] - # LLVM output always has immutable initial table contents: the table is - # fixed and may only be appended to at runtime (that is true even in - # relocatable mode) - if optimizing: - passes += ['--pass-arg=directize-initial-contents-immutable'] - - if settings.BINARYEN_EXTRA_PASSES: - # BINARYEN_EXTRA_PASSES is comma-separated, and we support both '-'-prefixed and - # unprefixed pass names - extras = settings.BINARYEN_EXTRA_PASSES.split(',') - passes += [('--' + p) if p[0] != '-' else p for p in extras if p] - - return passes - - -def make_js_executable(script): - src = read_file(script) - cmd = config.NODE_JS - if settings.MEMORY64 == 1: - cmd += shared.node_memory64_flags() - elif settings.WASM_BIGINT: - cmd += shared.node_bigint_flags() - if len(cmd) > 1 or not os.path.isabs(cmd[0]): - # Using -S (--split-string) here means that arguments to the executable are - # correctly parsed. We don't do this by default because old versions of env - # don't support -S. - cmd = '/usr/bin/env -S ' + shared.shlex_join(cmd) - else: - cmd = shared.shlex_join(cmd) - logger.debug('adding `#!` to JavaScript file: %s' % cmd) - # add shebang - with open(script, 'w') as f: - f.write('#!/bin/sh\n":" //# comment; exec /usr/bin/env %s --experimental-wasm-threads --experimental-wasm-bulk-memory\n' % cmd) - f.write(src) - try: - os.chmod(script, stat.S_IMODE(os.stat(script).st_mode) | stat.S_IXUSR) # make executable - except OSError: - pass # can fail if e.g. writing the executable to /dev/null - - -def do_split_module(wasm_file, options): - os.rename(wasm_file, wasm_file + '.orig') - args = ['--instrument'] - if options.requested_debug: - # Tell wasm-split to preserve function names. - args += ['-g'] - building.run_binaryen_command('wasm-split', wasm_file + '.orig', outfile=wasm_file, args=args) - - -def is_dash_s_for_emcc(args, i): - # -s OPT=VALUE or -s OPT or -sOPT are all interpreted as emscripten flags. - # -s by itself is a linker option (alias for --strip-all) - if args[i] == '-s': - if len(args) <= i + 1: - return False - arg = args[i + 1] - else: - arg = strip_prefix(args[i], '-s') - arg = arg.split('=')[0] - return arg.isidentifier() and arg.isupper() - - -def filter_out_dynamic_libs(options, inputs): - - # Filters out "fake" dynamic libraries that are really just intermediate object files. - def check(input_file): - if get_file_suffix(input_file) in DYNAMICLIB_ENDINGS and not building.is_wasm_dylib(input_file): - if not options.ignore_dynamic_linking: - diagnostics.warning('emcc', 'ignoring dynamic library %s because not compiling to JS or HTML, remember to link it when compiling to JS or HTML at the end', os.path.basename(input_file)) - return False - else: - return True - - return [f for f in inputs if check(f)] - - -def filter_out_duplicate_dynamic_libs(inputs): - seen = set() - - # Filter out duplicate "fake" shared libraries (intermediate object files). - # See test_core.py:test_redundant_link - def check(input_file): - if get_file_suffix(input_file) in DYNAMICLIB_ENDINGS and not building.is_wasm_dylib(input_file): - abspath = os.path.abspath(input_file) - if abspath in seen: - return False - seen.add(abspath) - return True - - return [f for f in inputs if check(f)] - - -def process_dynamic_libs(dylibs, lib_dirs): - extras = [] - seen = set() - to_process = dylibs.copy() - while to_process: - dylib = to_process.pop() - dylink = webassembly.parse_dylink_section(dylib) - for needed in dylink.needed: - if needed in seen: - continue - path = find_library(needed, lib_dirs) - if path: - extras.append(path) - seen.add(needed) - else: - exit_with_error(f'{os.path.normpath(dylib)}: shared library dependency not found: `{needed}`') - to_process.append(path) - - dylibs += extras - for dylib in dylibs: - exports = webassembly.get_exports(dylib) - exports = set(e.name for e in exports) - settings.SIDE_MODULE_EXPORTS.extend(sorted(exports)) - - imports = webassembly.get_imports(dylib) - imports = [i.field for i in imports if i.kind in (webassembly.ExternType.FUNC, webassembly.ExternType.GLOBAL, webassembly.ExternType.TAG)] - # For now we ignore `invoke_` functions imported by side modules and rely - # on the dynamic linker to create them on the fly. - # TODO(sbc): Integrate with metadata.invokeFuncs that comes from the - # main module to avoid creating new invoke functions at runtime. - imports = set(imports) - imports = set(i for i in imports if not i.startswith('invoke_')) - strong_imports = sorted(imports.difference(exports)) - logger.debug('Adding symbols requirements from `%s`: %s', dylib, imports) - - mangled_imports = [shared.asmjs_mangle(e) for e in sorted(imports)] - mangled_strong_imports = [shared.asmjs_mangle(e) for e in strong_imports] - settings.SIDE_MODULE_IMPORTS.extend(mangled_imports) - settings.EXPORT_IF_DEFINED.extend(sorted(imports)) - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.extend(sorted(imports)) - building.user_requested_exports.update(mangled_strong_imports) - - -def unmangle_symbols_from_cmdline(symbols): - def unmangle(x): - return x.replace('.', ' ').replace('#', '&').replace('?', ',') - - if type(symbols) is list: - return [unmangle(x) for x in symbols] - return unmangle(symbols) - - -def parse_s_args(args): - settings_changes = [] - for i in range(len(args)): - if args[i].startswith('-s'): - if is_dash_s_for_emcc(args, i): - if args[i] == '-s': - key = args[i + 1] - args[i + 1] = '' - else: - key = strip_prefix(args[i], '-s') - args[i] = '' - - # If not = is specified default to 1 - if '=' not in key: - key += '=1' - - # Special handling of browser version targets. A version -1 means that the specific version - # is not supported at all. Replace those with INT32_MAX to make it possible to compare e.g. - # #if MIN_FIREFOX_VERSION < 68 - if re.match(r'MIN_.*_VERSION(=.*)?', key): - try: - if int(key.split('=')[1]) < 0: - key = key.split('=')[0] + '=0x7FFFFFFF' - except Exception: - pass - - settings_changes.append(key) - - newargs = [a for a in args if a] - return (settings_changes, newargs) - - -def emsdk_cflags(user_args): - cflags = ['--sysroot=' + cache.get_sysroot(absolute=True)] - - def array_contains_any_of(hay, needles): - for n in needles: - if n in hay: - return True - - if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER) or array_contains_any_of(user_args, SIMD_NEON_FLAGS): - if '-msimd128' not in user_args and '-mrelaxed-simd' not in user_args: - exit_with_error('Passing any of ' + ', '.join(SIMD_INTEL_FEATURE_TOWER + SIMD_NEON_FLAGS) + ' flags also requires passing -msimd128 (or -mrelaxed-simd)!') - cflags += ['-D__SSE__=1'] - - if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[1:]): - cflags += ['-D__SSE2__=1'] - - if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[2:]): - cflags += ['-D__SSE3__=1'] - - if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[3:]): - cflags += ['-D__SSSE3__=1'] - - if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[4:]): - cflags += ['-D__SSE4_1__=1'] - - # Handle both -msse4.2 and its alias -msse4. - if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[5:]): - cflags += ['-D__SSE4_2__=1'] - - if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[7:]): - cflags += ['-D__AVX__=1'] - - if array_contains_any_of(user_args, SIMD_NEON_FLAGS): - cflags += ['-D__ARM_NEON__=1'] - - if not settings.USE_SDL: - cflags += ['-Xclang', '-iwithsysroot' + os.path.join('/include', 'fakesdl')] - - return cflags + ['-Xclang', '-iwithsysroot' + os.path.join('/include', 'compat')] - - -def get_target_flags(): - return ['-target', shared.get_llvm_target()] - - -def get_clang_flags(user_args): - flags = get_target_flags() - - # if exception catching is disabled, we can prevent that code from being - # generated in the frontend - if settings.DISABLE_EXCEPTION_CATCHING and not settings.WASM_EXCEPTIONS: - flags.append('-fignore-exceptions') - - if settings.INLINING_LIMIT: - flags.append('-fno-inline-functions') - - if settings.RELOCATABLE and '-fPIC' not in user_args: - flags.append('-fPIC') - - # We use default visiibilty=default in emscripten even though the upstream - # backend defaults visibility=hidden. This matched the expectations of C/C++ - # code in the wild which expects undecorated symbols to be exported to other - # DSO's by default. - if not any(a.startswith('-fvisibility') for a in user_args): - flags.append('-fvisibility=default') - - if settings.LTO: - if not any(a.startswith('-flto') for a in user_args): - flags.append('-flto=' + settings.LTO) - # setjmp/longjmp handling using Wasm EH - # For non-LTO, '-mllvm -wasm-enable-eh' added in - # building.llvm_backend_args() sets this feature in clang. But in LTO, the - # argument is added to wasm-ld instead, so clang needs to know that EH is - # enabled so that it can be added to the attributes in LLVM IR. - if settings.SUPPORT_LONGJMP == 'wasm': - flags.append('-mexception-handling') - - else: - # In LTO mode these args get passed instead at link time when the backend runs. - for a in building.llvm_backend_args(): - flags += ['-mllvm', a] - - return flags - - -cflags = None - - -def get_cflags(user_args, is_cxx): - global cflags - if cflags: - return cflags - - # Flags we pass to the compiler when building C/C++ code - # We add these to the user's flags (newargs), but not when building .s or .S assembly files - cflags = get_clang_flags(user_args) - - if settings.EMSCRIPTEN_TRACING: - cflags.append('-D__EMSCRIPTEN_TRACING__=1') - - if settings.SHARED_MEMORY: - cflags.append('-D__EMSCRIPTEN_SHARED_MEMORY__=1') - - if settings.WASM_WORKERS: - cflags.append('-D__EMSCRIPTEN_WASM_WORKERS__=1') - - if not settings.STRICT: - # The preprocessor define EMSCRIPTEN is deprecated. Don't pass it to code - # in strict mode. Code should use the define __EMSCRIPTEN__ instead. - cflags.append('-DEMSCRIPTEN') - - # Changes to default clang behavior - - # Implicit functions can cause horribly confusing function pointer type errors, see #2175 - # If your codebase really needs them - very unrecommended! - you can disable the error with - # -Wno-error=implicit-function-declaration - # or disable even a warning about it with - # -Wno-implicit-function-declaration - # This is already an error in C++ so we don't need to inject extra flags. - if not is_cxx: - cflags += ['-Werror=implicit-function-declaration'] - - ports.add_cflags(cflags, settings) - - if '-nostdinc' in user_args: - return cflags - - cflags += emsdk_cflags(user_args) - return cflags - - -def get_file_suffix(filename): - """Parses the essential suffix of a filename, discarding Unix-style version - numbers in the name. For example for 'libz.so.1.2.8' returns '.so'""" - if filename in SPECIAL_ENDINGLESS_FILENAMES: - return filename - while filename: - filename, suffix = os.path.splitext(filename) - if not suffix[1:].isdigit(): - return suffix - return '' - - -def get_library_basename(filename): - """Similar to get_file_suffix this strips off all numeric suffixes and then - then final non-numeric one. For example for 'libz.so.1.2.8' returns 'libz'""" - filename = os.path.basename(filename) - while filename: - filename, suffix = os.path.splitext(filename) - # Keep stipping suffixes until we strip a non-numeric one. - if not suffix[1:].isdigit(): - return filename - - -def get_secondary_target(target, ext): - # Depending on the output format emscripten creates zero or more secondary - # output files (e.g. the .wasm file when creating JS output, or the - # .js and the .wasm file when creating html output. - # Thus function names the secondary output files, while ensuring they - # never collide with the primary one. - base = unsuffixed(target) - if get_file_suffix(target) == ext: - base += '_' - return base + ext - - -def in_temp(name): - temp_dir = shared.get_emscripten_temp_dir() - return os.path.join(temp_dir, os.path.basename(name)) - - -def dedup_list(lst): - # Since we require python 3.6, that ordering of dictionaries is guaranteed - # to be insertion order so we can use 'dict' here but not 'set'. - return list(dict.fromkeys(lst)) - - -def move_file(src, dst): - logging.debug('move: %s -> %s', src, dst) - if os.path.isdir(dst): - exit_with_error(f'cannot write output file `{dst}`: Is a directory') - src = os.path.abspath(src) - dst = os.path.abspath(dst) - if src == dst: - return - if dst == os.devnull: - return - shutil.move(src, dst) - - -# Returns the subresource location for run-time access -def get_subresource_location(path, data_uri=None): - if data_uri is None: - data_uri = settings.SINGLE_FILE - if data_uri: - # if the path does not exist, then there is no data to encode - if not os.path.exists(path): - return '' - data = base64.b64encode(utils.read_binary(path)) - return 'data:application/octet-stream;base64,' + data.decode('ascii') - else: - return os.path.basename(path) - - -@ToolchainProfiler.profile_block('package_files') -def package_files(options, target): - rtn = [] - logger.debug('setting up files') - file_args = ['--from-emcc'] - if options.preload_files: - file_args.append('--preload') - file_args += options.preload_files - if options.embed_files: - file_args.append('--embed') - file_args += options.embed_files - if options.exclude_files: - file_args.append('--exclude') - file_args += options.exclude_files - if options.use_preload_cache: - file_args.append('--use-preload-cache') - if settings.LZ4: - file_args.append('--lz4') - if options.use_preload_plugins: - file_args.append('--use-preload-plugins') - if not settings.ENVIRONMENT_MAY_BE_NODE: - file_args.append('--no-node') - if options.embed_files: - if settings.MEMORY64: - file_args += ['--wasm64'] - object_file = in_temp('embedded_files.o') - file_args += ['--obj-output=' + object_file] - rtn.append(object_file) - - cmd = [shared.FILE_PACKAGER, shared.replace_suffix(target, '.data')] + file_args - if options.preload_files: - # Preloading files uses --pre-js code that runs before the module is loaded. - file_code = shared.check_call(cmd, stdout=PIPE).stdout - js_manipulation.add_files_pre_js(options.pre_js, file_code) - else: - # Otherwise, we are embedding files, which does not require --pre-js code, - # and instead relies on a static constrcutor to populate the filesystem. - shared.check_call(cmd) - - return rtn - - -run_via_emxx = False - - -# -# Main run() function -# -def run(args): - if run_via_emxx: - clang = shared.CLANG_CXX - else: - clang = shared.CLANG_CC - - # Special case the handling of `-v` because it has a special/different meaning - # when used with no other arguments. In particular, we must handle this early - # on, before we inject EMCC_CFLAGS. This is because tools like cmake and - # autoconf will run `emcc -v` to determine the compiler version and we don't - # want that to break for users of EMCC_CFLAGS. - if len(args) == 2 and args[1] == '-v': - # autoconf likes to see 'GNU' in the output to enable shared object support - print(version_string(), file=sys.stderr) - return shared.check_call([clang, '-v'] + get_target_flags(), check=False).returncode - - # Additional compiler flags that we treat as if they were passed to us on the - # commandline - EMCC_CFLAGS = os.environ.get('EMCC_CFLAGS') - if EMCC_CFLAGS: - args += shlex.split(EMCC_CFLAGS) - - if DEBUG: - logger.warning(f'invocation: {shared.shlex_join(args)} (in {os.getcwd()})') - - # Strip args[0] (program name) - args = args[1:] - - misc_temp_files = shared.get_temp_files() - - # Handle some global flags - - # read response files very early on - try: - args = substitute_response_files(args) - except IOError as e: - exit_with_error(e) - - if '--help' in args: - # Documentation for emcc and its options must be updated in: - # site/source/docs/tools_reference/emcc.rst - # This then gets built (via: `make -C site text`) to: - # site/build/text/docs/tools_reference/emcc.txt - # This then needs to be copied to its final home in docs/emcc.txt from where - # we read it here. We have CI rules that ensure its always up-to-date. - print(read_file(utils.path_from_root('docs/emcc.txt'))) - - print(''' ------------------------------------------------------------------- - -emcc: supported targets: llvm bitcode, WebAssembly, NOT elf -(autoconf likes to see elf above to enable shared object support) -''') - return 0 - - if '--version' in args: - print(version_string()) - print('''\ -Copyright (C) 2014 the Emscripten authors (see AUTHORS.txt) -This is free and open source software under the MIT license. -There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -''') - return 0 - - if '-dumpmachine' in args: - print(shared.get_llvm_target()) - return 0 - - if '-dumpversion' in args: # gcc's doc states "Print the compiler version [...] and don't do anything else." - print(shared.EMSCRIPTEN_VERSION) - return 0 - - if '--cflags' in args: - # fake running the command, to see the full args we pass to clang - args = [x for x in args if x != '--cflags'] - with misc_temp_files.get_file(suffix='.o') as temp_target: - input_file = 'hello_world.c' - compiler = shared.EMCC - if run_via_emxx: - compiler = shared.EMXX - cmd = [compiler, utils.path_from_root('test', input_file), '-v', '-c', '-o', temp_target] + args - proc = run_process(cmd, stderr=PIPE, check=False) - if proc.returncode != 0: - print(proc.stderr) - exit_with_error('error getting cflags') - lines = [x for x in proc.stderr.splitlines() if clang in x and input_file in x] - parts = shlex.split(lines[0].replace('\\', '\\\\')) - parts = [x for x in parts if x not in ['-c', '-o', '-v', '-emit-llvm'] and input_file not in x and temp_target not in x] - print(shared.shlex_join(parts[1:])) - return 0 - - passthrough_flags = ['-print-search-dirs', '-print-libgcc-file-name'] - if any(a in args for a in passthrough_flags) or any(a.startswith('-print-file-name=') for a in args): - return run_process([clang] + args + get_cflags(args, run_via_emxx), check=False).returncode - - ## Process argument and setup the compiler - state = EmccState(args) - options, newargs = phase_parse_arguments(state) - - shared.check_sanity() - - if 'EMMAKEN_NO_SDK' in os.environ: - exit_with_error('EMMAKEN_NO_SDK is no longer supported. The standard -nostdlib and -nostdinc flags should be used instead') - - if 'EMMAKEN_COMPILER' in os.environ: - exit_with_error('`EMMAKEN_COMPILER` is no longer supported.\n' + - 'Please use the `LLVM_ROOT` and/or `COMPILER_WRAPPER` config settings instread') - - if 'EMMAKEN_CFLAGS' in os.environ: - exit_with_error('`EMMAKEN_CFLAGS` is no longer supported, please use `EMCC_CFLAGS` instead') - - if 'EMCC_REPRODUCE' in os.environ: - options.reproduce = os.environ['EMCC_REPRODUCE'] - - # For internal consistency, ensure we don't attempt or read or write any link time - # settings until we reach the linking phase. - settings.limit_settings(COMPILE_TIME_SETTINGS) - - newargs, input_files = phase_setup(options, state, newargs) - - if options.reproduce: - create_reproduce_file(options.reproduce, args) - - if state.mode == Mode.POST_LINK_ONLY: - settings.limit_settings(None) - target, wasm_target = phase_linker_setup(options, state, newargs) - process_libraries(state, []) - if len(input_files) != 1: - exit_with_error('--post-link requires a single input file') - phase_post_link(options, state, input_files[0][1], wasm_target, target) - return 0 - - ## Compile source code to object files - linker_inputs = phase_compile_inputs(options, state, newargs, input_files) - - if state.mode != Mode.COMPILE_AND_LINK: - logger.debug('stopping after compile phase') - for flag in state.link_flags: - diagnostics.warning('unused-command-line-argument', "argument unused during compilation: '%s'" % flag[1]) - for f in linker_inputs: - diagnostics.warning('unused-command-line-argument', "%s: linker input file unused because linking not done" % f[1]) - - return 0 - - # We have now passed the compile phase, allow reading/writing of all settings. - settings.limit_settings(None) - - if options.output_file and options.output_file.startswith('-'): - exit_with_error(f'invalid output filename: `{options.output_file}`') - - target, wasm_target = phase_linker_setup(options, state, newargs) - - # Link object files using wasm-ld or llvm-link (for bitcode linking) - linker_arguments = phase_calculate_linker_inputs(options, state, linker_inputs) - - # Embed and preload files - if len(options.preload_files) or len(options.embed_files): - linker_arguments += package_files(options, target) - - settings.PRE_JS_FILES = [os.path.abspath(f) for f in options.pre_js] - settings.POST_JS_FILES = [os.path.abspath(f) for f in options.post_js] - - if options.oformat == OFormat.OBJECT: - logger.debug(f'link_to_object: {linker_arguments} -> {target}') - building.link_to_object(linker_arguments, target) - logger.debug('stopping after linking to object file') - return 0 - - js_syms = {} - if not settings.SIDE_MODULE: - js_syms = get_all_js_syms() - deps_info.append_deps_info(js_syms) - - phase_calculate_system_libraries(state, linker_arguments, linker_inputs, newargs) - - phase_link(linker_arguments, wasm_target, js_syms) - - # Special handling for when the user passed '-Wl,--version'. In this case the linker - # does not create the output file, but just prints its version and exits with 0. - if '--version' in linker_arguments: - return 0 - - # TODO(sbc): In theory we should really run the whole pipeline even if the output is - # /dev/null, but that will take some refactoring - if target == os.devnull: - return 0 - - # Perform post-link steps (unless we are running bare mode) - if options.oformat != OFormat.BARE: - phase_post_link(options, state, wasm_target, wasm_target, target) - - return 0 - - -@ToolchainProfiler.profile_block('calculate linker inputs') -def phase_calculate_linker_inputs(options, state, linker_inputs): - using_lld = not (options.oformat == OFormat.OBJECT and settings.LTO) - state.link_flags = filter_link_flags(state.link_flags, using_lld) - - # Decide what we will link - process_libraries(state, linker_inputs) - - linker_args = [val for _, val in sorted(linker_inputs + state.link_flags)] - - # If we are linking to an intermediate object then ignore other - # "fake" dynamic libraries, since otherwise we will end up with - # multiple copies in the final executable. - if options.oformat == OFormat.OBJECT or options.ignore_dynamic_linking: - linker_args = filter_out_dynamic_libs(options, linker_args) - else: - linker_args = filter_out_duplicate_dynamic_libs(linker_args) - - if settings.MAIN_MODULE: - dylibs = [a for a in linker_args if building.is_wasm_dylib(a)] - process_dynamic_libs(dylibs, state.lib_dirs) - - return linker_args - - -def normalize_boolean_setting(name, value): - # boolean NO_X settings are aliases for X - # (note that *non*-boolean setting values have special meanings, - # and we can't just flip them, so leave them as-is to be - # handled in a special way later) - if name.startswith('NO_') and value in ('0', '1'): - name = strip_prefix(name, 'NO_') - value = str(1 - int(value)) - return name, value - - -@ToolchainProfiler.profile_block('parse arguments') -def phase_parse_arguments(state): - """The first phase of the compiler. Parse command line argument and - populate settings. - """ - newargs = list(state.orig_args) - - # Scan and strip emscripten specific cmdline warning flags. - # This needs to run before other cmdline flags have been parsed, so that - # warnings are properly printed during arg parse. - newargs = diagnostics.capture_warnings(newargs) - - for i in range(len(newargs)): - if newargs[i] in ('-l', '-L', '-I'): - # Scan for individual -l/-L/-I arguments and concatenate the next arg on - # if there is no suffix - newargs[i] += newargs[i + 1] - newargs[i + 1] = '' - - options, settings_changes, user_js_defines, newargs = parse_args(newargs) - - if options.post_link or options.oformat == OFormat.BARE: - diagnostics.warning('experimental', '--oformat=bare/--post-link are experimental and subject to change.') - - explicit_settings_changes, newargs = parse_s_args(newargs) - settings_changes += explicit_settings_changes - - for s in settings_changes: - key, value = s.split('=', 1) - key, value = normalize_boolean_setting(key, value) - user_settings[key] = value - - # STRICT is used when applying settings so it needs to be applied first before - # calling `apply_user_settings`. - strict_cmdline = user_settings.get('STRICT') - if strict_cmdline: - settings.STRICT = int(strict_cmdline) - - # Apply user -jsD settings - for s in user_js_defines: - settings[s[0]] = s[1] - - # Apply -s settings in newargs here (after optimization levels, so they can override them) - apply_user_settings() - - return options, newargs - - -@ToolchainProfiler.profile_block('setup') -def phase_setup(options, state, newargs): - """Second phase: configure and setup the compiler based on the specified settings and arguments. - """ - - if settings.RUNTIME_LINKED_LIBS: - diagnostics.warning('deprecated', 'RUNTIME_LINKED_LIBS is deprecated; you can simply list the libraries directly on the commandline now') - newargs += settings.RUNTIME_LINKED_LIBS - - if settings.STRICT: - default_setting('DEFAULT_TO_CXX', 0) - - # Find input files - - # These three arrays are used to store arguments of different types for - # type-specific processing. In order to shuffle the arguments back together - # after processing, all of these arrays hold tuples (original_index, value). - # Note that the index part of the tuple can have a fractional part for input - # arguments that expand into multiple processed arguments, as in -Wl,-f1,-f2. - input_files = [] - - # find input files with a simple heuristic. we should really analyze - # based on a full understanding of gcc params, right now we just assume that - # what is left contains no more |-x OPT| things - skip = False - has_header_inputs = False - for i in range(len(newargs)): - if skip: - skip = False - continue - - arg = newargs[i] - if arg in ('-MT', '-MF', '-MJ', '-MQ', '-D', '-U', '-o', '-x', - '-Xpreprocessor', '-include', '-imacros', '-idirafter', - '-iprefix', '-iwithprefix', '-iwithprefixbefore', - '-isysroot', '-imultilib', '-A', '-isystem', '-iquote', - '-install_name', '-compatibility_version', - '-current_version', '-I', '-L', '-include-pch', - '-Xlinker', '-Xclang'): - skip = True - - if not arg.startswith('-'): - # we already removed -o , so all these should be inputs - newargs[i] = '' - # os.devnul should always be reported as existing but there is bug in windows - # python before 3.8: - # https://bugs.python.org/issue1311 - if not os.path.exists(arg) and arg != os.devnull: - exit_with_error('%s: No such file or directory ("%s" was expected to be an input file, based on the commandline arguments provided)', arg, arg) - file_suffix = get_file_suffix(arg) - if file_suffix in HEADER_ENDINGS: - has_header_inputs = True - if file_suffix in STATICLIB_ENDINGS and not building.is_ar(arg): - if building.is_bitcode(arg): - message = f'{arg}: File has a suffix of a static library {STATICLIB_ENDINGS}, but instead is an LLVM bitcode file! When linking LLVM bitcode files use .bc or .o.' - else: - message = arg + ': Unknown format, not a static library!' - exit_with_error(message) - if file_suffix in DYNAMICLIB_ENDINGS and not building.is_bitcode(arg) and not building.is_wasm(arg): - # For shared libraries that are neither bitcode nor wasm, assuming its local native - # library and attempt to find a library by the same name in our own library path. - # TODO(sbc): Do we really need this feature? See test_other.py:test_local_link - libname = strip_prefix(get_library_basename(arg), 'lib') - flag = '-l' + libname - diagnostics.warning('map-unrecognized-libraries', f'unrecognized file type: `{arg}`. Mapping to `{flag}` and hoping for the best') - add_link_flag(state, i, flag) - else: - input_files.append((i, arg)) - elif arg.startswith('-L'): - add_link_flag(state, i, arg) - newargs[i] = '' - elif arg.startswith('-l'): - add_link_flag(state, i, arg) - newargs[i] = '' - elif arg.startswith('-Wl,'): - # Multiple comma separated link flags can be specified. Create fake - # fractional indices for these: -Wl,a,b,c,d at index 4 becomes: - # (4, a), (4.25, b), (4.5, c), (4.75, d) - link_flags_to_add = arg.split(',')[1:] - for flag_index, flag in enumerate(link_flags_to_add): - add_link_flag(state, i + float(flag_index) / len(link_flags_to_add), flag) - newargs[i] = '' - elif arg == '-Xlinker': - add_link_flag(state, i + 1, newargs[i + 1]) - newargs[i] = '' - newargs[i + 1] = '' - elif arg == '-s': - # -s and some other compiler flags are normally passed onto the linker - # TODO(sbc): Pass this and other flags through when using lld - # link_flags.append((i, arg)) - newargs[i] = '' - elif arg == '-': - input_files.append((i, arg)) - newargs[i] = '' - - if not input_files and not state.link_flags: - exit_with_error('no input files') - - newargs = [a for a in newargs if a] - - # SSEx is implemented on top of SIMD128 instruction set, but do not pass SSE flags to LLVM - # so it won't think about generating native x86 SSE code. - newargs = [x for x in newargs if x not in SIMD_INTEL_FEATURE_TOWER and x not in SIMD_NEON_FLAGS] - - state.has_dash_c = '-c' in newargs or '--precompile' in newargs - state.has_dash_S = '-S' in newargs - state.has_dash_E = '-E' in newargs - - if options.post_link: - state.mode = Mode.POST_LINK_ONLY - elif state.has_dash_E or '-M' in newargs or '-MM' in newargs or '-fsyntax-only' in newargs: - state.mode = Mode.PREPROCESS_ONLY - elif has_header_inputs: - state.mode = Mode.PCH - elif state.has_dash_c or state.has_dash_S: - state.mode = Mode.COMPILE_ONLY - - if state.mode in (Mode.COMPILE_ONLY, Mode.PREPROCESS_ONLY): - for key in user_settings: - if key not in COMPILE_TIME_SETTINGS: - diagnostics.warning( - 'unused-command-line-argument', - "linker setting ignored during compilation: '%s'" % key) - for arg in state.orig_args: - if arg in LINK_ONLY_FLAGS: - diagnostics.warning( - 'unused-command-line-argument', - "linker flag ignored during compilation: '%s'" % arg) - if state.has_dash_c: - if '-emit-llvm' in newargs: - options.default_object_extension = '.bc' - elif state.has_dash_S: - if '-emit-llvm' in newargs: - options.default_object_extension = '.ll' - else: - options.default_object_extension = '.s' - elif '-M' in newargs or '-MM' in newargs: - options.default_object_extension = '.mout' # not bitcode, not js; but just dependency rule of the input file - - if options.output_file and len(input_files) > 1: - exit_with_error('cannot specify -o with -c/-S/-E/-M and multiple source files') - else: - for arg in state.orig_args: - if any(arg.startswith(f) for f in COMPILE_ONLY_FLAGS): - diagnostics.warning( - 'unused-command-line-argument', - "compiler flag ignored during linking: '%s'" % arg) - - if settings.MAIN_MODULE or settings.SIDE_MODULE: - settings.RELOCATABLE = 1 - - if 'USE_PTHREADS' in user_settings: - settings.PTHREADS = settings.USE_PTHREADS - - # Pthreads and Wasm Workers require targeting shared Wasm memory (SAB). - if settings.PTHREADS or settings.WASM_WORKERS: - settings.SHARED_MEMORY = 1 - - if settings.PTHREADS and '-pthread' not in newargs: - newargs += ['-pthread'] - elif settings.SHARED_MEMORY: - if '-matomics' not in newargs: - newargs += ['-matomics'] - if '-mbulk-memory' not in newargs: - newargs += ['-mbulk-memory'] - - if 'DISABLE_EXCEPTION_CATCHING' in user_settings and 'EXCEPTION_CATCHING_ALLOWED' in user_settings: - # If we get here then the user specified both DISABLE_EXCEPTION_CATCHING and EXCEPTION_CATCHING_ALLOWED - # on the command line. This is no longer valid so report either an error or a warning (for - # backwards compat with the old `DISABLE_EXCEPTION_CATCHING=2` - if user_settings['DISABLE_EXCEPTION_CATCHING'] in ('0', '2'): - diagnostics.warning('deprecated', 'DISABLE_EXCEPTION_CATCHING=X is no longer needed when specifying EXCEPTION_CATCHING_ALLOWED') - else: - exit_with_error('DISABLE_EXCEPTION_CATCHING and EXCEPTION_CATCHING_ALLOWED are mutually exclusive') - - if settings.EXCEPTION_CATCHING_ALLOWED: - settings.DISABLE_EXCEPTION_CATCHING = 0 - - if settings.WASM_EXCEPTIONS: - if user_settings.get('DISABLE_EXCEPTION_CATCHING') == '0': - exit_with_error('DISABLE_EXCEPTION_CATCHING=0 is not compatible with -fwasm-exceptions') - if user_settings.get('DISABLE_EXCEPTION_THROWING') == '0': - exit_with_error('DISABLE_EXCEPTION_THROWING=0 is not compatible with -fwasm-exceptions') - # -fwasm-exceptions takes care of enabling them, so users aren't supposed to - # pass them explicitly, regardless of their values - if 'DISABLE_EXCEPTION_CATCHING' in user_settings or 'DISABLE_EXCEPTION_THROWING' in user_settings: - diagnostics.warning('emcc', 'You no longer need to pass DISABLE_EXCEPTION_CATCHING or DISABLE_EXCEPTION_THROWING when using Wasm exceptions') - settings.DISABLE_EXCEPTION_CATCHING = 1 - settings.DISABLE_EXCEPTION_THROWING = 1 - - if user_settings.get('ASYNCIFY') == '1': - diagnostics.warning('emcc', 'ASYNCIFY=1 is not compatible with -fwasm-exceptions. Parts of the program that mix ASYNCIFY and exceptions will not compile.') - - if user_settings.get('SUPPORT_LONGJMP') == 'emscripten': - exit_with_error('SUPPORT_LONGJMP=emscripten is not compatible with -fwasm-exceptions') - - if settings.DISABLE_EXCEPTION_THROWING and not settings.DISABLE_EXCEPTION_CATCHING: - exit_with_error("DISABLE_EXCEPTION_THROWING was set (probably from -fno-exceptions) but is not compatible with enabling exception catching (DISABLE_EXCEPTION_CATCHING=0). If you don't want exceptions, set DISABLE_EXCEPTION_CATCHING to 1; if you do want exceptions, don't link with -fno-exceptions") - - if settings.MEMORY64: - diagnostics.warning('experimental', '-sMEMORY64 is still experimental. Many features may not work.') - - # Wasm SjLj cannot be used with Emscripten EH - if settings.SUPPORT_LONGJMP == 'wasm': - # DISABLE_EXCEPTION_THROWING is 0 by default for Emscripten EH throwing, but - # Wasm SjLj cannot be used with Emscripten EH. We error out if - # DISABLE_EXCEPTION_THROWING=0 is explicitly requested by the user; - # otherwise we disable it here. - if user_settings.get('DISABLE_EXCEPTION_THROWING') == '0': - exit_with_error('SUPPORT_LONGJMP=wasm cannot be used with DISABLE_EXCEPTION_THROWING=0') - # We error out for DISABLE_EXCEPTION_CATCHING=0, because it is 1 by default - # and this can be 0 only if the user specifies so. - if user_settings.get('DISABLE_EXCEPTION_CATCHING') == '0': - exit_with_error('SUPPORT_LONGJMP=wasm cannot be used with DISABLE_EXCEPTION_CATCHING=0') - default_setting('DISABLE_EXCEPTION_THROWING', 1) - - # SUPPORT_LONGJMP=1 means the default SjLj handling mechanism, which is 'wasm' - # if Wasm EH is used and 'emscripten' otherwise. - if settings.SUPPORT_LONGJMP == 1: - if settings.WASM_EXCEPTIONS: - settings.SUPPORT_LONGJMP = 'wasm' - else: - settings.SUPPORT_LONGJMP = 'emscripten' - - return (newargs, input_files) - - -def setup_pthreads(target): - if settings.RELOCATABLE: - # phtreads + dyanmic linking has certain limitations - if settings.SIDE_MODULE: - diagnostics.warning('experimental', '-sSIDE_MODULE + pthreads is experimental') - elif settings.MAIN_MODULE: - diagnostics.warning('experimental', '-sMAIN_MODULE + pthreads is experimental') - elif settings.LINKABLE: - diagnostics.warning('experimental', '-sLINKABLE + pthreads is experimental') - if settings.ALLOW_MEMORY_GROWTH: - diagnostics.warning('pthreads-mem-growth', '-pthread + ALLOW_MEMORY_GROWTH may run non-wasm code slowly, see https://github.com/WebAssembly/design/issues/1271') - - default_setting('DEFAULT_PTHREAD_STACK_SIZE', settings.STACK_SIZE) - - # Functions needs to be exported from the module since they are used in worker.js - settings.REQUIRED_EXPORTS += [ - 'emscripten_dispatch_to_thread_', - '_emscripten_thread_free_data', - 'emscripten_main_runtime_thread_id', - 'emscripten_main_thread_process_queued_calls', - '_emscripten_run_in_main_runtime_thread_js', - 'emscripten_stack_set_limits', - ] - - if settings.MAIN_MODULE: - settings.REQUIRED_EXPORTS += [ - '_emscripten_dlsync_self', - '_emscripten_dlsync_self_async', - '_emscripten_proxy_dlsync', - '_emscripten_proxy_dlsync_async', - '__dl_seterr', - ] - - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += [ - '$exitOnMainThread', - ] - # Some symbols are required by worker.js. - # Because emitDCEGraph only considers the main js file, and not worker.js - # we have explicitly mark these symbols as user-exported so that they will - # kept alive through DCE. - # TODO: Find a less hacky way to do this, perhaps by also scanning worker.js - # for roots. - worker_imports = [ - '__emscripten_thread_init', - '__emscripten_thread_exit', - '__emscripten_thread_crashed', - '__emscripten_thread_mailbox_await', - '__emscripten_tls_init', - '_pthread_self', - 'checkMailbox', - ] - settings.EXPORTED_FUNCTIONS += worker_imports - building.user_requested_exports.update(worker_imports) - - # set location of worker.js - settings.PTHREAD_WORKER_FILE = unsuffixed_basename(target) + '.worker.js' - - if settings.MINIMAL_RUNTIME: - building.user_requested_exports.add('exit') - - # All proxying async backends will need this. - if settings.WASMFS: - settings.REQUIRED_EXPORTS += ['emscripten_proxy_finish'] - # TODO: Remove this once we no longer need the heartbeat hack in - # wasmfs/thread_utils.h - settings.REQUIRED_EXPORTS += ['emscripten_proxy_execute_queue'] - - # pthread stack setup and other necessary utilities - def include_and_export(name): - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$' + name] - settings.EXPORTED_FUNCTIONS += [name] - - include_and_export('establishStackSpace') - include_and_export('invokeEntryPoint') - include_and_export('PThread') - if not settings.MINIMAL_RUNTIME: - # keepRuntimeAlive does not apply to MINIMAL_RUNTIME. - settings.EXPORTED_RUNTIME_METHODS += ['keepRuntimeAlive', 'ExitStatus', 'wasmMemory'] - - if settings.MODULARIZE: - if not settings.EXPORT_ES6 and settings.EXPORT_NAME == 'Module': - exit_with_error('pthreads + MODULARIZE currently require you to set -sEXPORT_NAME=Something (see settings.js) to Something != Module, so that the .worker.js file can work') - - # MODULARIZE+PTHREADS mode requires extra exports out to Module so that worker.js - # can access them: - - # general threading variables: - settings.EXPORTED_RUNTIME_METHODS += ['PThread'] - - # To keep code size to minimum, MINIMAL_RUNTIME does not utilize the global ExitStatus - # object, only regular runtime has it. - if not settings.MINIMAL_RUNTIME: - settings.EXPORTED_RUNTIME_METHODS += ['ExitStatus'] - - -@ToolchainProfiler.profile_block('linker_setup') -def phase_linker_setup(options, state, newargs): - autoconf = os.environ.get('EMMAKEN_JUST_CONFIGURE') or 'conftest.c' in state.orig_args or 'conftest.cpp' in state.orig_args - if autoconf: - # configure tests want a more shell-like style, where we emit return codes on exit() - settings.EXIT_RUNTIME = 1 - # use node.js raw filesystem access, to behave just like a native executable - settings.NODERAWFS = 1 - # Add `#!` line to output JS and make it executable. - options.executable = True - - system_libpath = '-L' + str(cache.get_lib_dir(absolute=True)) - add_link_flag(state, sys.maxsize, system_libpath) - - if settings.OPT_LEVEL >= 1: - default_setting('ASSERTIONS', 0) - - if options.emrun: - options.pre_js.append(utils.path_from_root('src/emrun_prejs.js')) - options.post_js.append(utils.path_from_root('src/emrun_postjs.js')) - # emrun mode waits on program exit - settings.EXIT_RUNTIME = 1 - - if options.cpu_profiler: - options.post_js.append(utils.path_from_root('src/cpuprofiler.js')) - - if not settings.RUNTIME_DEBUG: - settings.RUNTIME_DEBUG = (settings.LIBRARY_DEBUG or - settings.GL_DEBUG or - settings.DYLINK_DEBUG or - settings.OPENAL_DEBUG or - settings.SYSCALL_DEBUG or - settings.WEBSOCKET_DEBUG or - settings.SOCKET_DEBUG or - settings.FETCH_DEBUG or - settings.EXCEPTION_DEBUG or - settings.PTHREADS_DEBUG or - settings.ASYNCIFY_DEBUG) - - if options.memory_profiler: - settings.MEMORYPROFILER = 1 - - if settings.PTHREADS_PROFILING: - if not settings.ASSERTIONS: - exit_with_error('PTHREADS_PROFILING only works with ASSERTIONS enabled') - options.post_js.append(utils.path_from_root('src/threadprofiler.js')) - - options.extern_pre_js = read_js_files(options.extern_pre_js) - options.extern_post_js = read_js_files(options.extern_post_js) - - # TODO: support source maps with js_transform - if options.js_transform and settings.GENERATE_SOURCE_MAP: - logger.warning('disabling source maps because a js transform is being done') - settings.GENERATE_SOURCE_MAP = 0 - - # options.output_file is the user-specified one, target is what we will generate - if options.output_file: - target = options.output_file - # check for the existence of the output directory now, to avoid having - # to do so repeatedly when each of the various output files (.mem, .wasm, - # etc) are written. This gives a more useful error message than the - # IOError and python backtrace that users would otherwise see. - dirname = os.path.dirname(target) - if dirname and not os.path.isdir(dirname): - exit_with_error("specified output file (%s) is in a directory that does not exist" % target) - elif autoconf: - # Autoconf expects the executable output file to be called `a.out` - target = 'a.out' - elif settings.SIDE_MODULE: - target = 'a.out.wasm' - else: - target = 'a.out.js' - - final_suffix = get_file_suffix(target) - - if settings.EXTRA_EXPORTED_RUNTIME_METHODS: - diagnostics.warning('deprecated', 'EXTRA_EXPORTED_RUNTIME_METHODS is deprecated, please use EXPORTED_RUNTIME_METHODS instead') - settings.EXPORTED_RUNTIME_METHODS += settings.EXTRA_EXPORTED_RUNTIME_METHODS - - # If no output format was specified we try to deduce the format based on - # the output filename extension - if not options.oformat and (options.relocatable or (options.shared and not settings.SIDE_MODULE)): - # Until we have a better story for actually producing runtime shared libraries - # we support a compatibility mode where shared libraries are actually just - # object files linked with `wasm-ld --relocatable` or `llvm-link` in the case - # of LTO. - if final_suffix in EXECUTABLE_ENDINGS: - diagnostics.warning('emcc', '-shared/-r used with executable output suffix. This behaviour is deprecated. Please remove -shared/-r to build an executable or avoid the executable suffix (%s) when building object files.' % final_suffix) - else: - if options.shared: - diagnostics.warning('emcc', 'linking a library with `-shared` will emit a static object file. This is a form of emulation to support existing build systems. If you want to build a runtime shared library use the SIDE_MODULE setting.') - options.oformat = OFormat.OBJECT - - if not options.oformat: - if settings.SIDE_MODULE or final_suffix == '.wasm': - options.oformat = OFormat.WASM - elif final_suffix == '.mjs': - options.oformat = OFormat.MJS - elif final_suffix == '.html': - options.oformat = OFormat.HTML - else: - options.oformat = OFormat.JS - - if options.oformat == OFormat.MJS: - settings.EXPORT_ES6 = 1 - settings.MODULARIZE = 1 - - if options.oformat in (OFormat.WASM, OFormat.BARE): - # If the user asks directly for a wasm file then this *is* the target - wasm_target = target - else: - # Otherwise the wasm file is produced alongside the final target. - wasm_target = get_secondary_target(target, '.wasm') - - if settings.SAFE_HEAP not in [0, 1, 2]: - exit_with_error('emcc: SAFE_HEAP must be 0, 1 or 2') - - if not settings.WASM: - # When the user requests non-wasm output, we enable wasm2js. that is, - # we still compile to wasm normally, but we compile the final output - # to js. - settings.WASM = 1 - settings.WASM2JS = 1 - if settings.WASM == 2: - # Requesting both Wasm and Wasm2JS support - settings.WASM2JS = 1 - - if (options.oformat == OFormat.WASM or settings.PURE_WASI) and not settings.SIDE_MODULE: - # if the output is just a wasm file, it will normally be a standalone one, - # as there is no JS. an exception are side modules, as we can't tell at - # compile time whether JS will be involved or not - the main module may - # have JS, and the side module is expected to link against that. - # we also do not support standalone mode in fastcomp. - settings.STANDALONE_WASM = 1 - - if settings.LZ4: - settings.EXPORTED_RUNTIME_METHODS += ['LZ4'] - - if settings.WASM2C: - # wasm2c only makes sense with standalone wasm - there will be no JS, - # just wasm and then C - settings.STANDALONE_WASM = 1 - # wasm2c doesn't need any special handling of i64, we have proper i64 - # handling on the FFI boundary, which is exactly like the case of JS with - # BigInt support - settings.WASM_BIGINT = 1 - - if options.no_entry: - settings.EXPECT_MAIN = 0 - elif settings.STANDALONE_WASM: - if '_main' in settings.EXPORTED_FUNCTIONS: - # TODO(sbc): Make this into a warning? - logger.debug('including `_main` in EXPORTED_FUNCTIONS is not necessary in standalone mode') - else: - # In normal non-standalone mode we have special handling of `_main` in EXPORTED_FUNCTIONS. - # 1. If the user specifies exports, but doesn't include `_main` we assume they want to build a - # reactor. - # 2. If the user doesn't export anything we default to exporting `_main` (unless `--no-entry` - # is specified (see above). - if 'EXPORTED_FUNCTIONS' in user_settings: - if '_main' not in settings.USER_EXPORTED_FUNCTIONS: - settings.EXPECT_MAIN = 0 - else: - assert not settings.EXPORTED_FUNCTIONS - settings.EXPORTED_FUNCTIONS = ['_main'] - - if settings.STANDALONE_WASM: - # In STANDALONE_WASM mode we either build a command or a reactor. - # See https://github.com/WebAssembly/WASI/blob/main/design/application-abi.md - # For a command we always want EXIT_RUNTIME=1 - # For a reactor we always want EXIT_RUNTIME=0 - if 'EXIT_RUNTIME' in user_settings: - exit_with_error('Explicitly setting EXIT_RUNTIME not compatible with STANDALONE_WASM. EXIT_RUNTIME will always be True for programs (with a main function) and False for reactors (not main function).') - settings.EXIT_RUNTIME = settings.EXPECT_MAIN - settings.IGNORE_MISSING_MAIN = 0 - # the wasm must be runnable without the JS, so there cannot be anything that - # requires JS legalization - settings.LEGALIZE_JS_FFI = 0 - if 'MEMORY_GROWTH_LINEAR_STEP' in user_settings: - exit_with_error('MEMORY_GROWTH_LINEAR_STEP is not compatible with STANDALONE_WASM') - if 'MEMORY_GROWTH_GEOMETRIC_CAP' in user_settings: - exit_with_error('MEMORY_GROWTH_GEOMETRIC_CAP is not compatible with STANDALONE_WASM') - if settings.MINIMAL_RUNTIME: - exit_with_error('MINIMAL_RUNTIME reduces JS size, and is incompatible with STANDALONE_WASM which focuses on ignoring JS anyhow and being 100% wasm') - - # Note the exports the user requested - building.user_requested_exports.update(settings.EXPORTED_FUNCTIONS) - - if '_main' in settings.EXPORTED_FUNCTIONS: - settings.EXPORT_IF_DEFINED.append('__main_argc_argv') - - if settings.ASSERTIONS: - # Exceptions are thrown with a stack trace by default when ASSERTIONS is - # set and when building with either -fexceptions or -fwasm-exceptions. - if 'EXCEPTION_STACK_TRACES' in user_settings and not settings.EXCEPTION_STACK_TRACES: - exit_with_error('EXCEPTION_STACK_TRACES cannot be disabled when ASSERTIONS are enabled') - if settings.WASM_EXCEPTIONS or not settings.DISABLE_EXCEPTION_CATCHING: - settings.EXCEPTION_STACK_TRACES = 1 - - # -sASSERTIONS implies basic stack overflow checks, and ASSERTIONS=2 - # implies full stack overflow checks. However, we don't set this default in - # PURE_WASI, or when we are linking without standard libraries because - # STACK_OVERFLOW_CHECK depends on emscripten_stack_get_end which is defined - # in libcompiler-rt. - if not settings.PURE_WASI and '-nostdlib' not in newargs and '-nodefaultlibs' not in newargs: - default_setting('STACK_OVERFLOW_CHECK', max(settings.ASSERTIONS, settings.STACK_OVERFLOW_CHECK)) - - # For users that opt out of WARN_ON_UNDEFINED_SYMBOLS we assume they also - # want to opt out of ERROR_ON_UNDEFINED_SYMBOLS. - if user_settings.get('WARN_ON_UNDEFINED_SYMBOLS') == '0': - default_setting('ERROR_ON_UNDEFINED_SYMBOLS', 0) - - # It is unlikely that developers targeting "native web" APIs with MINIMAL_RUNTIME need - # errno support by default. - if settings.MINIMAL_RUNTIME: - default_setting('SUPPORT_ERRNO', 0) - # Require explicit -lfoo.js flags to link with JS libraries. - default_setting('AUTO_JS_LIBRARIES', 0) - # When using MINIMAL_RUNTIME, symbols should only be exported if requested. - default_setting('EXPORT_KEEPALIVE', 0) - - if settings.STRICT_JS and (settings.MODULARIZE or settings.EXPORT_ES6): - exit_with_error("STRICT_JS doesn't work with MODULARIZE or EXPORT_ES6") - - if settings.STRICT: - if not settings.MODULARIZE and not settings.EXPORT_ES6: - default_setting('STRICT_JS', 1) - default_setting('AUTO_JS_LIBRARIES', 0) - default_setting('AUTO_NATIVE_LIBRARIES', 0) - default_setting('AUTO_ARCHIVE_INDEXES', 0) - default_setting('IGNORE_MISSING_MAIN', 0) - default_setting('ALLOW_UNIMPLEMENTED_SYSCALLS', 0) - - if 'GLOBAL_BASE' not in user_settings and not settings.SHRINK_LEVEL and not settings.OPT_LEVEL: - # When optimizing for size it helps to put static data first before - # the stack (sincs this makes instructions for accessing this data - # use a smaller LEB encoding). - # However, for debugability is better to have the stack come first - # (becuase stack overflows will trap rather than corrupting data). - settings.STACK_FIRST = True - - # Default to TEXTDECODER=2 (always use TextDecoder to decode UTF-8 strings) - # in -Oz builds, since custom decoder for UTF-8 takes up space. - # In pthreads enabled builds, TEXTDECODER==2 may not work, see - # https://github.com/whatwg/encoding/issues/172 - # When supporting shell environments, do not do this as TextDecoder is not - # widely supported there. - if settings.SHRINK_LEVEL >= 2 and not settings.SHARED_MEMORY and \ - not settings.ENVIRONMENT_MAY_BE_SHELL: - default_setting('TEXTDECODER', 2) - - # If set to 1, we will run the autodebugger (the automatic debugging tool, see - # tools/autodebugger). Note that this will disable inclusion of libraries. This - # is useful because including dlmalloc makes it hard to compare native and js - # builds - if os.environ.get('EMCC_AUTODEBUG'): - settings.AUTODEBUG = 1 - - # Use settings - - if settings.DEBUG_LEVEL > 1 and options.use_closure_compiler: - diagnostics.warning('emcc', 'disabling closure because debug info was requested') - options.use_closure_compiler = False - - if settings.WASM == 2 and settings.SINGLE_FILE: - exit_with_error('cannot have both WASM=2 and SINGLE_FILE enabled at the same time') - - if settings.SEPARATE_DWARF and settings.WASM2JS: - exit_with_error('cannot have both SEPARATE_DWARF and WASM2JS at the same time (as there is no wasm file)') - - if settings.MINIMAL_RUNTIME_STREAMING_WASM_COMPILATION and settings.MINIMAL_RUNTIME_STREAMING_WASM_INSTANTIATION: - exit_with_error('MINIMAL_RUNTIME_STREAMING_WASM_COMPILATION and MINIMAL_RUNTIME_STREAMING_WASM_INSTANTIATION are mutually exclusive!') - - if options.emrun: - if settings.MINIMAL_RUNTIME: - exit_with_error('--emrun is not compatible with MINIMAL_RUNTIME') - - if options.use_closure_compiler: - settings.USE_CLOSURE_COMPILER = 1 - - if 'CLOSURE_WARNINGS' in user_settings: - if settings.CLOSURE_WARNINGS not in ['quiet', 'warn', 'error']: - exit_with_error('Invalid option -sCLOSURE_WARNINGS=%s specified! Allowed values are "quiet", "warn" or "error".' % settings.CLOSURE_WARNINGS) - - diagnostics.warning('deprecated', 'CLOSURE_WARNINGS is deprecated, use -Wclosure/-Wno-closure instread') - closure_warnings = diagnostics.manager.warnings['closure'] - if settings.CLOSURE_WARNINGS == 'error': - closure_warnings['error'] = True - closure_warnings['enabled'] = True - elif settings.CLOSURE_WARNINGS == 'warn': - closure_warnings['error'] = False - closure_warnings['enabled'] = True - elif settings.CLOSURE_WARNINGS == 'quiet': - closure_warnings['error'] = False - closure_warnings['enabled'] = False - - if not settings.MINIMAL_RUNTIME: - if not settings.BOOTSTRAPPING_STRUCT_INFO: - if settings.DYNCALLS: - # Include dynCall() function by default in DYNCALLS builds in classic runtime; in MINIMAL_RUNTIME, must add this explicitly. - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$dynCall'] - - if settings.ASSERTIONS: - # "checkUnflushedContent()" and "missingLibrarySymbol()" depend on warnOnce - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$warnOnce'] - - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$getValue', '$setValue'] - - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$ExitStatus'] - - if not settings.BOOTSTRAPPING_STRUCT_INFO and settings.SAFE_HEAP: - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$getValue_safe', '$setValue_safe'] - - if settings.MAIN_MODULE: - assert not settings.SIDE_MODULE - if settings.MAIN_MODULE == 1: - settings.INCLUDE_FULL_LIBRARY = 1 - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$loadDylibs'] - - # If we are including the entire JS library then we know for sure we will, by definition, - # require all the reverse dependencies. - if settings.INCLUDE_FULL_LIBRARY: - default_setting('REVERSE_DEPS', 'all') - - if settings.MAIN_MODULE == 1 or settings.SIDE_MODULE == 1: - settings.LINKABLE = 1 - - if settings.LINKABLE and settings.USER_EXPORTED_FUNCTIONS: - diagnostics.warning('unused-command-line-argument', 'EXPORTED_FUNCTIONS is not valid with LINKABLE set (normally due to SIDE_MODULE=1/MAIN_MODULE=1) since all functions are exported this mode. To export only a subset use SIDE_MODULE=2/MAIN_MODULE=2') - - if settings.MAIN_MODULE: - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += [ - '$getDylinkMetadata', - '$mergeLibSymbols', - ] - - if settings.PTHREADS: - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += [ - '$registerTLSInit', - ] - - if settings.RELOCATABLE: - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += [ - '$reportUndefinedSymbols', - '$relocateExports', - '$GOTHandler', - '__heap_base', - '__stack_pointer', - ] - - if settings.ASYNCIFY: - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += [ - '__asyncify_state', - '__asyncify_data' - ] - - # Emscripten EH dependency in library_dylink.js - if settings.SUPPORT_LONGJMP == 'emscripten' or not settings.DISABLE_EXCEPTION_CATCHING: - settings.REQUIRED_EXPORTS += ['setThrew'] - - if settings.MINIMAL_RUNTIME: - exit_with_error('MINIMAL_RUNTIME is not compatible with relocatable output') - if settings.WASM2JS: - exit_with_error('WASM2JS is not compatible with relocatable output') - # shared modules need memory utilities to allocate their memory - settings.ALLOW_TABLE_GROWTH = 1 - - # various settings require sbrk() access - if settings.DETERMINISTIC or \ - settings.EMSCRIPTEN_TRACING or \ - settings.SAFE_HEAP or \ - settings.MEMORYPROFILER: - settings.REQUIRED_EXPORTS += ['sbrk'] - - if settings.MEMORYPROFILER: - settings.REQUIRED_EXPORTS += ['__heap_base', - 'emscripten_stack_get_base', - 'emscripten_stack_get_end', - 'emscripten_stack_get_current'] - - if settings.ASYNCIFY_LAZY_LOAD_CODE: - settings.ASYNCIFY = 1 - - if settings.ASYNCIFY == 1: - # See: https://github.com/emscripten-core/emscripten/issues/12065 - # See: https://github.com/emscripten-core/emscripten/issues/12066 - settings.DYNCALLS = 1 - settings.REQUIRED_EXPORTS += ['emscripten_stack_get_base', - 'emscripten_stack_get_end', - 'emscripten_stack_set_limits'] - - settings.ASYNCIFY_ADD = unmangle_symbols_from_cmdline(settings.ASYNCIFY_ADD) - settings.ASYNCIFY_REMOVE = unmangle_symbols_from_cmdline(settings.ASYNCIFY_REMOVE) - settings.ASYNCIFY_ONLY = unmangle_symbols_from_cmdline(settings.ASYNCIFY_ONLY) - - if settings.EMULATE_FUNCTION_POINTER_CASTS: - # Emulated casts forces a wasm ABI of (i64, i64, ...) in the table, which - # means all table functions are illegal for JS to call directly. Use - # dyncalls which call into the wasm, which then does an indirect call. - settings.DYNCALLS = 1 - - if options.oformat != OFormat.OBJECT and final_suffix in ('.o', '.bc', '.so', '.dylib') and not settings.SIDE_MODULE: - diagnostics.warning('emcc', 'object file output extension (%s) used for non-object output. If you meant to build an object file please use `-c, `-r`, or `-shared`' % final_suffix) - - if settings.SUPPORT_BIG_ENDIAN: - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += [ - '$LE_HEAP_STORE_U16', - '$LE_HEAP_STORE_I16', - '$LE_HEAP_STORE_U32', - '$LE_HEAP_STORE_I32', - '$LE_HEAP_STORE_F32', - '$LE_HEAP_STORE_F64', - '$LE_HEAP_LOAD_U16', - '$LE_HEAP_LOAD_I16', - '$LE_HEAP_LOAD_U32', - '$LE_HEAP_LOAD_I32', - '$LE_HEAP_LOAD_F32', - '$LE_HEAP_LOAD_F64' - ] - - if settings.RUNTIME_DEBUG or settings.ASSERTIONS or settings.STACK_OVERFLOW_CHECK or settings.PTHREADS_PROFILING or settings.GL_ASSERTIONS: - # Lots of code in debug/assertion blocks uses ptrToString. - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$ptrToString'] - - if settings.STACK_OVERFLOW_CHECK: - settings.REQUIRED_EXPORTS += [ - 'emscripten_stack_get_end', - 'emscripten_stack_get_free', - 'emscripten_stack_get_base', - 'emscripten_stack_get_current', - ] - - # We call one of these two functions during startup which caches the stack limits - # in wasm globals allowing get_base/get_free to be super fast. - # See compiler-rt/stack_limits.S. - if settings.RELOCATABLE: - settings.REQUIRED_EXPORTS += ['emscripten_stack_set_limits'] - else: - settings.REQUIRED_EXPORTS += ['emscripten_stack_init'] - - if settings.MODULARIZE: - if settings.PROXY_TO_WORKER: - exit_with_error('-sMODULARIZE is not compatible with --proxy-to-worker (if you want to run in a worker with -sMODULARIZE, you likely want to do the worker side setup manually)') - # in MINIMAL_RUNTIME we may not need to emit the Promise code, as the - # HTML output creates a singleton instance, and it does so without the - # Promise. However, in Pthreads mode the Promise is used for worker - # creation. - if settings.MINIMAL_RUNTIME and options.oformat == OFormat.HTML and not settings.PTHREADS: - settings.EXPORT_READY_PROMISE = 0 - - if settings.LEGACY_VM_SUPPORT: - if settings.WASM2JS: - settings.POLYFILL_OLD_MATH_FUNCTIONS = 1 - - # Support all old browser versions - settings.MIN_FIREFOX_VERSION = 0 - settings.MIN_SAFARI_VERSION = 0 - settings.MIN_IE_VERSION = 0 - settings.MIN_EDGE_VERSION = 0 - settings.MIN_CHROME_VERSION = 0 - settings.MIN_NODE_VERSION = 0 - - if settings.MIN_CHROME_VERSION <= 37: - settings.WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG = 1 - - # 10.19.0 is the oldest version of node that we do any testing with. - # Keep this in sync with the test-node-compat in .circleci/config.yml - # and MINIMUM_NODE_VERSION in tools/shared.py - if settings.MIN_NODE_VERSION: - if settings.MIN_NODE_VERSION < 101900: - exit_with_error('targeting node older than 10.19.00 is not supported') - if settings.MIN_NODE_VERSION >= 150000: - default_setting('NODEJS_CATCH_REJECTION', 0) - - # Do not catch rejections or exits in modularize mode, as these options - # are for use when running emscripten modules standalone - # see https://github.com/emscripten-core/emscripten/issues/18723#issuecomment-1429236996 - if settings.MODULARIZE: - default_setting('NODEJS_CATCH_REJECTION', 0) - default_setting('NODEJS_CATCH_EXIT', 0) - if settings.NODEJS_CATCH_REJECTION or settings.NODEJS_CATCH_EXIT: - exit_with_error('Cannot use -sNODEJS_CATCH_REJECTION or -sNODEJS_CATCH_EXIT with -sMODULARIZE') - - setup_environment_settings() - - if options.use_closure_compiler != 0: - # Emscripten requires certain ES6 constructs by default in library code - # - https://caniuse.com/let : EDGE:12 FF:44 CHROME:49 SAFARI:11 - # - https://caniuse.com/const : EDGE:12 FF:36 CHROME:49 SAFARI:11 - # - https://caniuse.com/arrow-functions: : EDGE:12 FF:22 CHROME:45 SAFARI:10 - # - https://caniuse.com/mdn-javascript_builtins_object_assign: - # EDGE:12 FF:34 CHROME:45 SAFARI:9 - # Taking the highest requirements gives is our minimum: - # Max Version: EDGE:12 FF:44 CHROME:49 SAFARI:11 - settings.TRANSPILE_TO_ES5 = (settings.MIN_EDGE_VERSION < 12 or - settings.MIN_FIREFOX_VERSION < 44 or - settings.MIN_CHROME_VERSION < 49 or - settings.MIN_SAFARI_VERSION < 110000 or - settings.MIN_IE_VERSION != 0x7FFFFFFF) - - if options.use_closure_compiler is None and settings.TRANSPILE_TO_ES5: - diagnostics.warning('transpile', 'enabling transpilation via closure due to browser version settings. This warning can be suppressed by passing `--closure=1` or `--closure=0` to opt into our explicitly.') - - # https://caniuse.com/class: EDGE:13 FF:45 CHROME:49 SAFARI:9 - supports_es6_classes = (settings.MIN_EDGE_VERSION >= 13 and - settings.MIN_FIREFOX_VERSION >= 45 and - settings.MIN_CHROME_VERSION >= 49 and - settings.MIN_SAFARI_VERSION >= 90000 and - settings.MIN_IE_VERSION == 0x7FFFFFFF) - - if not settings.DISABLE_EXCEPTION_CATCHING and settings.EXCEPTION_STACK_TRACES and not supports_es6_classes: - diagnostics.warning('transpile', '-sEXCEPTION_STACK_TRACES requires an engine that support ES6 classes.') - settings.EXCEPTION_STACK_TRACES = 0 - - # Silently drop any individual backwards compatibility emulation flags that are known never to occur on browsers that support WebAssembly. - if not settings.WASM2JS: - settings.POLYFILL_OLD_MATH_FUNCTIONS = 0 - settings.WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG = 0 - - if settings.STB_IMAGE and final_suffix in EXECUTABLE_ENDINGS: - state.forced_stdlibs.append('libstb_image') - settings.EXPORTED_FUNCTIONS += ['_stbi_load', '_stbi_load_from_memory', '_stbi_image_free'] - - if settings.USE_WEBGL2: - settings.MAX_WEBGL_VERSION = 2 - - # MIN_WEBGL_VERSION=2 implies MAX_WEBGL_VERSION=2 - if settings.MIN_WEBGL_VERSION == 2: - default_setting('MAX_WEBGL_VERSION', 2) - - if settings.MIN_WEBGL_VERSION > settings.MAX_WEBGL_VERSION: - exit_with_error('MIN_WEBGL_VERSION must be smaller or equal to MAX_WEBGL_VERSION!') - - if not settings.GL_SUPPORT_SIMPLE_ENABLE_EXTENSIONS and settings.GL_SUPPORT_AUTOMATIC_ENABLE_EXTENSIONS: - exit_with_error('-sGL_SUPPORT_SIMPLE_ENABLE_EXTENSIONS=0 only makes sense with -sGL_SUPPORT_AUTOMATIC_ENABLE_EXTENSIONS=0!') - - if settings.WASMFS: - state.forced_stdlibs.append('libwasmfs') - settings.FILESYSTEM = 0 - settings.SYSCALLS_REQUIRE_FILESYSTEM = 0 - settings.JS_LIBRARIES.append((0, 'library_wasmfs.js')) - settings.REQUIRED_EXPORTS += ['_wasmfs_read_file'] - if settings.MAIN_MODULE: - # Dynamic library support uses JS API internals, so include it all - # TODO: rewriting more of the dynamic linking support code into wasm could - # avoid this. also, after we remove the old FS, we could write a - # more specific API for wasmfs/dynamic linking integration perhaps - settings.FORCE_FILESYSTEM = 1 - if settings.FORCE_FILESYSTEM: - # Add exports for the JS API. Like the old JS FS, WasmFS by default - # includes just what JS parts it actually needs, and FORCE_FILESYSTEM is - # required to force all of it to be included if the user wants to use the - # JS API directly. - settings.REQUIRED_EXPORTS += [ - '_wasmfs_write_file', - '_wasmfs_mkdir', - '_wasmfs_unlink', - '_wasmfs_chdir', - '_wasmfs_symlink', - '_wasmfs_chmod', - '_wasmfs_identify', - '_wasmfs_readdir_start', - '_wasmfs_readdir_get', - '_wasmfs_readdir_finish', - ] - - # Explicitly drop linking in a malloc implementation if program is not using any dynamic allocation calls. - if not settings.USES_DYNAMIC_ALLOC: - settings.MALLOC = 'none' - - if settings.FETCH and final_suffix in EXECUTABLE_ENDINGS: - state.forced_stdlibs.append('libfetch') - settings.JS_LIBRARIES.append((0, 'library_fetch.js')) - if settings.PTHREADS: - settings.FETCH_WORKER_FILE = unsuffixed_basename(target) + '.fetch.js' - - if settings.DEMANGLE_SUPPORT: - settings.REQUIRED_EXPORTS += ['__cxa_demangle'] - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$demangle', '$stackTrace'] - - if settings.FULL_ES3: - settings.FULL_ES2 = 1 - settings.MAX_WEBGL_VERSION = max(2, settings.MAX_WEBGL_VERSION) - - # WASM_SYSTEM_EXPORTS are actually native function but they are allowed to be exported - # via EXPORTED_RUNTIME_METHODS for backwards compat. - for sym in settings.WASM_SYSTEM_EXPORTS: - if sym in settings.EXPORTED_RUNTIME_METHODS: - settings.REQUIRED_EXPORTS.append(sym) - - settings.REQUIRED_EXPORTS += ['stackSave', 'stackRestore', 'stackAlloc'] - - if settings.RELOCATABLE: - # TODO(https://reviews.llvm.org/D128515): Make this mandatory once - # llvm change lands - settings.EXPORT_IF_DEFINED.append('__wasm_apply_data_relocs') - - if settings.RELOCATABLE and not settings.DYNAMIC_EXECUTION: - exit_with_error('cannot have both DYNAMIC_EXECUTION=0 and RELOCATABLE enabled at the same time, since RELOCATABLE needs to eval()') - - if settings.SIDE_MODULE and 'GLOBAL_BASE' in user_settings: - exit_with_error('GLOBAL_BASE is not compatible with SIDE_MODULE') - - if options.use_preload_plugins or len(options.preload_files) or len(options.embed_files): - if settings.NODERAWFS: - exit_with_error('--preload-file and --embed-file cannot be used with NODERAWFS which disables virtual filesystem') - # if we include any files, or intend to use preload plugins, then we definitely need filesystem support - settings.FORCE_FILESYSTEM = 1 - - if settings.PROXY_TO_WORKER or options.use_preload_plugins: - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$Browser'] - - if not settings.BOOTSTRAPPING_STRUCT_INFO: - if settings.DYNAMIC_EXECUTION == 2 and not settings.MINIMAL_RUNTIME: - # Used by makeEval in the DYNAMIC_EXECUTION == 2 case - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$stackTrace'] - - if not settings.STANDALONE_WASM and (settings.EXIT_RUNTIME or settings.ASSERTIONS): - # to flush streams on FS exit, we need to be able to call fflush - # we only include it if the runtime is exitable, or when ASSERTIONS - # (ASSERTIONS will check that streams do not need to be flushed, - # helping people see when they should have enabled EXIT_RUNTIME) - settings.EXPORT_IF_DEFINED += ['fflush'] - - if settings.SUPPORT_ERRNO: - # so setErrNo JS library function can report errno back to C - settings.REQUIRED_EXPORTS += ['__errno_location'] - - if settings.SAFE_HEAP: - # SAFE_HEAP check includes calling emscripten_get_sbrk_ptr() from wasm - settings.REQUIRED_EXPORTS += ['emscripten_get_sbrk_ptr', 'emscripten_stack_get_base'] - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$unSign'] - - if not settings.DECLARE_ASM_MODULE_EXPORTS: - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$exportAsmFunctions'] - - if settings.ALLOW_MEMORY_GROWTH: - # Setting ALLOW_MEMORY_GROWTH turns off ABORTING_MALLOC, as in that mode we default to - # the behavior of trying to grow and returning 0 from malloc on failure, like - # a standard system would. However, if the user sets the flag it - # overrides that. - default_setting('ABORTING_MALLOC', 0) - - if settings.PTHREADS: - setup_pthreads(target) - settings.JS_LIBRARIES.append((0, 'library_pthread.js')) - if settings.PROXY_TO_PTHREAD: - settings.PTHREAD_POOL_SIZE_STRICT = 0 - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$runtimeKeepalivePush'] - else: - if settings.PROXY_TO_PTHREAD: - exit_with_error('-sPROXY_TO_PTHREAD requires -pthread to work!') - settings.JS_LIBRARIES.append((0, 'library_pthread_stub.js')) - - # TODO: Move this into the library JS file once it becomes possible. - # See https://github.com/emscripten-core/emscripten/pull/15982 - if settings.INCLUDE_FULL_LIBRARY and not settings.DISABLE_EXCEPTION_CATCHING: - settings.EXPORTED_FUNCTIONS += ['___get_exception_message', '_free'] - - if settings.WASM_WORKERS: - # TODO: After #15982 is resolved, these dependencies can be declared in library_wasm_worker.js - # instead of having to record them here. - wasm_worker_imports = ['_emscripten_wasm_worker_initialize', '___set_thread_state'] - settings.EXPORTED_FUNCTIONS += wasm_worker_imports - building.user_requested_exports.update(wasm_worker_imports) - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['_wasm_worker_initializeRuntime'] - # set location of Wasm Worker bootstrap JS file - if settings.WASM_WORKERS == 1: - settings.WASM_WORKER_FILE = unsuffixed(os.path.basename(target)) + '.ww.js' - settings.JS_LIBRARIES.append((0, shared.path_from_root('src', 'library_wasm_worker.js'))) - - settings.SUPPORTS_GLOBALTHIS = feature_matrix.caniuse(feature_matrix.Feature.GLOBALTHIS) - - if settings.AUDIO_WORKLET: - if not settings.SUPPORTS_GLOBALTHIS: - exit_with_error('Must target recent enough browser versions that will support globalThis in order to target Wasm Audio Worklets!') - if settings.AUDIO_WORKLET == 1: - settings.AUDIO_WORKLET_FILE = unsuffixed(os.path.basename(target)) + '.aw.js' - settings.JS_LIBRARIES.append((0, shared.path_from_root('src', 'library_webaudio.js'))) - if not settings.MINIMAL_RUNTIME: - # MINIMAL_RUNTIME exports these manually, since this export mechanism is placed - # in global scope that is not suitable for MINIMAL_RUNTIME loader. - settings.EXPORTED_RUNTIME_METHODS += ['stackSave', 'stackAlloc', 'stackRestore'] - - if settings.FORCE_FILESYSTEM and not settings.MINIMAL_RUNTIME: - # when the filesystem is forced, we export by default methods that filesystem usage - # may need, including filesystem usage from standalone file packager output (i.e. - # file packages not built together with emcc, but that are loaded at runtime - # separately, and they need emcc's output to contain the support they need) - settings.EXPORTED_RUNTIME_METHODS += [ - 'FS_createPath', - 'FS_createDataFile', - 'FS_createPreloadedFile', - 'FS_unlink' - ] - if not settings.WASMFS: - # The old FS has some functionality that WasmFS lacks. - settings.EXPORTED_RUNTIME_METHODS += [ - 'FS_createLazyFile', - 'FS_createDevice' - ] - - settings.EXPORTED_RUNTIME_METHODS += [ - 'addRunDependency', - 'removeRunDependency', - ] - - def check_memory_setting(setting): - if settings[setting] % webassembly.WASM_PAGE_SIZE != 0: - exit_with_error(f'{setting} must be a multiple of WebAssembly page size (64KiB), was {settings[setting]}') - - check_memory_setting('INITIAL_MEMORY') - check_memory_setting('MAXIMUM_MEMORY') - if settings.INITIAL_MEMORY < settings.STACK_SIZE: - exit_with_error(f'INITIAL_MEMORY must be larger than STACK_SIZE, was {settings.INITIAL_MEMORY} (STACK_SIZE={settings.STACK_SIZE})') - if settings.MEMORY_GROWTH_LINEAR_STEP != -1: - check_memory_setting('MEMORY_GROWTH_LINEAR_STEP') - - if 'MAXIMUM_MEMORY' in user_settings and not settings.ALLOW_MEMORY_GROWTH: - diagnostics.warning('unused-command-line-argument', 'MAXIMUM_MEMORY is only meaningful with ALLOW_MEMORY_GROWTH') - - if settings.EXPORT_ES6: - if not settings.MODULARIZE: - # EXPORT_ES6 requires output to be a module - if 'MODULARIZE' in user_settings: - exit_with_error('EXPORT_ES6 requires MODULARIZE to be set') - settings.MODULARIZE = 1 - if shared.target_environment_may_be('node') and not settings.USE_ES6_IMPORT_META: - # EXPORT_ES6 + ENVIRONMENT=*node* requires the use of import.meta.url - if 'USE_ES6_IMPORT_META' in user_settings: - exit_with_error('EXPORT_ES6 and ENVIRONMENT=*node* requires USE_ES6_IMPORT_META to be set') - settings.USE_ES6_IMPORT_META = 1 - - if settings.MODULARIZE and not settings.DECLARE_ASM_MODULE_EXPORTS: - # When MODULARIZE option is used, currently requires declaring all module exports - # individually - TODO: this could be optimized - exit_with_error('DECLARE_ASM_MODULE_EXPORTS=0 is not compatible with MODULARIZE') - - # When not declaring wasm module exports in outer scope one by one, disable minifying - # wasm module export names so that the names can be passed directly to the outer scope. - # Also, if using library_exports.js API, disable minification so that the feature can work. - if not settings.DECLARE_ASM_MODULE_EXPORTS or '-lexports.js' in [x for _, x in state.link_flags]: - settings.MINIFY_WASM_EXPORT_NAMES = 0 - - if '-lembind' in [x for _, x in state.link_flags]: - settings.EMBIND = 1 - - # Enable minification of wasm imports and exports when appropriate, if we - # are emitting an optimized JS+wasm combo (then the JS knows how to load the minified names). - # Things that process the JS after this operation would be done must disable this. - # For example, ASYNCIFY_LAZY_LOAD_CODE needs to identify import names. - # ASYNCIFY=2 does not support this optimization yet as it has a hardcoded - # check for 'main' as an export name. TODO - if will_metadce() and \ - settings.OPT_LEVEL >= 2 and \ - settings.DEBUG_LEVEL <= 2 and \ - options.oformat not in (OFormat.WASM, OFormat.BARE) and \ - settings.ASYNCIFY != 2 and \ - not settings.LINKABLE and \ - not settings.STANDALONE_WASM and \ - not settings.AUTODEBUG and \ - not settings.ASSERTIONS and \ - not settings.RELOCATABLE and \ - not settings.ASYNCIFY_LAZY_LOAD_CODE and \ - settings.MINIFY_WASM_EXPORT_NAMES: - settings.MINIFY_WASM_IMPORTS_AND_EXPORTS = 1 - settings.MINIFY_WASM_IMPORTED_MODULES = 1 - - if settings.MINIMAL_RUNTIME: - # Minimal runtime uses a different default shell file - if options.shell_path == utils.path_from_root('src/shell.html'): - options.shell_path = utils.path_from_root('src/shell_minimal_runtime.html') - - if settings.ASSERTIONS: - # In ASSERTIONS-builds, functions UTF8ArrayToString() and stringToUTF8Array() (which are not JS library functions), both - # use warnOnce(), which in MINIMAL_RUNTIME is a JS library function, so explicitly have to mark dependency to warnOnce() - # in that case. If string functions are turned to library functions in the future, then JS dependency tracking can be - # used and this special directive can be dropped. - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$warnOnce'] - - if settings.MODULARIZE and not (settings.EXPORT_ES6 and not settings.SINGLE_FILE) and \ - settings.EXPORT_NAME == 'Module' and options.oformat == OFormat.HTML and \ - (options.shell_path == utils.path_from_root('src/shell.html') or options.shell_path == utils.path_from_root('src/shell_minimal.html')): - exit_with_error(f'Due to collision in variable name "Module", the shell file "{options.shell_path}" is not compatible with build options "-sMODULARIZE -sEXPORT_NAME=Module". Either provide your own shell file, change the name of the export to something else to avoid the name collision. (see https://github.com/emscripten-core/emscripten/issues/7950 for details)') - - # TODO(sbc): Remove WASM2JS here once the size regression it would introduce has been fixed. - if settings.SHARED_MEMORY or settings.RELOCATABLE or settings.ASYNCIFY_LAZY_LOAD_CODE or settings.WASM2JS: - settings.IMPORTED_MEMORY = 1 - - if settings.MEMORY64: - if settings.ASYNCIFY and settings.MEMORY64 == 1: - exit_with_error('MEMORY64=1 is not compatible with ASYNCIFY') - if not settings.DISABLE_EXCEPTION_CATCHING: - exit_with_error('MEMORY64 is not compatible with DISABLE_EXCEPTION_CATCHING=0') - # Any "pointers" passed to JS will now be i64's, in both modes. - settings.WASM_BIGINT = 1 - - if settings.WASM_BIGINT: - settings.LEGALIZE_JS_FFI = 0 - - if settings.SINGLE_FILE: - settings.GENERATE_SOURCE_MAP = 0 - - if settings.EVAL_CTORS: - if settings.WASM2JS: - # code size/memory and correctness issues TODO - exit_with_error('EVAL_CTORS is not compatible with wasm2js yet') - elif settings.RELOCATABLE: - exit_with_error('EVAL_CTORS is not compatible with relocatable yet (movable segments)') - elif settings.ASYNCIFY: - # In Asyncify exports can be called more than once, and this seems to not - # work properly yet (see test_emscripten_scan_registers). - exit_with_error('EVAL_CTORS is not compatible with asyncify yet') - - if options.use_closure_compiler == 2 and not settings.WASM2JS: - exit_with_error('closure compiler mode 2 assumes the code is asm.js, so not meaningful for wasm') - - if settings.WASM2JS: - if options.memory_init_file is None: - options.memory_init_file = settings.OPT_LEVEL >= 2 - settings.MAYBE_WASM2JS = 1 - # when using wasm2js, if the memory segments are in the wasm then they - # end up converted by wasm2js into base64 encoded JS. alternatively, we - # can use a .mem file like asm.js used to. - # generally we follow what the options tell us to do (which is to use - # a .mem file in most cases, since it is binary & compact). however, for - # shared memory builds we must keep the memory segments in the wasm as - # they will be passive segments which the .mem format cannot handle. - settings.MEM_INIT_IN_WASM = not options.memory_init_file or settings.SINGLE_FILE or settings.SHARED_MEMORY - - if ( - settings.MAYBE_WASM2JS or - settings.AUTODEBUG or - settings.LINKABLE or - settings.INCLUDE_FULL_LIBRARY or - not settings.DISABLE_EXCEPTION_CATCHING or - (settings.MAIN_MODULE == 1 and (settings.DYNCALLS or not settings.WASM_BIGINT)) - ): - settings.REQUIRED_EXPORTS += ["getTempRet0", "setTempRet0"] - - if settings.LEGALIZE_JS_FFI: - settings.REQUIRED_EXPORTS += ['__get_temp_ret', '__set_temp_ret'] - - if settings.SPLIT_MODULE and settings.ASYNCIFY == 2: - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['_load_secondary_module'] - - # wasm side modules have suffix .wasm - if settings.SIDE_MODULE and shared.suffix(target) == '.js': - diagnostics.warning('emcc', 'output suffix .js requested, but wasm side modules are just wasm files; emitting only a .wasm, no .js') - - sanitize = set() - - for arg in newargs: - if arg.startswith('-fsanitize='): - sanitize.update(arg.split('=', 1)[1].split(',')) - elif arg.startswith('-fno-sanitize='): - sanitize.difference_update(arg.split('=', 1)[1].split(',')) - - if sanitize: - settings.USE_OFFSET_CONVERTER = 1 - settings.REQUIRED_EXPORTS += [ - 'memalign', - 'emscripten_builtin_memalign', - 'emscripten_builtin_malloc', - 'emscripten_builtin_free', - ] - - if ('leak' in sanitize or 'address' in sanitize) and not settings.ALLOW_MEMORY_GROWTH: - # Increase the minimum memory requirements to account for extra memory - # that the sanitizers might need (in addition to the shadow memory - # requirements handled below). - # These values are designed be an over-estimate of the actual requirements and - # are based on experimentation with different tests/programs under asan and - # lsan. - settings.INITIAL_MEMORY += 50 * 1024 * 1024 - if settings.PTHREADS: - settings.INITIAL_MEMORY += 50 * 1024 * 1024 - - if settings.USE_OFFSET_CONVERTER and settings.WASM2JS: - exit_with_error('wasm2js is not compatible with USE_OFFSET_CONVERTER (see #14630)') - - if sanitize & UBSAN_SANITIZERS: - if '-fsanitize-minimal-runtime' in newargs: - settings.UBSAN_RUNTIME = 1 - else: - settings.UBSAN_RUNTIME = 2 - - if 'leak' in sanitize: - settings.USE_LSAN = 1 - default_setting('EXIT_RUNTIME', 1) - - if 'address' in sanitize: - settings.USE_ASAN = 1 - default_setting('EXIT_RUNTIME', 1) - if not settings.UBSAN_RUNTIME: - settings.UBSAN_RUNTIME = 2 - - # helper functions for JS to call into C to do memory operations. these - # let us sanitize memory access from the JS side, by calling into C where - # it has been instrumented. - ASAN_C_HELPERS = [ - '_asan_c_load_1', '_asan_c_load_1u', - '_asan_c_load_2', '_asan_c_load_2u', - '_asan_c_load_4', '_asan_c_load_4u', - '_asan_c_load_f', '_asan_c_load_d', - '_asan_c_store_1', '_asan_c_store_1u', - '_asan_c_store_2', '_asan_c_store_2u', - '_asan_c_store_4', '_asan_c_store_4u', - '_asan_c_store_f', '_asan_c_store_d', - ] - - settings.REQUIRED_EXPORTS += ASAN_C_HELPERS - - if settings.ASYNCIFY and not settings.ASYNCIFY_ONLY: - # we do not want asyncify to instrument these helpers - they just access - # memory as small getters/setters, so they cannot pause anyhow, and also - # we access them in the runtime as we prepare to rewind, which would hit - # an asyncify assertion, if asyncify instrumented them. - # - # note that if ASYNCIFY_ONLY was set by the user then we do not need to - # do anything (as the user's list won't contain these functions), and if - # we did add them, the pass would assert on incompatible lists, hence the - # condition in the above if. - settings.ASYNCIFY_REMOVE += ASAN_C_HELPERS - - if settings.ASAN_SHADOW_SIZE != -1: - diagnostics.warning('emcc', 'ASAN_SHADOW_SIZE is ignored and will be removed in a future release') - - if 'GLOBAL_BASE' in user_settings: - exit_with_error("ASan does not support custom GLOBAL_BASE") - - # Increase the TOTAL_MEMORY and shift GLOBAL_BASE to account for - # the ASan shadow region which starts at address zero. - # The shadow region is 1/8th the size of the total memory and is - # itself part of the total memory. - # We use the following variables in this calculation: - # - user_mem : memory usable/visible by the user program. - # - shadow_size : memory used by asan for shadow memory. - # - total_mem : the sum of the above. this is the size of the wasm memory (and must be aligned to WASM_PAGE_SIZE) - user_mem = settings.INITIAL_MEMORY - if settings.ALLOW_MEMORY_GROWTH: - user_mem = settings.MAXIMUM_MEMORY - - # Given the know value of user memory size we can work backwards - # to find the total memory and the shadow size based on the fact - # that the user memory is 7/8ths of the total memory. - # (i.e. user_mem == total_mem * 7 / 8 - total_mem = user_mem * 8 / 7 - - # But we might need to re-align to wasm page size - total_mem = int(align_to_wasm_page_boundary(total_mem)) - - # The shadow size is 1/8th the resulting rounded up size - shadow_size = total_mem // 8 - - # We start our global data after the shadow memory. - # We don't need to worry about alignment here. wasm-ld will take care of that. - settings.GLOBAL_BASE = shadow_size - settings.STACK_FIRST = False - - if not settings.ALLOW_MEMORY_GROWTH: - settings.INITIAL_MEMORY = total_mem - else: - settings.INITIAL_MEMORY += align_to_wasm_page_boundary(shadow_size) - - if settings.SAFE_HEAP: - # SAFE_HEAP instruments ASan's shadow memory accesses. - # Since the shadow memory starts at 0, the act of accessing the shadow memory is detected - # by SAFE_HEAP as a null pointer dereference. - exit_with_error('ASan does not work with SAFE_HEAP') - - if sanitize and settings.GENERATE_SOURCE_MAP: - settings.LOAD_SOURCE_MAP = 1 - - if settings.MINIMAL_RUNTIME: - if settings.EXIT_RUNTIME: - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['proc_exit', '$callRuntimeCallbacks'] - else: - # MINIMAL_RUNTIME only needs callRuntimeCallbacks in certain cases, but the normal runtime - # always does. - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$callRuntimeCallbacks'] - - if settings.EXIT_RUNTIME and not settings.STANDALONE_WASM: - # Internal function implemented in musl that calls any functions registered - # via `atexit` et al. With STANDALONE_WASM this is all taken care of via - # _start and exit handling in musl, but with the normal emscripten ABI we - # need to be able to call these explicitly. - settings.REQUIRED_EXPORTS += ['__funcs_on_exit'] - - # various settings require malloc/free support from JS - if settings.RELOCATABLE or \ - settings.BUILD_AS_WORKER or \ - settings.USE_WEBGPU or \ - settings.OFFSCREENCANVAS_SUPPORT or \ - settings.LEGACY_GL_EMULATION or \ - settings.ASYNCIFY or \ - settings.WASMFS or \ - settings.DEMANGLE_SUPPORT or \ - settings.FORCE_FILESYSTEM or \ - settings.STB_IMAGE or \ - settings.EMBIND or \ - settings.FETCH or \ - settings.PROXY_POSIX_SOCKETS or \ - options.memory_profiler or \ - sanitize: - settings.REQUIRED_EXPORTS += ['malloc', 'free'] - - if not settings.DISABLE_EXCEPTION_CATCHING: - settings.REQUIRED_EXPORTS += [ - # For normal builds the entries in deps_info.py are enough to include - # these symbols whenever __cxa_find_matching_catch_* functions are - # found. However, under LTO these symbols don't exist prior to linking - # so we include then unconditionally when exceptions are enabled. - '__cxa_is_pointer_type', - '__cxa_can_catch', - - # Emscripten exception handling can generate invoke calls, and they call - # setThrew(). We cannot handle this using deps_info as the invokes are not - # emitted because of library function usage, but by codegen itself. - 'setThrew', - '__cxa_free_exception', - ] - - if settings.ASYNCIFY: - if not settings.ASYNCIFY_IGNORE_INDIRECT: - # if we are not ignoring indirect calls, then we must treat invoke_* as if - # they are indirect calls, since that is what they do - we can't see their - # targets statically. - settings.ASYNCIFY_IMPORTS += ['invoke_*'] - # add the default imports - settings.ASYNCIFY_IMPORTS += DEFAULT_ASYNCIFY_IMPORTS - # add the default exports (only used for ASYNCIFY == 2) - settings.ASYNCIFY_EXPORTS += DEFAULT_ASYNCIFY_EXPORTS - - # return the full import name, including module. The name may - # already have a module prefix; if not, we assume it is "env". - def get_full_import_name(name): - if '.' in name: - return name - return 'env.' + name - - settings.ASYNCIFY_IMPORTS = [get_full_import_name(i) for i in settings.ASYNCIFY_IMPORTS] - - if settings.ASYNCIFY == 2: - diagnostics.warning('experimental', 'ASYNCIFY with stack switching is experimental') - - if settings.WASM2JS: - if settings.GENERATE_SOURCE_MAP: - exit_with_error('wasm2js does not support source maps yet (debug in wasm for now)') - if settings.WASM_BIGINT: - exit_with_error('wasm2js does not support WASM_BIGINT') - if settings.MEMORY64: - exit_with_error('wasm2js does not support MEMORY64') - - if settings.NODE_CODE_CACHING: - if settings.WASM_ASYNC_COMPILATION: - exit_with_error('NODE_CODE_CACHING requires sync compilation (WASM_ASYNC_COMPILATION=0)') - if not shared.target_environment_may_be('node'): - exit_with_error('NODE_CODE_CACHING only works in node, but target environments do not include it') - if settings.SINGLE_FILE: - exit_with_error('NODE_CODE_CACHING saves a file on the side and is not compatible with SINGLE_FILE') - - if not js_manipulation.isidentifier(settings.EXPORT_NAME): - exit_with_error(f'EXPORT_NAME is not a valid JS identifier: `{settings.EXPORT_NAME}`') - - if settings.EMSCRIPTEN_TRACING and settings.ALLOW_MEMORY_GROWTH: - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['emscripten_trace_report_memory_layout'] - settings.REQUIRED_EXPORTS += ['emscripten_stack_get_current', - 'emscripten_stack_get_base', - 'emscripten_stack_get_end'] - - # check if we can address the 2GB mark and higher: either if we start at - # 2GB, or if we allow growth to either any amount or to 2GB or more. - if settings.INITIAL_MEMORY > 2 * 1024 * 1024 * 1024 or \ - (settings.ALLOW_MEMORY_GROWTH and - (settings.MAXIMUM_MEMORY < 0 or - settings.MAXIMUM_MEMORY > 2 * 1024 * 1024 * 1024)): - settings.CAN_ADDRESS_2GB = 1 - - settings.EMSCRIPTEN_VERSION = shared.EMSCRIPTEN_VERSION - settings.SOURCE_MAP_BASE = options.source_map_base or '' - - settings.LINK_AS_CXX = (run_via_emxx or settings.DEFAULT_TO_CXX) and '-nostdlib++' not in newargs - - # WASMFS itself is written in C++, and needs C++ standard libraries - if settings.WASMFS: - settings.LINK_AS_CXX = True - - # Some settings make no sense when not linking as C++ - if not settings.LINK_AS_CXX: - cxx_only_settings = [ - 'DEMANGLE_SUPPORT', - 'EXCEPTION_DEBUG', - 'DISABLE_EXCEPTION_CATCHING', - 'EXCEPTION_CATCHING_ALLOWED', - 'DISABLE_EXCEPTION_THROWING', - ] - for setting in cxx_only_settings: - if setting in user_settings: - diagnostics.warning('linkflags', 'setting `%s` is not meaningful unless linking as C++', setting) - - if settings.WASM_EXCEPTIONS: - settings.REQUIRED_EXPORTS += ['__trap'] - - if settings.EXCEPTION_STACK_TRACES: - # If the user explicitly gave EXCEPTION_STACK_TRACES=1 without enabling EH, - # errors out. - if settings.DISABLE_EXCEPTION_CATCHING and not settings.WASM_EXCEPTIONS: - exit_with_error('EXCEPTION_STACK_TRACES requires either of -fexceptions or -fwasm-exceptions') - # EXCEPTION_STACK_TRACES implies EXPORT_EXCEPTION_HANDLING_HELPERS - settings.EXPORT_EXCEPTION_HANDLING_HELPERS = True - - # Make `getExceptionMessage` and other necessary functions available for use. - if settings.EXPORT_EXCEPTION_HANDLING_HELPERS: - # If the user explicitly gave EXPORT_EXCEPTION_HANDLING_HELPERS=1 without - # enabling EH, errors out. - if settings.DISABLE_EXCEPTION_CATCHING and not settings.WASM_EXCEPTIONS: - exit_with_error('EXPORT_EXCEPTION_HANDLING_HELPERS requires either of -fexceptions or -fwasm-exceptions') - # We also export refcount increasing and decreasing functions because if you - # catch an exception, be it an Emscripten exception or a Wasm exception, in - # JS, you may need to manipulate the refcount manually not to leak memory. - # What you need to do is different depending on the kind of EH you use - # (https://github.com/emscripten-core/emscripten/issues/17115). - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$getExceptionMessage', '$incrementExceptionRefcount', '$decrementExceptionRefcount'] - settings.EXPORTED_FUNCTIONS += ['getExceptionMessage', '___get_exception_message', '_free'] - if settings.WASM_EXCEPTIONS: - settings.EXPORTED_FUNCTIONS += ['___cpp_exception', '___cxa_increment_exception_refcount', '___cxa_decrement_exception_refcount', '___thrown_object_from_unwind_exception'] - - feature_matrix.apply_min_browser_versions() - - if settings.SIDE_MODULE: - # For side modules, we ignore all REQUIRED_EXPORTS that might have been added above. - # They all come from either libc or compiler-rt. The exception is __wasm_call_ctors - # which is a per-module export. - settings.REQUIRED_EXPORTS.clear() - - if not settings.STANDALONE_WASM: - # in standalone mode, crt1 will call the constructors from inside the wasm - settings.REQUIRED_EXPORTS.append('__wasm_call_ctors') - - return target, wasm_target - - -@ToolchainProfiler.profile_block('compile inputs') -def phase_compile_inputs(options, state, newargs, input_files): - def is_link_flag(flag): - if flag in ('-nostdlib', '-nostartfiles', '-nolibc', '-nodefaultlibs'): - return True - return flag.startswith(('-l', '-L', '-Wl,')) - - CXX = [shared.CLANG_CXX] - CC = [shared.CLANG_CC] - if config.COMPILER_WRAPPER: - logger.debug('using compiler wrapper: %s', config.COMPILER_WRAPPER) - CXX.insert(0, config.COMPILER_WRAPPER) - CC.insert(0, config.COMPILER_WRAPPER) - - compile_args = [a for a in newargs if a and not is_link_flag(a)] - system_libs.ensure_sysroot() - - def get_language_mode(args): - return_next = False - for item in args: - if return_next: - return item - if item == '-x': - return_next = True - continue - if item.startswith('-x'): - return strip_prefix(item, '-x') - return '' - - language_mode = get_language_mode(newargs) - - def use_cxx(src): - if 'c++' in language_mode or run_via_emxx: - return True - suffix = shared.suffix(src) - # Next consider the filename - if suffix in C_ENDINGS + OBJC_ENDINGS: - return False - if suffix in CXX_ENDINGS: - return True - # Finally fall back to the default - if settings.DEFAULT_TO_CXX: - # Default to using C++ even when run as `emcc`. - # This means that emcc will act as a C++ linker when no source files are - # specified. - # This differs to clang and gcc where the default is always C unless run as - # clang++/g++. - return True - return False - - def get_compiler(src_file): - if use_cxx(src_file): - return CXX - return CC - - def get_clang_command(src_file): - return get_compiler(src_file) + get_cflags(state.orig_args, use_cxx(src_file)) + compile_args + [src_file] - - def get_clang_command_preprocessed(src_file): - return get_compiler(src_file) + get_clang_flags(state.orig_args) + compile_args + [src_file] - - def get_clang_command_asm(src_file): - return get_compiler(src_file) + get_target_flags() + compile_args + [src_file] - - # preprocessor-only (-E) support - if state.mode == Mode.PREPROCESS_ONLY: - for input_file in [x[1] for x in input_files]: - cmd = get_clang_command(input_file) - if options.output_file: - cmd += ['-o', options.output_file] - # Do not compile, but just output the result from preprocessing stage or - # output the dependency rule. Warning: clang and gcc behave differently - # with -MF! (clang seems to not recognize it) - logger.debug(('just preprocessor ' if state.has_dash_E else 'just dependencies: ') + ' '.join(cmd)) - shared.check_call(cmd) - return [] - - # Precompiled headers support - if state.mode == Mode.PCH: - headers = [header for _, header in input_files] - for header in headers: - if not shared.suffix(header) in HEADER_ENDINGS: - exit_with_error(f'cannot mix precompiled headers with non-header inputs: {headers} : {header}') - cmd = get_clang_command(header) - if options.output_file: - cmd += ['-o', options.output_file] - logger.debug(f"running (for precompiled headers): {cmd[0]} {' '.join(cmd[1:])}") - shared.check_call(cmd) - return [] - - linker_inputs = [] - seen_names = {} - - def uniquename(name): - if name not in seen_names: - seen_names[name] = str(len(seen_names)) - return unsuffixed(name) + '_' + seen_names[name] + shared.suffix(name) - - def get_object_filename(input_file): - if state.mode == Mode.COMPILE_ONLY: - # In compile-only mode we don't use any temp file. The object files - # are written directly to their final output locations. - if options.output_file: - assert len(input_files) == 1 - if get_file_suffix(options.output_file) == '.bc' and not settings.LTO and '-emit-llvm' not in state.orig_args: - diagnostics.warning('emcc', '.bc output file suffix used without -flto or -emit-llvm. Consider using .o extension since emcc will output an object file, not a bitcode file') - return options.output_file - else: - return unsuffixed_basename(input_file) + options.default_object_extension - else: - return in_temp(unsuffixed(uniquename(input_file)) + options.default_object_extension) - - def compile_source_file(i, input_file): - logger.debug(f'compiling source file: {input_file}') - output_file = get_object_filename(input_file) - if state.mode not in (Mode.COMPILE_ONLY, Mode.PREPROCESS_ONLY): - linker_inputs.append((i, output_file)) - if get_file_suffix(input_file) in ASSEMBLY_ENDINGS: - cmd = get_clang_command_asm(input_file) - elif get_file_suffix(input_file) in PREPROCESSED_ENDINGS: - cmd = get_clang_command_preprocessed(input_file) - else: - cmd = get_clang_command(input_file) - if get_file_suffix(input_file) in ['.pcm']: - cmd = [c for c in cmd if not c.startswith('-fprebuilt-module-path=')] - if not state.has_dash_c: - cmd += ['-c'] - cmd += ['-o', output_file] - if state.mode == Mode.COMPILE_AND_LINK and '-gsplit-dwarf' in newargs: - # When running in COMPILE_AND_LINK mode we compile to temporary location - # but we want the `.dwo` file to be generated in the current working directory, - # like it is under clang. We could avoid this hack if we use the clang driver - # to generate the temporary files, but that would also involve using the clang - # driver to perform linking which would be big change. - cmd += ['-Xclang', '-split-dwarf-file', '-Xclang', unsuffixed_basename(input_file) + '.dwo'] - cmd += ['-Xclang', '-split-dwarf-output', '-Xclang', unsuffixed_basename(input_file) + '.dwo'] - shared.check_call(cmd) - if output_file not in ('-', os.devnull): - assert os.path.exists(output_file) - - # First, generate LLVM bitcode. For each input file, we get base.o with bitcode - for i, input_file in input_files: - file_suffix = get_file_suffix(input_file) - if file_suffix in SOURCE_ENDINGS + ASSEMBLY_ENDINGS or (state.has_dash_c and file_suffix == '.bc'): - compile_source_file(i, input_file) - elif file_suffix in DYNAMICLIB_ENDINGS: - logger.debug(f'using shared library: {input_file}') - linker_inputs.append((i, input_file)) - elif building.is_ar(input_file): - logger.debug(f'using static library: {input_file}') - linker_inputs.append((i, input_file)) - elif language_mode: - compile_source_file(i, input_file) - elif input_file == '-': - exit_with_error('-E or -x required when input is from standard input') - else: - # Default to assuming the inputs are object files and pass them to the linker - logger.debug(f'using object file: {input_file}') - linker_inputs.append((i, input_file)) - - return linker_inputs - - -@ToolchainProfiler.profile_block('calculate system libraries') -def phase_calculate_system_libraries(state, linker_arguments, linker_inputs, newargs): - extra_files_to_link = [] - # Link in ports and system libraries, if necessary - if not settings.SIDE_MODULE: - # Ports are always linked into the main module, never the side module. - extra_files_to_link += ports.get_libs(settings) - all_linker_inputs = [f for _, f in sorted(linker_inputs)] + extra_files_to_link - extra_files_to_link += system_libs.calculate(all_linker_inputs, newargs, forced=state.forced_stdlibs) - linker_arguments.extend(extra_files_to_link) - - -@ToolchainProfiler.profile_block('link') -def phase_link(linker_arguments, wasm_target, js_syms): - logger.debug(f'linking: {linker_arguments}') - - # Make a final pass over settings.EXPORTED_FUNCTIONS to remove any - # duplication between functions added by the driver/libraries and function - # specified by the user - settings.EXPORTED_FUNCTIONS = dedup_list(settings.EXPORTED_FUNCTIONS) - settings.REQUIRED_EXPORTS = dedup_list(settings.REQUIRED_EXPORTS) - settings.EXPORT_IF_DEFINED = dedup_list(settings.EXPORT_IF_DEFINED) - - building.link_lld(linker_arguments, wasm_target, external_symbols=js_syms) - - -@ToolchainProfiler.profile_block('post_link') -def phase_post_link(options, state, in_wasm, wasm_target, target): - global final_js - - target_basename = unsuffixed_basename(target) - - if options.oformat != OFormat.WASM: - final_js = in_temp(target_basename + '.js') - - settings.TARGET_BASENAME = unsuffixed_basename(target) - - if options.oformat in (OFormat.JS, OFormat.MJS): - state.js_target = target - else: - state.js_target = get_secondary_target(target, '.js') - - settings.TARGET_JS_NAME = os.path.basename(state.js_target) - - if settings.MEM_INIT_IN_WASM: - memfile = None - else: - memfile = shared.replace_or_append_suffix(target, '.mem') - - phase_emscript(options, in_wasm, wasm_target, memfile) - - if options.js_transform: - phase_source_transforms(options) - - if memfile and not settings.MINIMAL_RUNTIME: - # MINIMAL_RUNTIME doesn't use `var memoryInitializer` but instead expects Module['mem'] to - # be loaded before the module. See src/postamble_minimal.js. - phase_memory_initializer(memfile) - - phase_binaryen(target, options, wasm_target) - - # If we are not emitting any JS then we are all done now - if options.oformat != OFormat.WASM: - phase_final_emitting(options, state, target, wasm_target, memfile) - - -@ToolchainProfiler.profile_block('emscript') -def phase_emscript(options, in_wasm, wasm_target, memfile): - # Emscripten - logger.debug('emscript') - - if embed_memfile(options): - settings.SUPPORT_BASE64_EMBEDDING = 1 - # _read in shell.js depends on intArrayToString when SUPPORT_BASE64_EMBEDDING is set - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.append('$intArrayToString') - - emscripten.run(in_wasm, wasm_target, final_js, memfile) - save_intermediate('original') - - -@ToolchainProfiler.profile_block('source transforms') -def phase_source_transforms(options): - # Apply a source code transformation, if requested - global final_js - safe_copy(final_js, final_js + '.tr.js') - final_js += '.tr.js' - posix = not shared.WINDOWS - logger.debug('applying transform: %s', options.js_transform) - shared.check_call(building.remove_quotes(shlex.split(options.js_transform, posix=posix) + [os.path.abspath(final_js)])) - save_intermediate('transformed') - - -@ToolchainProfiler.profile_block('memory initializer') -def phase_memory_initializer(memfile): - # For the wasm backend, we don't have any memory info in JS. All we need to do - # is set the memory initializer url. - global final_js - - src = read_file(final_js) - src = do_replace(src, '<<< MEM_INITIALIZER >>>', '"%s"' % os.path.basename(memfile)) - write_file(final_js + '.mem.js', src) - final_js += '.mem.js' - - -@ToolchainProfiler.profile_block('final emitting') -def phase_final_emitting(options, state, target, wasm_target, memfile): - global final_js - - # Remove some trivial whitespace - # TODO: do not run when compress has already been done on all parts of the code - # src = read_file(final_js) - # src = re.sub(r'\n+[ \n]*\n+', '\n', src) - # write_file(final_js, src) - - target_dir = os.path.dirname(os.path.abspath(target)) - if settings.PTHREADS: - worker_output = os.path.join(target_dir, settings.PTHREAD_WORKER_FILE) - contents = shared.read_and_preprocess(utils.path_from_root('src/worker.js'), expand_macros=True) - write_file(worker_output, contents) - - # Minify the worker.js file in optimized builds - if (settings.OPT_LEVEL >= 1 or settings.SHRINK_LEVEL >= 1) and not settings.DEBUG_LEVEL: - minified_worker = building.acorn_optimizer(worker_output, ['minifyWhitespace'], return_output=True) - write_file(worker_output, minified_worker) - - # Deploy the Wasm Worker bootstrap file as an output file (*.ww.js) - if settings.WASM_WORKERS == 1: - worker_output = os.path.join(target_dir, settings.WASM_WORKER_FILE) - contents = shared.read_and_preprocess(shared.path_from_root('src/wasm_worker.js'), expand_macros=True) - write_file(worker_output, contents) - - # Minify the wasm_worker.js file in optimized builds - if (settings.OPT_LEVEL >= 1 or settings.SHRINK_LEVEL >= 1) and not settings.DEBUG_LEVEL: - minified_worker = building.acorn_optimizer(worker_output, ['minifyWhitespace'], return_output=True) - write_file(worker_output, minified_worker) - - # Deploy the Audio Worklet module bootstrap file (*.aw.js) - if settings.AUDIO_WORKLET == 1: - worklet_output = os.path.join(target_dir, settings.AUDIO_WORKLET_FILE) - with open(worklet_output, 'w') as f: - f.write(shared.read_and_preprocess(shared.path_from_root('src', 'audio_worklet.js'), expand_macros=True)) - - # Minify the audio_worklet.js file in optimized builds - if (settings.OPT_LEVEL >= 1 or settings.SHRINK_LEVEL >= 1) and not settings.DEBUG_LEVEL: - minified_worker = building.acorn_optimizer(worklet_output, ['minifyWhitespace'], return_output=True) - open(worklet_output, 'w').write(minified_worker) - - # track files that will need native eols - generated_text_files_with_native_eols = [] - - if settings.MODULARIZE: - modularize() - - if settings.USE_CLOSURE_COMPILER: - module_export_name_substitution() - - # Run a final optimization pass to clean up items that were not possible to - # optimize by Closure, or unoptimalities that were left behind by processing - # steps that occurred after Closure. - if settings.MINIMAL_RUNTIME == 2 and settings.USE_CLOSURE_COMPILER and settings.DEBUG_LEVEL == 0: - shared.run_js_tool(utils.path_from_root('tools/unsafe_optimizations.js'), [final_js, '-o', final_js], cwd=utils.path_from_root('.')) - # Finally, rerun Closure compile with simple optimizations. It will be able - # to further minify the code. (n.b. it would not be safe to run in advanced - # mode) - final_js = building.closure_compiler(final_js, pretty=False, advanced=False, extra_closure_args=options.closure_args) - - # Unmangle previously mangled `import.meta` and `await import` references in - # both main code and libraries. - # See also: `preprocess` in parseTools.js. - if settings.EXPORT_ES6 and settings.USE_ES6_IMPORT_META: - src = read_file(final_js) - final_js += '.esmeta.js' - write_file(final_js, src - .replace('EMSCRIPTEN$IMPORT$META', 'import.meta') - .replace('EMSCRIPTEN$AWAIT$IMPORT', 'await import')) - shared.get_temp_files().note(final_js) - save_intermediate('es6-module') - - # Apply pre and postjs files - if options.extern_pre_js or options.extern_post_js: - logger.debug('applying extern pre/postjses') - src = read_file(final_js) - final_js += '.epp.js' - with open(final_js, 'w', encoding='utf-8') as f: - f.write(options.extern_pre_js) - f.write(src) - f.write(options.extern_post_js) - save_intermediate('extern-pre-post') - - js_manipulation.handle_license(final_js) - - js_target = state.js_target - - # The JS is now final. Move it to its final location - move_file(final_js, js_target) - - if not settings.SINGLE_FILE: - generated_text_files_with_native_eols += [js_target] - - target_basename = unsuffixed_basename(target) - - # If we were asked to also generate HTML, do that - if options.oformat == OFormat.HTML: - generate_html(target, options, js_target, target_basename, - wasm_target, memfile) - elif settings.PROXY_TO_WORKER: - generate_worker_js(target, js_target, target_basename) - - if embed_memfile(options) and memfile: - delete_file(memfile) - - if settings.SPLIT_MODULE: - diagnostics.warning('experimental', 'The SPLIT_MODULE setting is experimental and subject to change') - do_split_module(wasm_target, options) - - for f in generated_text_files_with_native_eols: - tools.line_endings.convert_line_endings_in_file(f, os.linesep, options.output_eol) - - if options.executable: - make_js_executable(js_target) - - -def version_string(): - # if the emscripten folder is not a git repo, don't run git show - that can - # look up and find the revision in a parent directory that is a git repo - revision_suffix = '' - if os.path.exists(utils.path_from_root('.git')): - git_rev = run_process( - ['git', 'rev-parse', 'HEAD'], - stdout=PIPE, stderr=PIPE, cwd=utils.path_from_root()).stdout.strip() - revision_suffix = ' (%s)' % git_rev - elif os.path.exists(utils.path_from_root('emscripten-revision.txt')): - rev = read_file(utils.path_from_root('emscripten-revision.txt')).strip() - revision_suffix = ' (%s)' % rev - return f'emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) {shared.EMSCRIPTEN_VERSION}{revision_suffix}' - - -def parse_args(newargs): - options = EmccOptions() - settings_changes = [] - user_js_defines = [] - should_exit = False - skip = False - - for i in range(len(newargs)): - if skip: - skip = False - continue - - # Support legacy '--bind' flag, by mapping to `-lembind` which now - # has the same effect - if newargs[i] == '--bind': - newargs[i] = '-lembind' - - arg = newargs[i] - arg_value = None - - def check_flag(value): - # Check for and consume a flag - if arg == value: - newargs[i] = '' - return True - return False - - def check_arg(name): - nonlocal arg_value - if arg.startswith(name) and '=' in arg: - arg_value = arg.split('=', 1)[1] - newargs[i] = '' - return True - if arg == name: - if len(newargs) <= i + 1: - exit_with_error("option '%s' requires an argument" % arg) - arg_value = newargs[i + 1] - newargs[i] = '' - newargs[i + 1] = '' - return True - return False - - def consume_arg(): - nonlocal arg_value - assert arg_value is not None - rtn = arg_value - arg_value = None - return rtn - - def consume_arg_file(): - name = consume_arg() - if not os.path.isfile(name): - exit_with_error("'%s': file not found: '%s'" % (arg, name)) - return name - - if arg.startswith('-O'): - # Let -O default to -O2, which is what gcc does. - requested_level = strip_prefix(arg, '-O') or '2' - if requested_level == 's': - requested_level = 2 - settings.SHRINK_LEVEL = 1 - elif requested_level == 'z': - requested_level = 2 - settings.SHRINK_LEVEL = 2 - elif requested_level == 'g': - requested_level = 1 - settings.SHRINK_LEVEL = 0 - settings.DEBUG_LEVEL = max(settings.DEBUG_LEVEL, 1) - else: - settings.SHRINK_LEVEL = 0 - settings.OPT_LEVEL = validate_arg_level(requested_level, 3, 'invalid optimization level: ' + arg, clamp=True) - elif check_arg('--js-opts'): - logger.warning('--js-opts ignored when using llvm backend') - consume_arg() - elif check_arg('--llvm-opts'): - diagnostics.warning('deprecated', '--llvm-opts is deprecated. All non-emcc args are passed through to clang.') - elif arg.startswith('-flto'): - if '=' in arg: - settings.LTO = arg.split('=')[1] - else: - settings.LTO = 'full' - elif check_arg('--llvm-lto'): - logger.warning('--llvm-lto ignored when using llvm backend') - consume_arg() - elif check_arg('--closure-args'): - args = consume_arg() - options.closure_args += shlex.split(args) - elif check_arg('--closure'): - options.use_closure_compiler = int(consume_arg()) - elif check_arg('--js-transform'): - options.js_transform = consume_arg() - elif check_arg('--reproduce'): - options.reproduce = consume_arg() - elif check_arg('--pre-js'): - options.pre_js.append(consume_arg_file()) - elif check_arg('--post-js'): - options.post_js.append(consume_arg_file()) - elif check_arg('--extern-pre-js'): - options.extern_pre_js.append(consume_arg_file()) - elif check_arg('--extern-post-js'): - options.extern_post_js.append(consume_arg_file()) - elif check_arg('--compiler-wrapper'): - config.COMPILER_WRAPPER = consume_arg() - elif check_flag('--post-link'): - options.post_link = True - elif check_arg('--oformat'): - formats = [f.lower() for f in OFormat.__members__] - fmt = consume_arg() - if fmt not in formats: - exit_with_error('invalid output format: `%s` (must be one of %s)' % (fmt, formats)) - options.oformat = getattr(OFormat, fmt.upper()) - elif check_arg('--minify'): - arg = consume_arg() - if arg != '0': - exit_with_error('0 is the only supported option for --minify; 1 has been deprecated') - settings.DEBUG_LEVEL = max(1, settings.DEBUG_LEVEL) - elif arg.startswith('-g'): - options.requested_debug = arg - requested_level = strip_prefix(arg, '-g') or '3' - if is_int(requested_level): - # the -gX value is the debug level (-g1, -g2, etc.) - settings.DEBUG_LEVEL = validate_arg_level(requested_level, 4, 'invalid debug level: ' + arg) - # if we don't need to preserve LLVM debug info, do not keep this flag - # for clang - if settings.DEBUG_LEVEL < 3: - newargs[i] = '-g0' - else: - # for 3+, report -g3 to clang as -g4 etc. are not accepted - newargs[i] = '-g3' - if settings.DEBUG_LEVEL == 3: - settings.GENERATE_DWARF = 1 - if settings.DEBUG_LEVEL == 4: - settings.GENERATE_SOURCE_MAP = 1 - diagnostics.warning('deprecated', 'please replace -g4 with -gsource-map') - else: - if requested_level.startswith('force_dwarf'): - exit_with_error('gforce_dwarf was a temporary option and is no longer necessary (use -g)') - elif requested_level.startswith('separate-dwarf'): - # emit full DWARF but also emit it in a file on the side - newargs[i] = '-g' - # if a file is provided, use that; otherwise use the default location - # (note that we do not know the default location until all args have - # been parsed, so just note True for now). - if requested_level != 'separate-dwarf': - if not requested_level.startswith('separate-dwarf=') or requested_level.count('=') != 1: - exit_with_error('invalid -gseparate-dwarf=FILENAME notation') - settings.SEPARATE_DWARF = requested_level.split('=')[1] - else: - settings.SEPARATE_DWARF = True - elif requested_level == 'source-map': - settings.GENERATE_SOURCE_MAP = 1 - newargs[i] = '-g' - # a non-integer level can be something like -gline-tables-only. keep - # the flag for the clang frontend to emit the appropriate DWARF info. - # set the emscripten debug level to 3 so that we do not remove that - # debug info during link (during compile, this does not make a - # difference). - settings.DEBUG_LEVEL = 3 - elif check_flag('-profiling') or check_flag('--profiling'): - settings.DEBUG_LEVEL = max(settings.DEBUG_LEVEL, 2) - elif check_flag('-profiling-funcs') or check_flag('--profiling-funcs'): - settings.EMIT_NAME_SECTION = 1 - elif newargs[i] == '--tracing' or newargs[i] == '--memoryprofiler': - if newargs[i] == '--memoryprofiler': - options.memory_profiler = True - newargs[i] = '' - settings_changes.append('EMSCRIPTEN_TRACING=1') - settings.JS_LIBRARIES.append((0, 'library_trace.js')) - elif check_flag('--emit-symbol-map'): - options.emit_symbol_map = True - settings.EMIT_SYMBOL_MAP = 1 - elif check_arg('--embed-file'): - options.embed_files.append(consume_arg()) - elif check_arg('--preload-file'): - options.preload_files.append(consume_arg()) - elif check_arg('--exclude-file'): - options.exclude_files.append(consume_arg()) - elif check_flag('--use-preload-cache'): - options.use_preload_cache = True - elif check_flag('--no-heap-copy'): - diagnostics.warning('legacy-settings', 'ignoring legacy flag --no-heap-copy (that is the only mode supported now)') - elif check_flag('--use-preload-plugins'): - options.use_preload_plugins = True - elif check_flag('--ignore-dynamic-linking'): - options.ignore_dynamic_linking = True - elif arg == '-v': - shared.PRINT_STAGES = True - elif check_arg('--shell-file'): - options.shell_path = consume_arg_file() - elif check_arg('--source-map-base'): - options.source_map_base = consume_arg() - elif check_flag('--no-entry'): - options.no_entry = True - elif check_arg('--js-library'): - settings.JS_LIBRARIES.append((i + 1, os.path.abspath(consume_arg_file()))) - elif check_flag('--remove-duplicates'): - diagnostics.warning('legacy-settings', '--remove-duplicates is deprecated as it is no longer needed. If you cannot link without it, file a bug with a testcase') - elif check_flag('--jcache'): - logger.error('jcache is no longer supported') - elif check_arg('--cache'): - config.CACHE = os.path.normpath(consume_arg()) - cache.setup(config.CACHE) - # Ensure child processes share the same cache (e.g. when using emcc to compiler system - # libraries) - os.environ['EM_CACHE'] = config.CACHE - elif check_flag('--clear-cache'): - logger.info('clearing cache as requested by --clear-cache: `%s`', cache.cachedir) - cache.erase() - shared.perform_sanity_checks() # this is a good time for a sanity check - should_exit = True - elif check_flag('--clear-ports'): - logger.info('clearing ports and cache as requested by --clear-ports') - ports.clear() - cache.erase() - shared.perform_sanity_checks() # this is a good time for a sanity check - should_exit = True - elif check_flag('--check'): - print(version_string(), file=sys.stderr) - shared.check_sanity(force=True) - should_exit = True - elif check_flag('--show-ports'): - ports.show_ports() - should_exit = True - elif check_arg('--memory-init-file'): - # This flag is ignored unless targetting wasm2js. - # TODO(sbc): Error out if used without wasm2js. - options.memory_init_file = int(consume_arg()) - elif check_flag('--proxy-to-worker'): - settings_changes.append('PROXY_TO_WORKER=1') - elif check_arg('--valid-abspath'): - options.valid_abspaths.append(consume_arg()) - elif check_flag('--separate-asm'): - exit_with_error('cannot --separate-asm with the wasm backend, since not emitting asm.js') - elif arg.startswith(('-I', '-L')): - path_name = arg[2:] - if os.path.isabs(path_name) and not is_valid_abspath(options, path_name): - # Of course an absolute path to a non-system-specific library or header - # is fine, and you can ignore this warning. The danger are system headers - # that are e.g. x86 specific and non-portable. The emscripten bundled - # headers are modified to be portable, local system ones are generally not. - diagnostics.warning( - 'absolute-paths', f'-I or -L of an absolute path "{arg}" ' - 'encountered. If this is to a local system header/library, it may ' - 'cause problems (local system files make sense for compiling natively ' - 'on your system, but not necessarily to JavaScript).') - elif check_flag('--emrun'): - options.emrun = True - elif check_flag('--cpuprofiler'): - options.cpu_profiler = True - elif check_flag('--threadprofiler'): - settings_changes.append('PTHREADS_PROFILING=1') - elif arg == '-fno-exceptions': - settings.DISABLE_EXCEPTION_CATCHING = 1 - settings.DISABLE_EXCEPTION_THROWING = 1 - settings.WASM_EXCEPTIONS = 0 - elif arg == '-fexceptions': - # TODO Currently -fexceptions only means Emscripten EH. Switch to wasm - # exception handling by default when -fexceptions is given when wasm - # exception handling becomes stable. - settings.DISABLE_EXCEPTION_THROWING = 0 - settings.DISABLE_EXCEPTION_CATCHING = 0 - elif arg == '-fwasm-exceptions': - settings.WASM_EXCEPTIONS = 1 - elif arg == '-fignore-exceptions': - settings.DISABLE_EXCEPTION_CATCHING = 1 - elif check_arg('--default-obj-ext'): - options.default_object_extension = consume_arg() - if not options.default_object_extension.startswith('.'): - options.default_object_extension = '.' + options.default_object_extension - elif arg.startswith('-fsanitize=cfi'): - exit_with_error('emscripten does not currently support -fsanitize=cfi') - elif check_arg('--output_eol'): - style = consume_arg() - if style.lower() == 'windows': - options.output_eol = '\r\n' - elif style.lower() == 'linux': - options.output_eol = '\n' - else: - exit_with_error(f'Invalid value "{style}" to --output_eol!') - # Record PTHREADS setting because it controls whether --shared-memory is passed to lld - elif arg == '-pthread': - settings.PTHREADS = 1 - # Also set the legacy setting name, in case use JS code depends on it. - settings.USE_PTHREADS = 1 - elif arg == '-pthreads': - exit_with_error('unrecognized command-line option `-pthreads`; did you mean `-pthread`?') - elif arg in ('-fno-diagnostics-color', '-fdiagnostics-color=never'): - colored_logger.disable() - diagnostics.color_enabled = False - elif arg == '-fno-rtti': - settings.USE_RTTI = 0 - elif arg == '-frtti': - settings.USE_RTTI = 1 - elif arg.startswith('-jsD'): - key = strip_prefix(arg, '-jsD') - if '=' in key: - key, value = key.split('=') - else: - value = '1' - if key in settings.keys(): - exit_with_error(f'{arg}: cannot change built-in settings values with a -jsD directive. Pass -s{key}={value} instead!') - user_js_defines += [(key, value)] - newargs[i] = '' - elif check_flag('-shared'): - options.shared = True - elif check_flag('-r'): - options.relocatable = True - elif check_arg('-o'): - options.output_file = consume_arg() - elif arg.startswith('-o'): - options.output_file = strip_prefix(arg, '-o') - newargs[i] = '' - elif arg == '-mllvm': - # Ignore the next argument rather than trying to parse it. This is needed - # because llvm args could, for example, start with `-o` and we don't want - # to confuse that with a normal `-o` flag. - skip = True - - if should_exit: - sys.exit(0) - - newargs = [a for a in newargs if a] - return options, settings_changes, user_js_defines, newargs - - -@ToolchainProfiler.profile_block('binaryen') -def phase_binaryen(target, options, wasm_target): - global final_js - logger.debug('using binaryen') - if settings.GENERATE_SOURCE_MAP and not settings.SOURCE_MAP_BASE: - logger.warning("Wasm source map won't be usable in a browser without --source-map-base") - # whether we need to emit -g (function name debug info) in the final wasm - debug_info = settings.DEBUG_LEVEL >= 2 or settings.EMIT_NAME_SECTION - # whether we need to emit -g in the intermediate binaryen invocations (but not - # necessarily at the very end). this is necessary if we depend on debug info - # during compilation, even if we do not emit it at the end. - # we track the number of causes for needing intermdiate debug info so - # that we can stop emitting it when possible - in particular, that is - # important so that we stop emitting it before the end, and it is not in the - # final binary (if it shouldn't be) - intermediate_debug_info = 0 - if debug_info: - intermediate_debug_info += 1 - if options.emit_symbol_map: - intermediate_debug_info += 1 - if settings.ASYNCIFY == 1: - intermediate_debug_info += 1 - # note that wasm-ld can strip DWARF info for us too (--strip-debug), but it - # also strips the Names section. so to emit just the Names section we don't - # tell wasm-ld to strip anything, and we do it here. - strip_debug = settings.DEBUG_LEVEL < 3 - strip_producers = not settings.EMIT_PRODUCERS_SECTION - # run wasm-opt if we have work for it: either passes, or if we are using - # source maps (which requires some extra processing to keep the source map - # but remove DWARF) - passes = get_binaryen_passes() - if passes or settings.GENERATE_SOURCE_MAP: - # if we need to strip certain sections, and we have wasm-opt passes - # to run anyhow, do it with them. - if strip_debug: - passes += ['--strip-debug'] - if strip_producers: - passes += ['--strip-producers'] - # if asyncify is used, we will use it in the next stage, and so if it is - # the only reason we need intermediate debug info, we can stop keeping it - if settings.ASYNCIFY == 1: - intermediate_debug_info -= 1 - # currently binaryen's DWARF support will limit some optimizations; warn on - # that. see https://github.com/emscripten-core/emscripten/issues/15269 - dwarf_info = settings.DEBUG_LEVEL >= 3 - if dwarf_info: - diagnostics.warning('limited-postlink-optimizations', 'running limited binaryen optimizations because DWARF info requested (or indirectly required)') - with ToolchainProfiler.profile_block('wasm_opt'): - building.run_wasm_opt(wasm_target, - wasm_target, - args=passes, - debug=intermediate_debug_info) - building.save_intermediate(wasm_target, 'byn.wasm') - elif strip_debug or strip_producers: - # we are not running wasm-opt. if we need to strip certain sections - # then do so using llvm-objcopy which is fast and does not rewrite the - # code (which is better for debug info) - sections = ['producers'] if strip_producers else [] - with ToolchainProfiler.profile_block('strip_producers'): - building.strip(wasm_target, wasm_target, debug=strip_debug, sections=sections) - building.save_intermediate(wasm_target, 'strip.wasm') - - if settings.EVAL_CTORS: - with ToolchainProfiler.profile_block('eval_ctors'): - building.eval_ctors(final_js, wasm_target, debug_info=intermediate_debug_info) - building.save_intermediate(wasm_target, 'ctors.wasm') - - # after generating the wasm, do some final operations - - if final_js: - if settings.SUPPORT_BIG_ENDIAN: - with ToolchainProfiler.profile_block('little_endian_heap'): - final_js = building.little_endian_heap(final_js) - - # >=2GB heap support requires pointers in JS to be unsigned. rather than - # require all pointers to be unsigned by default, which increases code size - # a little, keep them signed, and just unsign them here if we need that. - if settings.CAN_ADDRESS_2GB: - with ToolchainProfiler.profile_block('use_unsigned_pointers_in_js'): - final_js = building.use_unsigned_pointers_in_js(final_js) - - # pthreads memory growth requires some additional JS fixups. - # note that we must do this after handling of unsigned pointers. unsigning - # adds some >>> 0 things, while growth will replace a HEAP8 with a call to - # a method to get the heap, and that call would not be recognized by the - # unsigning pass - if settings.PTHREADS and settings.ALLOW_MEMORY_GROWTH: - with ToolchainProfiler.profile_block('apply_wasm_memory_growth'): - final_js = building.apply_wasm_memory_growth(final_js) - - if settings.USE_ASAN: - final_js = building.instrument_js_for_asan(final_js) - - if settings.SAFE_HEAP: - final_js = building.instrument_js_for_safe_heap(final_js) - - if settings.OPT_LEVEL >= 2 and settings.DEBUG_LEVEL <= 2: - # minify the JS. Do not minify whitespace if Closure is used, so that - # Closure can print out readable error messages (Closure will then - # minify whitespace afterwards) - with ToolchainProfiler.profile_block('minify_wasm'): - save_intermediate_with_wasm('preclean', wasm_target) - final_js = building.minify_wasm_js(js_file=final_js, - wasm_file=wasm_target, - expensive_optimizations=will_metadce(), - minify_whitespace=minify_whitespace() and not options.use_closure_compiler, - debug_info=intermediate_debug_info) - save_intermediate_with_wasm('postclean', wasm_target) - - if settings.ASYNCIFY_LAZY_LOAD_CODE: - with ToolchainProfiler.profile_block('asyncify_lazy_load_code'): - building.asyncify_lazy_load_code(wasm_target, debug=intermediate_debug_info) - - def preprocess_wasm2js_script(): - return read_and_preprocess(utils.path_from_root('src/wasm2js.js'), expand_macros=True) - - if final_js and (options.use_closure_compiler or settings.TRANSPILE_TO_ES5): - if options.use_closure_compiler: - with ToolchainProfiler.profile_block('closure_compile'): - final_js = building.closure_compiler(final_js, pretty=not minify_whitespace(), - extra_closure_args=options.closure_args) - else: - with ToolchainProfiler.profile_block('closure_transpile'): - final_js = building.closure_transpile(final_js, pretty=not minify_whitespace()) - save_intermediate_with_wasm('closure', wasm_target) - - symbols_file = None - if options.emit_symbol_map: - symbols_file = shared.replace_or_append_suffix(target, '.symbols') - - if settings.WASM2JS: - symbols_file_js = None - if settings.WASM == 2: - wasm2js_template = wasm_target + '.js' - write_file(wasm2js_template, preprocess_wasm2js_script()) - # generate secondary file for JS symbols - if options.emit_symbol_map: - symbols_file_js = shared.replace_or_append_suffix(wasm2js_template, '.symbols') - else: - wasm2js_template = final_js - if options.emit_symbol_map: - symbols_file_js = shared.replace_or_append_suffix(target, '.symbols') - - wasm2js = building.wasm2js(wasm2js_template, - wasm_target, - opt_level=settings.OPT_LEVEL, - minify_whitespace=minify_whitespace(), - use_closure_compiler=options.use_closure_compiler, - debug_info=debug_info, - symbols_file=symbols_file, - symbols_file_js=symbols_file_js) - - shared.get_temp_files().note(wasm2js) - - if settings.WASM == 2: - safe_copy(wasm2js, wasm2js_template) - - if settings.WASM != 2: - final_js = wasm2js - # if we only target JS, we don't need the wasm any more - delete_file(wasm_target) - - save_intermediate('wasm2js') - - # emit the final symbols, either in the binary or in a symbol map. - # this will also remove debug info if we only kept it around in the intermediate invocations. - # note that if we aren't emitting a binary (like in wasm2js) then we don't - # have anything to do here. - if options.emit_symbol_map: - intermediate_debug_info -= 1 - if os.path.exists(wasm_target): - with ToolchainProfiler.profile_block('handle_final_symbols'): - building.handle_final_wasm_symbols(wasm_file=wasm_target, symbols_file=symbols_file, debug_info=intermediate_debug_info) - save_intermediate_with_wasm('symbolmap', wasm_target) - - if settings.DEBUG_LEVEL >= 3 and settings.SEPARATE_DWARF and os.path.exists(wasm_target): - building.emit_debug_on_side(wasm_target) - - if settings.WASM2C: - wasm2c.do_wasm2c(wasm_target) - - # we have finished emitting the wasm, and so intermediate debug info will - # definitely no longer be used tracking it. - if debug_info: - intermediate_debug_info -= 1 - assert intermediate_debug_info == 0 - # strip debug info if it was not already stripped by the last command - if not debug_info and building.binaryen_kept_debug_info and \ - building.os.path.exists(wasm_target): - with ToolchainProfiler.profile_block('strip_with_wasm_opt'): - building.run_wasm_opt(wasm_target, wasm_target) - - # replace placeholder strings with correct subresource locations - if final_js and settings.SINGLE_FILE and not settings.WASM2JS: - js = read_file(final_js) - - if settings.MINIMAL_RUNTIME: - js = do_replace(js, '<<< WASM_BINARY_DATA >>>', base64_encode(read_binary(wasm_target))) - else: - js = do_replace(js, '<<< WASM_BINARY_FILE >>>', get_subresource_location(wasm_target)) - delete_file(wasm_target) - write_file(final_js, js) - - -def node_es6_imports(): - if not settings.EXPORT_ES6 or not shared.target_environment_may_be('node'): - return '' - - # Multi-environment builds uses `await import` in `shell.js` - if shared.target_environment_may_be('web'): - return '' - - # Use static import declaration if we only target Node.js - return ''' -import { createRequire } from 'module'; -const require = createRequire(import.meta.url); -''' - - -def modularize(): - global final_js - logger.debug(f'Modularizing, assigning to var {settings.EXPORT_NAME}') - src = read_file(final_js) - - # Multi-environment ES6 builds require an async function - async_emit = '' - if settings.EXPORT_ES6 and \ - shared.target_environment_may_be('node') and \ - shared.target_environment_may_be('web'): - async_emit = 'async ' - - return_value = settings.EXPORT_NAME - if settings.WASM_ASYNC_COMPILATION: - return_value += '.ready' - if not settings.EXPORT_READY_PROMISE: - return_value = '{}' - - # TODO: Remove when https://bugs.webkit.org/show_bug.cgi?id=223533 is resolved. - if async_emit != '' and settings.EXPORT_NAME == 'config': - diagnostics.warning('emcc', 'EXPORT_NAME should not be named "config" when targeting Safari') - - src = ''' -%(maybe_async)sfunction(%(EXPORT_NAME)s = {}) { - -%(src)s - - return %(return_value)s -} -%(capture_module_function_for_audio_worklet)s -''' % { - 'maybe_async': async_emit, - 'EXPORT_NAME': settings.EXPORT_NAME, - 'src': src, - 'return_value': return_value, - # Given the async nature of how the Module function and Module object come into existence in AudioWorkletGlobalScope, - # store the Module function under a different variable name so that AudioWorkletGlobalScope will be able to reference - # it without aliasing/conflicting with the Module variable name. - 'capture_module_function_for_audio_worklet': 'globalThis.AudioWorkletModule = Module;' if settings.AUDIO_WORKLET and settings.MODULARIZE else '' - } - - if settings.MINIMAL_RUNTIME and not settings.PTHREADS: - # Single threaded MINIMAL_RUNTIME programs do not need access to - # document.currentScript, so a simple export declaration is enough. - src = 'var %s=%s' % (settings.EXPORT_NAME, src) - else: - script_url_node = '' - # When MODULARIZE this JS may be executed later, - # after document.currentScript is gone, so we save it. - # In EXPORT_ES6 + PTHREADS the 'thread' is actually an ES6 module webworker running in strict mode, - # so doesn't have access to 'document'. In this case use 'import.meta' instead. - if settings.EXPORT_ES6 and settings.USE_ES6_IMPORT_META: - script_url = 'import.meta.url' - else: - script_url = "typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined" - if shared.target_environment_may_be('node'): - script_url_node = "if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename;" - src = '''%(node_imports)s -var %(EXPORT_NAME)s = (() => { - var _scriptDir = %(script_url)s; - %(script_url_node)s - return (%(src)s); -})(); -''' % { - 'node_imports': node_es6_imports(), - 'EXPORT_NAME': settings.EXPORT_NAME, - 'script_url': script_url, - 'script_url_node': script_url_node, - 'src': src - } - - final_js += '.modular.js' - with open(final_js, 'w', encoding='utf-8') as f: - f.write(src) - - # Export using a UMD style export, or ES6 exports if selected - - if settings.EXPORT_ES6: - f.write('export default %s;' % settings.EXPORT_NAME) - elif not settings.MINIMAL_RUNTIME: - f.write('''\ -if (typeof exports === 'object' && typeof module === 'object') - module.exports = %(EXPORT_NAME)s; -else if (typeof define === 'function' && define['amd']) - define([], function() { return %(EXPORT_NAME)s; }); -else if (typeof exports === 'object') - exports["%(EXPORT_NAME)s"] = %(EXPORT_NAME)s; -''' % {'EXPORT_NAME': settings.EXPORT_NAME}) - - shared.get_temp_files().note(final_js) - save_intermediate('modularized') - - -def module_export_name_substitution(): - global final_js - logger.debug(f'Private module export name substitution with {settings.EXPORT_NAME}') - src = read_file(final_js) - final_js += '.module_export_name_substitution.js' - if settings.MINIMAL_RUNTIME and not settings.ENVIRONMENT_MAY_BE_NODE and not settings.ENVIRONMENT_MAY_BE_SHELL and not settings.AUDIO_WORKLET: - # On the web, with MINIMAL_RUNTIME, the Module object is always provided - # via the shell html in order to provide the .asm.js/.wasm content. - replacement = settings.EXPORT_NAME - else: - replacement = "typeof %(EXPORT_NAME)s !== 'undefined' ? %(EXPORT_NAME)s : {}" % {"EXPORT_NAME": settings.EXPORT_NAME} - new_src = re.sub(r'{\s*[\'"]?__EMSCRIPTEN_PRIVATE_MODULE_EXPORT_NAME_SUBSTITUTION__[\'"]?:\s*1\s*}', replacement, src) - assert new_src != src, 'Unable to find Closure syntax __EMSCRIPTEN_PRIVATE_MODULE_EXPORT_NAME_SUBSTITUTION__ in source!' - write_file(final_js, new_src) - shared.get_temp_files().note(final_js) - save_intermediate('module_export_name_substitution') - - -def generate_traditional_runtime_html(target, options, js_target, target_basename, - wasm_target, memfile): - script = ScriptSource() - - shell = read_and_preprocess(options.shell_path) - assert '{{{ SCRIPT }}}' in shell, 'HTML shell must contain {{{ SCRIPT }}} , see src/shell.html for an example' - base_js_target = os.path.basename(js_target) - - if settings.PROXY_TO_WORKER: - proxy_worker_filename = (settings.PROXY_TO_WORKER_FILENAME or target_basename) + '.js' - worker_js = worker_js_script(proxy_worker_filename) - script.inline = (''' - var filename = '%s'; - if ((',' + window.location.search.substr(1) + ',').indexOf(',noProxy,') < 0) { - console.log('running code in a web worker'); -''' % get_subresource_location(proxy_worker_filename)) + worker_js + ''' - } else { - console.log('running code on the main thread'); - var fileBytes = tryParseAsDataURI(filename); - var script = document.createElement('script'); - if (fileBytes) { - script.innerHTML = intArrayToString(fileBytes); - } else { - script.src = filename; - } - document.body.appendChild(script); - } -''' - else: - # Normal code generation path - script.src = base_js_target - - if not settings.SINGLE_FILE: - if memfile and not settings.MINIMAL_RUNTIME: - # start to load the memory init file in the HTML, in parallel with the JS - script.un_src() - script.inline = (''' - var memoryInitializer = '%s'; - memoryInitializer = Module['locateFile'] ? Module['locateFile'](memoryInitializer, '') : memoryInitializer; - Module['memoryInitializerRequestURL'] = memoryInitializer; - var meminitXHR = Module['memoryInitializerRequest'] = new XMLHttpRequest(); - meminitXHR.open('GET', memoryInitializer, true); - meminitXHR.responseType = 'arraybuffer'; - meminitXHR.send(null); -''' % get_subresource_location(memfile)) + script.inline - - if not settings.WASM_ASYNC_COMPILATION: - # We need to load the wasm file before anything else, it has to be synchronously ready TODO: optimize - script.un_src() - script.inline = ''' - var wasmURL = '%s'; - var wasmXHR = new XMLHttpRequest(); - wasmXHR.open('GET', wasmURL, true); - wasmXHR.responseType = 'arraybuffer'; - wasmXHR.onload = function() { - if (wasmXHR.status === 200 || wasmXHR.status === 0) { - Module.wasmBinary = wasmXHR.response; - } else { - var wasmURLBytes = tryParseAsDataURI(wasmURL); - if (wasmURLBytes) { - Module.wasmBinary = wasmURLBytes.buffer; - } - } -%s - }; - wasmXHR.send(null); -''' % (get_subresource_location(wasm_target), script.inline) - - if settings.WASM == 2: - # If target browser does not support WebAssembly, we need to load the .wasm.js file before the main .js file. - script.un_src() - script.inline = ''' - function loadMainJs() { -%s - } - if (!window.WebAssembly || location.search.indexOf('_rwasm=0') > 0) { - // Current browser does not support WebAssembly, load the .wasm.js JavaScript fallback - // before the main JS runtime. - var wasm2js = document.createElement('script'); - wasm2js.src = '%s'; - wasm2js.onload = loadMainJs; - document.body.appendChild(wasm2js); - } else { - // Current browser supports Wasm, proceed with loading the main JS runtime. - loadMainJs(); - } -''' % (script.inline, get_subresource_location(wasm_target) + '.js') - - # when script.inline isn't empty, add required helper functions such as tryParseAsDataURI - if script.inline: - for filename in ('arrayUtils.js', 'base64Utils.js', 'URIUtils.js'): - content = read_and_preprocess(utils.path_from_root('src', filename)) - script.inline = content + script.inline - - script.inline = 'var ASSERTIONS = %s;\n%s' % (settings.ASSERTIONS, script.inline) - - # inline script for SINGLE_FILE output - if settings.SINGLE_FILE: - js_contents = script.inline or '' - if script.src: - js_contents += read_file(js_target) - delete_file(js_target) - script.src = None - script.inline = js_contents - - html_contents = do_replace(shell, '{{{ SCRIPT }}}', script.replacement()) - html_contents = tools.line_endings.convert_line_endings(html_contents, '\n', options.output_eol) - - try: - # Force UTF-8 output for consistency across platforms and with the web. - utils.write_binary(target, html_contents.encode('utf-8')) - except OSError as e: - exit_with_error(f'cannot write output file: {e}') - - -def minify_html(filename): - if settings.DEBUG_LEVEL >= 2: - return - - opts = [] - # -g1 and greater retain whitespace and comments in source - if settings.DEBUG_LEVEL == 0: - opts += ['--collapse-whitespace', - '--collapse-inline-tag-whitespace', - '--remove-comments', - '--remove-tag-whitespace', - '--sort-attributes', - '--sort-class-name'] - # -g2 and greater do not minify HTML at all - if settings.DEBUG_LEVEL <= 1: - opts += ['--decode-entities', - '--collapse-boolean-attributes', - '--remove-attribute-quotes', - '--remove-redundant-attributes', - '--remove-script-type-attributes', - '--remove-style-link-type-attributes', - '--use-short-doctype', - '--minify-css', 'true', - '--minify-js', 'true'] - - # html-minifier also has the following options, but they look unsafe for use: - # '--remove-optional-tags': removes e.g. and tags from the page. - # (Breaks at least browser.test_sdl2glshader) - # '--remove-empty-attributes': removes all attributes with whitespace-only values. - # (Breaks at least browser.test_asmfs_hello_file) - # '--remove-empty-elements': removes all elements with empty contents. - # (Breaks at least browser.test_asm_swapping) - - logger.debug(f'minifying HTML file {filename}') - size_before = os.path.getsize(filename) - start_time = time.time() - shared.check_call(shared.get_npm_cmd('html-minifier-terser') + [filename, '-o', filename] + opts, env=shared.env_with_node_in_path()) - - elapsed_time = time.time() - start_time - size_after = os.path.getsize(filename) - delta = size_after - size_before - logger.debug(f'HTML minification took {elapsed_time:.2f} seconds, and shrunk size of {filename} from {size_before} to {size_after} bytes, delta={delta} ({delta * 100.0 / size_before:+.2f}%)') - - -def generate_html(target, options, js_target, target_basename, - wasm_target, memfile): - logger.debug('generating HTML') - - if settings.EXPORT_NAME != 'Module' and \ - not settings.MINIMAL_RUNTIME and \ - options.shell_path == utils.path_from_root('src/shell.html'): - # the minimal runtime shell HTML is designed to support changing the export - # name, but the normal one does not support that currently - exit_with_error('Customizing EXPORT_NAME requires that the HTML be customized to use that name (see https://github.com/emscripten-core/emscripten/issues/10086)') - - if settings.MINIMAL_RUNTIME: - generate_minimal_runtime_html(target, options, js_target, target_basename) - else: - generate_traditional_runtime_html(target, options, js_target, target_basename, - wasm_target, memfile) - - if settings.MINIFY_HTML and (settings.OPT_LEVEL >= 1 or settings.SHRINK_LEVEL >= 1): - minify_html(target) - - -def generate_worker_js(target, js_target, target_basename): - # compiler output is embedded as base64 - if settings.SINGLE_FILE: - proxy_worker_filename = get_subresource_location(js_target) - - # compiler output goes in .worker.js file - else: - move_file(js_target, shared.replace_suffix(js_target, '.worker.js')) - worker_target_basename = target_basename + '.worker' - proxy_worker_filename = (settings.PROXY_TO_WORKER_FILENAME or worker_target_basename) + '.js' - - target_contents = worker_js_script(proxy_worker_filename) - write_file(target, target_contents) - - -def worker_js_script(proxy_worker_filename): - web_gl_client_src = read_file(utils.path_from_root('src/webGLClient.js')) - idb_store_src = read_file(utils.path_from_root('src/IDBStore.js')) - proxy_client_src = read_file(utils.path_from_root('src/proxyClient.js')) - proxy_client_src = do_replace(proxy_client_src, '{{{ filename }}}', proxy_worker_filename) - proxy_client_src = do_replace(proxy_client_src, '{{{ IDBStore.js }}}', idb_store_src) - return web_gl_client_src + '\n' + proxy_client_src - - -def find_library(lib, lib_dirs): - for lib_dir in lib_dirs: - path = os.path.join(lib_dir, lib) - if os.path.isfile(path): - logger.debug('found library "%s" at %s', lib, path) - return path - return None - - -def process_libraries(state, linker_inputs): - new_flags = [] - libraries = [] - suffixes = STATICLIB_ENDINGS + DYNAMICLIB_ENDINGS - system_libs_map = system_libs.Library.get_usable_variations() - - # Find library files - for i, flag in state.link_flags: - if not flag.startswith('-l'): - new_flags.append((i, flag)) - continue - lib = strip_prefix(flag, '-l') - - logger.debug('looking for library "%s"', lib) - js_libs, native_lib = building.map_to_js_libs(lib) - if js_libs is not None: - libraries += [(i, js_lib) for js_lib in js_libs] - # If native_lib is returned then include it in the link - # via forced_stdlibs. - if native_lib: - state.forced_stdlibs.append(native_lib) - continue - - # We don't need to resolve system libraries to absolute paths here, we can just - # let wasm-ld handle that. However, we do want to map to the correct variant. - # For example we map `-lc` to `-lc-mt` if we are building with threading support. - if 'lib' + lib in system_libs_map: - lib = system_libs_map['lib' + lib] - new_flags.append((i, '-l' + strip_prefix(lib.get_base_name(), 'lib'))) - continue - - if building.map_and_apply_to_settings(lib): - continue - - path = None - for suff in suffixes: - name = 'lib' + lib + suff - path = find_library(name, state.lib_dirs) - if path: - break - - if path: - linker_inputs.append((i, path)) - continue - - new_flags.append((i, flag)) - - settings.JS_LIBRARIES += libraries - - # At this point processing JS_LIBRARIES is finished, no more items will be added to it. - # Sort the input list from (order, lib_name) pairs to a flat array in the right order. - settings.JS_LIBRARIES.sort(key=lambda lib: lib[0]) - settings.JS_LIBRARIES = [lib[1] for lib in settings.JS_LIBRARIES] - state.link_flags = new_flags - - for _, f in linker_inputs: - if building.is_ar(f): - ensure_archive_index(f) - - -class ScriptSource: - def __init__(self): - self.src = None # if set, we have a script to load with a src attribute - self.inline = None # if set, we have the contents of a script to write inline in a script - - def un_src(self): - """Use this if you want to modify the script and need it to be inline.""" - if self.src is None: - return - quoted_src = quote(self.src) - if settings.EXPORT_ES6: - self.inline = f''' - import("./{quoted_src}").then(exports => exports.default(Module)) - ''' - else: - self.inline = f''' - var script = document.createElement('script'); - script.src = "{quoted_src}"; - document.body.appendChild(script); - ''' - self.src = None - - def replacement(self): - """Returns the script tag to replace the {{{ SCRIPT }}} tag in the target""" - assert (self.src or self.inline) and not (self.src and self.inline) - if self.src: - quoted_src = quote(self.src) - if settings.EXPORT_ES6: - return f''' - - ''' - else: - return f'' - else: - return '' % self.inline - - -def is_valid_abspath(options, path_name): - # Any path that is underneath the emscripten repository root must be ok. - if utils.path_from_root().replace('\\', '/') in path_name.replace('\\', '/'): - return True - - def in_directory(root, child): - # make both path absolute - root = os.path.realpath(root) - child = os.path.realpath(child) - - # return true, if the common prefix of both is equal to directory - # e.g. /a/b/c/d.rst and directory is /a/b, the common prefix is /a/b - return os.path.commonprefix([root, child]) == root - - for valid_abspath in options.valid_abspaths: - if in_directory(valid_abspath, path_name): - return True - return False - - -def parse_symbol_list_file(contents): - """Parse contents of one-symbol-per-line response file. This format can by used - with, for example, -sEXPORTED_FUNCTIONS=@filename and avoids the need for any - kind of quoting or escaping. - """ - values = contents.splitlines() - return [v.strip() for v in values] - - -def parse_value(text, expected_type): - # Note that using response files can introduce whitespace, if the file - # has a newline at the end. For that reason, we rstrip() in relevant - # places here. - def parse_string_value(text): - first = text[0] - if first == "'" or first == '"': - text = text.rstrip() - assert text[-1] == text[0] and len(text) > 1, 'unclosed opened quoted string. expected final character to be "%s" and length to be greater than 1 in "%s"' % (text[0], text) - return text[1:-1] - return text - - def parse_string_list_members(text): - sep = ',' - values = text.split(sep) - result = [] - index = 0 - while True: - current = values[index].lstrip() # Cannot safely rstrip for cases like: "HERE-> ," - if not len(current): - exit_with_error('string array should not contain an empty value') - first = current[0] - if not (first == "'" or first == '"'): - result.append(current.rstrip()) - else: - start = index - while True: # Continue until closing quote found - if index >= len(values): - exit_with_error("unclosed quoted string. expected final character to be '%s' in '%s'" % (first, values[start])) - new = values[index].rstrip() - if new and new[-1] == first: - if start == index: - result.append(current.rstrip()[1:-1]) - else: - result.append((current + sep + new)[1:-1]) - break - else: - current += sep + values[index] - index += 1 - - index += 1 - if index >= len(values): - break - return result - - def parse_string_list(text): - text = text.rstrip() - if text and text[0] == '[': - if text[-1] != ']': - exit_with_error('unclosed opened string list. expected final character to be "]" in "%s"' % (text)) - text = text[1:-1] - if text.strip() == "": - return [] - return parse_string_list_members(text) - - if expected_type == list or (text and text[0] == '['): - # if json parsing fails, we fall back to our own parser, which can handle a few - # simpler syntaxes - try: - return json.loads(text) - except ValueError: - return parse_string_list(text) - - if expected_type == float: - try: - return float(text) - except ValueError: - pass - - try: - if text.startswith('0x'): - base = 16 - else: - base = 10 - return int(text, base) - except ValueError: - return parse_string_value(text) - - -def validate_arg_level(level_string, max_level, err_msg, clamp=False): - try: - level = int(level_string) - except ValueError: - exit_with_error(err_msg) - if clamp: - if level > max_level: - logger.warning("optimization level '-O" + level_string + "' is not supported; using '-O" + str(max_level) + "' instead") - level = max_level - if not 0 <= level <= max_level: - exit_with_error(err_msg) - return level - - -def is_int(s): - try: - int(s) - return True - except ValueError: - return False - - -@ToolchainProfiler.profile() -def main(args): - start_time = time.time() - ret = run(args) - logger.debug('total time: %.2f seconds', (time.time() - start_time)) - return ret - - -if __name__ == '__main__': - try: - sys.exit(main(sys.argv)) - except KeyboardInterrupt: - logger.debug('KeyboardInterrupt') - sys.exit(1) diff --git a/icu/icu4c/source/data/curr/root.txt b/icu/icu4c/source/data/curr/root.txt index 7a715b0e019..428f2176c4c 100644 --- a/icu/icu4c/source/data/curr/root.txt +++ b/icu/icu4c/source/data/curr/root.txt @@ -1,215 +1,206 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// Generated using tools/cldr/cldr-to-icu/build-icu-data.xml +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html#License root{ Currencies{ - AUD{ - "A$", - "AUD", + ETB{ + "ብር", + "ETB", } - BRL{ - "R$", - "BRL", + SAR{ + "ر.س.‏", + "SAR", } - CAD{ - "CA$", - "CAD", + BGN{ + "лв.", + "BGN", } - CNY{ - "CN¥", - "CNY", + BDT{ + "৳", + "BDT", + } + INR{ + "₹", + "INR", } EUR{ "€", "EUR", } + CZK{ + "Kč", + "CZK", + } + DKK{ + "kr.", + "DKK", + } + CHF{ + "CHF", + "CHF", + } + CAD{ + "$", + "CAD", + } GBP{ "£", "GBP", } - HKD{ - "HK$", - "HKD", + USD{ + "$", + "USD", + } + MXN{ + "$", + "MXN", + } + AUD{ + "$", + "AUD", + } + NZD{ + "$", + "NZD", + } + IRR{ + "ریال", + "IRR", + } + PHP{ + "₱", + "PHP", } ILS{ "₪", "ILS", } - INR{ - "₹", - "INR", + BAM{ + "KM", + "BAM", + } + HRK{ + "HRK", + "HRK", + } + HUF{ + "Ft", + "HUF", + } + IDR{ + "Rp", + "IDR", } JPY{ - "JP¥", + "¥", "JPY", } KRW{ "₩", "KRW", } - MXN{ - "MX$", - "MXN", + BND{ + "$", + "BND", } - NZD{ - "NZ$", - "NZD", + MYR{ + "RM", + "MYR", } - PHP{ - "₱", - "PHP", + SGD{ + "$", + "SGD", } - TWD{ - "NT$", - "TWD", + AWG{ + "Afl.", + "AWG", } - USD{ - "US$", - "USD", + PLN{ + "zł", + "PLN", + } + BRL{ + "R$", + "BRL", + } + RON{ + "RON", + "RON", + } + RUB{ + "₽", + "RUB", + } + RSD{ + "RSD", + "RSD", + } + SEK{ + "kr", + "SEK", + } + CDF{ + "FC", + "CDF", + } + KES{ + "Ksh", + "KES", + } + TZS{ + "TSh", + "TZS", + } + UGX{ + "USh", + "UGX", + } + LKR{ + "Rs.", + "LKR", + } + THB{ + "฿", + "THB", + } + TRY{ + "₺", + "TRY", + } + UAH{ + "₴", + "UAH", } VND{ "₫", "VND", } - XAF{ - "FCFA", - "XAF", - } - XCD{ - "EC$", - "XCD", + CNY{ + "¥", + "CNY", } - XOF{ - "F CFA", - "XOF", + HKD{ + "HK$", + "HKD", } - XPF{ - "CFPF", - "XPF", + TWD{ + "$", + "TWD", } XXX{ "¤", "XXX", } } - Currencies%narrow{ - AFN{"؋"} - AMD{"֏"} - AOA{"Kz"} - ARS{"$"} - AUD{"$"} - AZN{"₼"} - BAM{"KM"} - BBD{"$"} - BDT{"৳"} - BMD{"$"} - BND{"$"} - BOB{"Bs"} - BRL{"R$"} - BSD{"$"} - BWP{"P"} - BZD{"$"} - CAD{"$"} - CLP{"$"} - CNY{"¥"} - COP{"$"} - CRC{"₡"} - CUC{"$"} - CUP{"$"} - CZK{"Kč"} - DKK{"kr"} - DOP{"$"} - EGP{"E£"} - ESP{"₧"} - EUR{"€"} - FJD{"$"} - FKP{"£"} - GBP{"£"} - GEL{"₾"} - GHS{"GH₵"} - GIP{"£"} - GNF{"FG"} - GTQ{"Q"} - GYD{"$"} - HKD{"$"} - HNL{"L"} - HRK{"kn"} - HUF{"Ft"} - IDR{"Rp"} - ILS{"₪"} - INR{"₹"} - ISK{"kr"} - JMD{"$"} - JPY{"¥"} - KHR{"៛"} - KMF{"CF"} - KPW{"₩"} - KRW{"₩"} - KYD{"$"} - KZT{"₸"} - LAK{"₭"} - LBP{"L£"} - LKR{"Rs"} - LRD{"$"} - LTL{"Lt"} - LVL{"Ls"} - MGA{"Ar"} - MMK{"K"} - MNT{"₮"} - MUR{"Rs"} - MXN{"$"} - MYR{"RM"} - NAD{"$"} - NGN{"₦"} - NIO{"C$"} - NOK{"kr"} - NPR{"Rs"} - NZD{"$"} - PHP{"₱"} - PKR{"Rs"} - PLN{"zł"} - PYG{"₲"} - RON{"lei"} - RUB{"₽"} - RWF{"RF"} - SBD{"$"} - SEK{"kr"} - SGD{"$"} - SHP{"£"} - SRD{"$"} - SSP{"£"} - STN{"Db"} - SYP{"£"} - THB{"฿"} - TOP{"T$"} - TRY{"₺"} - TTD{"$"} - TWD{"$"} - UAH{"₴"} - USD{"$"} - UYU{"$"} - VEF{"Bs"} - VND{"₫"} - XCD{"$"} - ZAR{"R"} - ZMW{"ZK"} - } - Currencies%variant{ - TRY{"TL"} - } - CurrencyUnitPatterns{ - other{"{0} {1}"} - } - Version{"42"} + Version{"37"} currencySpacing{ afterCurrency{ - currencyMatch{"[[:^S:]&[:^Z:]]"} - insertBetween{" "} + currencyMatch{"[:^S:]"} + insertBetween{" "} surroundingMatch{"[:digit:]"} } beforeCurrency{ - currencyMatch{"[[:^S:]&[:^Z:]]"} - insertBetween{" "} + currencyMatch{"[:^S:]"} + insertBetween{" "} surroundingMatch{"[:digit:]"} } }