diff options
Diffstat (limited to 'emcc')
-rwxr-xr-x | emcc | 1256 |
1 files changed, 737 insertions, 519 deletions
@@ -47,13 +47,27 @@ emcc can be influenced by a few environment variables: EMMAKEN_COMPILER - The compiler to be used, if you don't want the default clang. ''' -import os, sys, shutil, tempfile, subprocess, shlex, time, re, logging +import os, sys, shutil, tempfile, subprocess, shlex, time, re, logging, json from subprocess import PIPE, STDOUT -from tools import shared -from tools.shared import Compression, execute, suffix, unsuffixed, unsuffixed_basename +from tools import shared, jsrun, system_libs +from tools.shared import Compression, execute, suffix, unsuffixed, unsuffixed_basename, WINDOWS from tools.response_file import read_response_file -logging = logging.getLogger('emcc') +# endings = dot + a suffix, safe to test by filename.endswith(endings) +C_ENDINGS = ('.c', '.C') +CXX_ENDINGS = ('.cpp', '.cxx', '.cc', '.CPP', '.CXX', '.CC') +OBJC_ENDINGS = ('.m',) +OBJCXX_ENDINGS = ('.mm',) +SOURCE_ENDINGS = C_ENDINGS + CXX_ENDINGS + OBJC_ENDINGS + OBJCXX_ENDINGS +BITCODE_ENDINGS = ('.bc', '.o', '.obj') +DYNAMICLIB_ENDINGS = ('.dylib', '.so', '.dll') +STATICLIB_ENDINGS = ('.a',) +ASSEMBLY_ENDINGS = ('.ll',) +HEADER_ENDINGS = ('.h', '.hxx', '.hpp', '.hh', '.H', '.HXX', '.HPP', '.HH') + +LIB_PREFIXES = ('', 'lib') + +JS_CONTAINING_SUFFIXES = ('js', 'html') # Mapping of emcc opt levels to llvm opt levels. We use llvm opt level 3 in emcc opt # levels 2 and 3 (emcc 3 is unsafe opts, so unsuitable for the only level to get @@ -120,39 +134,39 @@ Most normal gcc/g++ options will work, for example: --version Display compiler version information Options that are modified or new in %s include: - -O0 No optimizations (default) + + -O0 No optimizations (default). This is the recommended + setting for starting to port a project, as it + includes various assertions. + -O1 Simple optimizations, including asm.js, LLVM -O1 - optimizations, and no runtime assertions + optimizations, relooping, and no runtime assertions or C++ exception catching (to re-enable C++ exception catching, use - -s DISABLE_EXCEPTION_CATCHING=0 ). - (For details on the affects of different - opt levels, see apply_opt_level() in - tools/shared.py and also src/settings.js.) - Note: Optimizations are only done when - compiling to JavaScript, not to intermediate - bitcode, *unless* you build with - EMCC_OPTIMIZE_NORMALLY=1 (not recommended - unless you know what you are doing!) - -O2 As -O1, plus the relooper (loop recreation), - LLVM -O2 optimizations, and + -s DISABLE_EXCEPTION_CATCHING=0 ), and enables -s ALIASING_FUNCTION_POINTERS=1 - -O3 As -O2, plus dangerous optimizations that may - break the generated code! This adds + This is the recommended setting when you want a + reasonably optimized build that is generated as + quickly as possible (it builds much faster than -O2). + + (Note: for details on the affects of different + opt levels, see apply_opt_level() in + tools/shared.py and also src/settings.js.) - -s FORCE_ALIGNED_MEMORY=1 - -s DOUBLE_MODE=0 - -s PRECISE_I64_MATH=0 - --closure 1 - --llvm-lto 1 + -O2 As -O1, plus various js-level optimizations and + LLVM -O3 optimizations. This is the recommended + setting for a release build: slower compilation + time in return for the smallest and fastest + output. - This is not recommended at all. A better idea - is to try each of these separately on top of - -O2 to see what works. See the wiki and - src/settings.js (for the -s options) for more - information. + -O3 As -O2, plus additional optimizations that can + take a significant amount of compilation time and/or + are relatively new. + + For tips on optimizing your code, see + https://github.com/kripken/emscripten/wiki/Optimizing-Code -s OPTION=VALUE JavaScript code generation option passed into the emscripten compiler. For the @@ -184,39 +198,71 @@ Options that are modified or new in %s include: ). Note that the path must be absolute, not relative. - -g Use debug info. Note that you need this during - the last compilation phase from bitcode to - JavaScript, or else we will remove it by - default in -O1 and above. - In -O0, line numbers wil be shown in the - generated code. In -O1 and above, the optimizer - removes those comments. This flag does however - have the effect of disabling anything that - causes name mangling or minification (closure - or the registerize pass). + -g Use debug info. When compiling to bitcode, + this is the same as in clang and gcc, it + adds debug info to the object files. When + compiling from source to JS or bitcode to JS, + it is equivalent to -g3 (keep code as debuggable + as possible, except for discarding LLVM + debug info, so no C/C++ line numbers; use + -g4 to get line number debugging info in JS). + + -g<level> When compiling from bitcode to JS, we can + keep the code debuggable to different + degrees. Each of these levels builds on the + previous: + + -g0 Make no effort to keep code debuggable. + Will discard LLVM debug info. (default + in -O1+) + -g1 Preserve (do not minify) whitespace + -g2 Preserve function names + -g3 Preserve variable names + -g4 Preserve LLVM debug info (if -g was + used when compiling the C/C++ sources), + show line number debug comments, and + generate source maps. This is the highest + level of debuggability. Note that this + may make -O1 and above significantly + slower because JS optimization will be + limited to 1 core. (default in -O0) --typed-arrays <mode> 0: No typed arrays 1: Parallel typed arrays 2: Shared (C-like) typed arrays (default) + --js-opts 0: Prevent JS optimizer from running + 1: Use JS optimizer (default) + --llvm-opts <level> 0: No LLVM optimizations (default in -O0) 1: -O1 LLVM optimizations (default in -O1) 2: -O2 LLVM optimizations 3: -O3 LLVM optimizations (default in -O2+) - --llvm-lto <level> 0: No LLVM LTO (default in -O2 and below) - 1: LLVM LTO (default in -O3) + You can also specify arbitrary LLVM options, e.g. + + --llvm-opts "['-O3', '-somethingelse']" + + --llvm-lto <level> 0: No LLVM LTO (default) + 1: LLVM LTO is performed + 2: We combine all the bitcode and run LLVM opt -O3 + on that (which optimizes across modules, but is + not the same as normal LTO), but do not do normal + LTO + 3: We do both 2 and then 1 Note: If LLVM optimizations are not run - (see --llvm-opts), setting this to 1 has no + (see --llvm-opts), setting this has no effect. + Note that LLVM LTO is not perfectly stable yet, + and can can cause code to behave incorrectly. + --closure <on> 0: No closure compiler (default in -O2 and below) 1: Run closure compiler. This greatly reduces code size and may in some cases increase runtime speed (although the opposite can also occur). Note that it takes time to run, and - may require some changes to the code. This - is run by default in -O3. + may require some changes to the code. In asm.js mode, closure will only be used on the 'shell' code around the compiled code (the @@ -227,6 +273,9 @@ Options that are modified or new in %s include: try adjusting JAVA_HEAP_SIZE in the environment (for example, to 4096m for 4GB). + Note: Closure is only run if js opts are being + done (-O2 or above, or --js-opts 1). + --js-transform <cmd> <cmd> will be called on the generated code before it is optimized. This lets you modify the JavaScript, for example adding some code @@ -264,6 +313,13 @@ Options that are modified or new in %s include: If a directory is passed here, its entire contents will be embedded. + Note: Embedding files is much less + efficient than preloading them. You + should only use it for small amounts + of small files. Instead, use + --preload-file which emits efficient + binary data. + --preload-file <name> A file to preload before running the compiled code asynchronously. Otherwise similar to --embed-file, except that this @@ -286,7 +342,16 @@ Options that are modified or new in %s include: your main compiled code (or run it before in some other way). - --compression <codec> Compress both the compiled code and embedded/ + For more docs on the options --preload-file + accepts, see https://github.com/kripken/emscripten/wiki/Filesystem-Guide + + --exclude-file <name> Files and directories to be excluded from + --embed-file and --preload-file + wildcard is supported + + --compression <codec> **THIS OPTION IS DEPRECATED** + + Compress both the compiled code and embedded/ preloaded files. <codec> should be a triple, <native_encoder>,<js_decoder>,<js_name> @@ -305,12 +370,7 @@ Options that are modified or new in %s include: archive, which is given the same name as the output HTML but with suffix .data.compress - --minify <on> 0: Do not minify the generated JavaScript's - whitespace (default in -O0, -O1, or if - -g is used) - 1: Minify the generated JavaScript's - whitespace (default in -O2+, assuming - -g is not used) + --minify 0 Identical to -g1 --split <size> Splits the resulting javascript file into pieces to ease debugging. This option only works if @@ -333,6 +393,9 @@ Options that are modified or new in %s include: The main file resides in the base directory and has the suffix ".js". + Note: this option is deprecated (modern JS debuggers + should work ok even on large files) + --bind Compiles the source code using the "embind" bindings approach, which connects C/C++ and JS. @@ -348,7 +411,8 @@ Options that are modified or new in %s include: --shell-file <path> The path name to a skeleton HTML file used when generating HTML output. The shell file used needs to have this token inside it: - {{{ SCRIPT_CODE }}} + {{{ SCRIPT }}} + (see src/shell.html for an example) Note that this argument is ignored if a target other than HTML is specified using the -o option. @@ -434,16 +498,47 @@ Options that are modified or new in %s include: libraries, and after any link-time optimizations (if any). - --memory-init-file <on> If on, we generate a separate memory initialization - file. This is more efficient than storing the - memory initialization data embedded inside - JavaScript as text. (default is off) + --memory-init-file <on> 0: Do not emit a separate memory initialization + file, keep the static initialization inside + the generated JavaScript as text (default) + 1: Emit a separate memory initialization file + in binary format. This is more efficient than + storing it as text inside JavaScript, but does + mean you have another file to publish. + + -Wno-warn-absolute-paths If not specified, the compiler will warn about any + uses of absolute paths in -I and -L command line + directives. Pass this flag on the command line + to hide these warnings and acknowledge that the + explicit use of absolute paths is intentional. + + --proxy-to-worker Generates both html and js files. The main + program is in js, and the html proxies to/from it. + + --emrun Enables the generated output to be aware of the + emrun command line tool. This allows stdout, stderr + and exit(returncode) capture when running the + generated application through emrun. + + --em-config Specifies the location of the .emscripten configuration + file for the current compiler run. If not specified, + the environment variable EM_CONFIG is read for this + file, and if that is not set, the default location + ~/.emscripten is assumed. + + --default-obj-ext .ext Specifies the file suffix to generate if the location + of a directory name is passed to -o directive, e.g. + emcc -c a.c -o dir/ + will by default generate an output name 'dir/a.o', + but this cmdline param can be passed to generate a + file with a custom suffix 'dir/a.ext'. The target file, if specified (-o <target>), defines what will be generated: <name>.js JavaScript - <name>.html HTML with embedded JavaScript + <name>.html HTML + side JavaScript file (<name>.js) + (JS on the side improves page load time) <name>.bc LLVM bitcode (default) <name>.o LLVM bitcode (same as .bc) @@ -484,7 +579,7 @@ There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR P exit(0) elif len(sys.argv) == 2 and sys.argv[1] == '-v': # -v with no inputs - print 'emcc (Emscripten GCC-like replacement + linker emulating GNU ld ) 2.0' + print 'emcc (Emscripten GCC-like replacement + linker emulating GNU ld ) %s' % shared.EMSCRIPTEN_VERSION exit(subprocess.call([shared.CLANG, '-v'])) def is_minus_s_for_emcc(newargs,i): @@ -511,16 +606,18 @@ if CONFIGURE_CONFIG or CMAKE_CONFIG: open(tempout, 'w').write('//\n') src = None - for i in range(len(sys.argv)): - if sys.argv[i].endswith('.c'): + for arg in sys.argv: + if arg.endswith('.c'): try: - src = open(sys.argv[i]).read() - if debug_configure: open(tempout, 'a').write('============= ' + sys.argv[i] + '\n' + src + '\n=============\n\n') + src = open(arg).read() + if debug_configure: open(tempout, 'a').write('============= ' + arg + '\n' + src + '\n=============\n\n') except: pass - if sys.argv[i].endswith('.s'): + elif arg.endswith('.s'): if debug_configure: open(tempout, 'a').write('(compiling .s assembly, must use clang\n') use_js = 0 + elif arg == '-E': + use_js = 0 if src: if 'fopen' in src and '"w"' in src: @@ -539,7 +636,7 @@ if CONFIGURE_CONFIG or CMAKE_CONFIG: skip_next = False idx += 1 continue - if el == '-s' and is_minus_s_for_emcc(argv, idx): + if not use_js and el == '-s' and is_minus_s_for_emcc(argv, idx): # skip -s X=Y if not using js for configure skip_next = True else: yield el @@ -597,19 +694,10 @@ if os.environ.get('EMMAKEN_CXX'): CC_ADDITIONAL_ARGS = shared.COMPILER_OPTS EMMAKEN_CFLAGS = os.environ.get('EMMAKEN_CFLAGS') -if EMMAKEN_CFLAGS: CC_ADDITIONAL_ARGS += shlex.split(EMMAKEN_CFLAGS) +if EMMAKEN_CFLAGS: sys.argv += shlex.split(EMMAKEN_CFLAGS) # ---------------- Utilities --------------- -SOURCE_SUFFIXES = ('.c', '.cpp', '.cxx', '.cc', '.m', '.mm') -BITCODE_SUFFIXES = ('.bc', '.o', '.obj') -DYNAMICLIB_SUFFIXES = ('.dylib', '.so', '.dll') -STATICLIB_SUFFIXES = ('.a',) -ASSEMBLY_SUFFIXES = ('.ll',) -LIB_PREFIXES = ('', 'lib') - -JS_CONTAINING_SUFFIXES = ('js', 'html') - seen_names = {} def uniquename(name): if name not in seen_names: @@ -624,15 +712,12 @@ if len(sys.argv) == 1 or sys.argv[1] in ['x', 't']: sys.exit(0) use_cxx = True -header = False # pre-compiled headers. We fake that by just copying the file for i in range(1, len(sys.argv)): arg = sys.argv[i] if not arg.startswith('-'): - if arg.endswith(('.c','.m')): + if arg.endswith(C_ENDINGS + OBJC_ENDINGS): use_cxx = False - if arg.endswith('.h') and sys.argv[i-1] != '-include': - header = True if '-M' in sys.argv or '-MM' in sys.argv: # Just output dependencies, do not compile. Warning: clang and gcc behave differently with -MF! (clang seems to not recognize it) @@ -640,6 +725,12 @@ if '-M' in sys.argv or '-MM' in sys.argv: logging.debug('just dependencies: ' + ' '.join(cmd)) exit(subprocess.call(cmd)) +if '-E' in sys.argv: + # Just run the preprocessor + cmd = [CC] + sys.argv[1:] + logging.debug('just preprocessor ' + ' '.join(cmd)) + exit(subprocess.call(cmd)) + # Check if a target is specified target = None for i in range(len(sys.argv)-1): @@ -660,15 +751,6 @@ if '.' in target: else: final_suffix = '' -if header: # header or such - if len(sys.argv) >= 3: # if there is a source and a target, then copy, otherwise do nothing - sys.argv = filter(lambda arg: not arg.startswith('-I'), sys.argv) - logging.debug('Just copy:' + sys.argv[-1] + target) - shutil.copy(sys.argv[-1], target) - else: - logging.debug('No-op.') - exit(0) - if TEMP_DIR: temp_dir = TEMP_DIR if os.path.exists(temp_dir): @@ -683,6 +765,25 @@ else: def in_temp(name): return os.path.join(temp_dir, os.path.basename(name)) +# 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' +def filename_type_suffix(filename): + for i in reversed(filename.split('.')[1:]): + if not i.isdigit(): + return i + return '' + +def filename_type_ending(filename): + suffix = filename_type_suffix(filename) + return '' if not suffix else ('.' + suffix) + +# Log out times for emcc stages +log_time_last = time.time() +def log_time(name): + global log_time_last + now = time.time() + logging.debug('emcc step "%s" took %.2f seconds' % (name, now - log_time_last)) + log_time_last = now + try: call = CXX if use_cxx else CC @@ -691,27 +792,31 @@ try: newargs = sys.argv[1:] opt_level = 0 + debug_level = 0 + js_opts = None llvm_opts = None llvm_lto = None closure = None js_transform = None pre_js = '' post_js = '' - minify_whitespace = None split_js_file = None preload_files = [] embed_files = [] + exclude_files = [] compression = None ignore_dynamic_linking = False shell_path = shared.path_from_root('src', 'shell.html') js_libraries = [] - keep_llvm_debug = False - keep_js_debug = False bind = False + emrun = False jcache = False save_bc = False memory_init_file = False use_preload_cache = False + no_heap_copy = False + proxy_to_worker = False + default_object_extension = '.o' if use_cxx: default_cxx_std = '-std=c++03' # Enforce a consistent C++ standard when compiling .cpp files, if user does not specify one on the cmdline. @@ -723,8 +828,22 @@ try: absolute_warning_shown = False + # Scan for warning suppression message in advance from other cmdline flags, so that it works even if -I or -L directives are present before this. + for i in range(len(newargs)): + if newargs[i] == '-Wno-warn-absolute-paths': + newargs[i] = '' + absolute_warning_shown = True + settings_changes = [] + def validate_arg_level(level_string, max_level, err_msg): + try: + level = int(level_string) + assert 0 <= level <= max_level + except: + raise Exception(err_msg) + return level + for i in range(len(newargs)): newargs[i] = newargs[i].strip() # On Windows Vista (and possibly others), excessive spaces in the command line leak into the items in this array, so trim e.g. 'foo.cpp ' -> 'foo.cpp' if newargs[i].startswith('-O'): @@ -733,12 +852,17 @@ try: if requested_level == 's': requested_level = 2 settings_changes.append('INLINING_LIMIT=50') - try: - opt_level = int(requested_level) - assert 0 <= opt_level <= 3 - except: - raise Exception('Invalid optimization level: ' + newargs[i]) + opt_level = validate_arg_level(requested_level, 3, 'Invalid optimization level: ' + newargs[i]) + # We leave the -O option in place so that the clang front-end runs in that + # optimization mode, but we disable the actual optimization passes, as we'll + # run them seperately. + newargs.append('-mllvm') + newargs.append('-disable-llvm-optzns'); + elif newargs[i].startswith('--js-opts'): + check_bad_eq(newargs[i]) + js_opts = eval(newargs[i+1]) newargs[i] = '' + newargs[i+1] = '' elif newargs[i].startswith('--llvm-opts'): check_bad_eq(newargs[i]) llvm_opts = eval(newargs[i+1]) @@ -771,7 +895,8 @@ try: newargs[i+1] = '' elif newargs[i].startswith('--minify'): check_bad_eq(newargs[i]) - minify_whitespace = int(newargs[i+1]) + assert newargs[i+1] == '0', '0 is the only supported option for --minify; 1 has been deprecated' + debug_level = max(1, debug_level) newargs[i] = '' newargs[i+1] = '' elif newargs[i].startswith('--split'): @@ -779,9 +904,10 @@ try: split_js_file = int(newargs[i+1]) newargs[i] = '' newargs[i+1] = '' - elif newargs[i] == '-g': - keep_llvm_debug = True - keep_js_debug = True + elif newargs[i].startswith('-g'): + requested_level = newargs[i][2:] or '3' + debug_level = validate_arg_level(requested_level, 4, 'Invalid debug level: ' + newargs[i]) + newargs[i] = '-g' # we'll need this to get LLVM debug info elif newargs[i] == '--bind': bind = True newargs[i] = '' @@ -799,12 +925,27 @@ try: preload_files.append(newargs[i+1]) newargs[i] = '' newargs[i+1] = '' + elif newargs[i].startswith('--exclude-file'): + check_bad_eq(newargs[i]) + exclude_files.append(newargs[i+1]) + newargs[i] = '' + newargs[i+1] = '' elif newargs[i].startswith('--compression'): + logging.warning('--compression is deprecated. Instead, it is recommended you use native gzip compression in your webserver') check_bad_eq(newargs[i]) parts = newargs[i+1].split(',') assert len(parts) == 3, '--compression requires specifying native_encoder,js_decoder,js_name - see emcc --help. got: %s' % newargs[i+1] - Compression.encoder = parts[0] - Compression.decoder = parts[1] + def locate(tool): + if WINDOWS: + if os.path.exists(tool+'.exe'): + return tool+'.exe' + if os.path.exists(tool+'.bat'): + return tool+'.bat' + if os.path.exists(tool+'.cmd'): + return tool+'.cmd' + return tool + Compression.encoder = locate(parts[0]) + Compression.decoder = locate(parts[1]) Compression.js_name = parts[2] assert os.path.exists(Compression.encoder), 'native encoder %s does not exist' % Compression.encoder assert os.path.exists(Compression.decoder), 'js decoder %s does not exist' % Compression.decoder @@ -812,15 +953,23 @@ try: newargs[i] = '' newargs[i+1] = '' elif newargs[i].startswith('--use-preload-cache'): - use_preload_cache = True; + use_preload_cache = True + newargs[i] = '' + elif newargs[i].startswith('--no-heap-copy'): + no_heap_copy = True newargs[i] = '' elif newargs[i] == '--ignore-dynamic-linking': ignore_dynamic_linking = True newargs[i] = '' elif newargs[i] == '-v': shared.COMPILER_OPTS += ['-v'] - DEBUG = 1 os.environ['EMCC_DEBUG'] = '1' # send to child processes too + if DEBUG != 1: + # swap in debug logging + DEBUG = 1 + shared.set_logging() + logging.debug('invocation: ' + ' '.join(sys.argv)) + shared.apply_configuration() # reset config to pick up change newargs[i] = '' elif newargs[i].startswith('--shell-file'): check_bad_eq(newargs[i]) @@ -853,21 +1002,50 @@ try: memory_init_file = int(newargs[i+1]) newargs[i] = '' newargs[i+1] = '' - elif newargs[i].startswith(('-I/', '-L/')): - if not absolute_warning_shown: - logging.warning ('-I or -L of an absolute path 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)') # 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 nonportable. The emscripten bundled headers are modified to be portable, local system ones are generally not + elif newargs[i] == '--proxy-to-worker': + proxy_to_worker = True + newargs[i] = '' + elif newargs[i].startswith(('-I', '-L')): + path_name = newargs[i][2:] + if not absolute_warning_shown and os.path.isabs(path_name): + logging.warning ('-I or -L of an absolute path "' + newargs[i] + '" 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). Pass \'-Wno-warn-absolute-paths\' to emcc to hide this warning.') # 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 nonportable. The emscripten bundled headers are modified to be portable, local system ones are generally not absolute_warning_shown = True + elif newargs[i] == '--emrun': + emrun = True + newargs[i] = '' + elif newargs[i] == '--em-config': + # This option is parsed in tools/shared.py, here only clean it up from being passed to clang. + newargs[i] = '' + newargs[i+1] = '' + elif newargs[i] == '--default-obj-ext': + newargs[i] = '' + default_object_extension = newargs[i+1] + if not default_object_extension.startswith('.'): + default_object_extension = '.' + default_object_extension + newargs[i+1] = '' + newargs = [ arg for arg in newargs if arg is not '' ] # If user did not specify a default -std for C++ code, specify the emscripten default. if default_cxx_std: newargs = newargs + [default_cxx_std] + if emrun: + pre_js += open(shared.path_from_root('src', 'emrun_prejs.js')).read() + '\n' + post_js += open(shared.path_from_root('src', 'emrun_postjs.js')).read() + '\n' + + if js_opts is None: js_opts = opt_level >= 2 if llvm_opts is None: llvm_opts = LLVM_OPT_LEVEL[opt_level] - if llvm_lto is None: llvm_lto = opt_level >= 3 - if opt_level <= 0: keep_llvm_debug = keep_js_debug = True # always keep debug in -O0 - if opt_level > 0: keep_llvm_debug = False # JS optimizer wipes out llvm debug info from being visible - if closure is None and opt_level == 3: closure = True + if opt_level == 0: debug_level = 4 + + if llvm_lto is None and bind: + logging.debug('running lto for embind') # XXX this is a workaround for a pointer issue + llvm_lto = 1 + + # TODO: support source maps with js_transform + if js_transform and debug_level >= 4: + logging.warning('disabling source maps because a js transform is being done') + debug_level = 3 if DEBUG: start_time = time.time() # done after parsing arguments, which might affect debug state @@ -893,6 +1071,7 @@ try: input_files = [] has_source_inputs = False + has_header_inputs = False lib_dirs = [shared.path_from_root('system', 'local', 'lib'), shared.path_from_root('system', 'lib')] libs = [] @@ -902,35 +1081,48 @@ try: if i > 0: prev = newargs[i-1] - if prev in ['-MT', '-install_name', '-I', '-L']: continue # ignore this gcc-style argument + if prev in ['-MT', '-MF', '-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']: continue # ignore this gcc-style argument - if (os.path.islink(arg) and os.path.realpath(arg).endswith(SOURCE_SUFFIXES + BITCODE_SUFFIXES + DYNAMICLIB_SUFFIXES + ASSEMBLY_SUFFIXES)): + if (os.path.islink(arg) and os.path.realpath(arg).endswith(SOURCE_ENDINGS + BITCODE_ENDINGS + DYNAMICLIB_ENDINGS + ASSEMBLY_ENDINGS + HEADER_ENDINGS)): arg = os.path.realpath(arg) - if not arg.startswith('-') and (arg.endswith(SOURCE_SUFFIXES + BITCODE_SUFFIXES + DYNAMICLIB_SUFFIXES + ASSEMBLY_SUFFIXES) or shared.Building.is_ar(arg)): # we already removed -o <target>, so all these should be inputs - newargs[i] = '' - if os.path.exists(arg): - if arg.endswith(SOURCE_SUFFIXES): + if not arg.startswith('-'): + if not os.path.exists(arg): + logging.error('%s: No such file or directory ("%s" was expected to be an input file, based on the commandline arguments provided)' % (arg, arg)) + exit(1) + + arg_ending = filename_type_ending(arg) + if arg_ending.endswith(SOURCE_ENDINGS + BITCODE_ENDINGS + DYNAMICLIB_ENDINGS + ASSEMBLY_ENDINGS + HEADER_ENDINGS) or shared.Building.is_ar(arg): # we already removed -o <target>, so all these should be inputs + newargs[i] = '' + if arg_ending.endswith(SOURCE_ENDINGS): input_files.append(arg) has_source_inputs = True + elif arg_ending.endswith(HEADER_ENDINGS): + input_files.append(arg) + has_header_inputs = True + elif arg_ending.endswith(ASSEMBLY_ENDINGS) or shared.Building.is_bitcode(arg): # this should be bitcode, make sure it is valid + input_files.append(arg) + elif arg_ending.endswith(STATICLIB_ENDINGS + DYNAMICLIB_ENDINGS): + # if it's not, and it's a library, just add it to libs to find later + l = unsuffixed_basename(arg) + for prefix in LIB_PREFIXES: + if not prefix: continue + if l.startswith(prefix): + l = l[len(prefix):] + break + libs.append(l) + newargs[i] = '' else: - # this should be bitcode, make sure it is valid - if arg.endswith(ASSEMBLY_SUFFIXES) or shared.Building.is_bitcode(arg): - input_files.append(arg) - elif arg.endswith(STATICLIB_SUFFIXES + DYNAMICLIB_SUFFIXES): - # if it's not, and it's a library, just add it to libs to find later - l = unsuffixed_basename(arg) - for prefix in LIB_PREFIXES: - if not prefix: continue - if l.startswith(prefix): - l = l[len(prefix):] - break; - libs.append(l) - newargs[i] = '' + logging.warning(arg + ' is not valid LLVM bitcode') + elif arg_ending.endswith(STATICLIB_ENDINGS): + if not shared.Building.is_ar(arg): + if shared.Building.is_bitcode(arg): + logging.error(arg + ': File has a suffix of a static library ' + str(STATICLIB_ENDINGS) + ', but instead is an LLVM bitcode file! When linking LLVM bitcode files, use one of the suffixes ' + str(BITCODE_ENDINGS)) else: - logging.warning(arg + ' is not valid LLVM bitcode') + logging.error(arg + ': Unknown format, not a static library!') + exit(1) else: - logging.error(arg + ': No such file or directory') + logging.error(arg + ": Input file has an unknown suffix, don't know what to do with it!") exit(1) elif arg.startswith('-L'): lib_dirs.append(arg[2:]) @@ -946,21 +1138,16 @@ try: # -c means do not link in gcc, and for us, the parallel is to not go all the way to JS, but stop at bitcode has_dash_c = '-c' in newargs if has_dash_c: - assert has_source_inputs, 'Must have source code inputs to use -c' + assert has_source_inputs or has_header_inputs, 'Must have source code or header inputs to use -c' target = target_basename + '.o' final_suffix = 'o' - # do not link in libs when just generating object code (not an 'executable', i.e. JS, or a library) - if ('.' + final_suffix) in BITCODE_SUFFIXES and len(libs) > 0: - logging.warning('not linking against libraries since only compiling to bitcode') - libs = [] - # Find library files for lib in libs: logging.debug('looking for library "%s"' % lib) found = False for prefix in LIB_PREFIXES: - for suff in STATICLIB_SUFFIXES + DYNAMICLIB_SUFFIXES: + for suff in STATICLIB_ENDINGS + DYNAMICLIB_ENDINGS: name = prefix + lib + suff for lib_dir in lib_dirs: path = os.path.join(lib_dir, name) @@ -971,15 +1158,24 @@ try: break if found: break if found: break - - if ignore_dynamic_linking: - input_files = filter(lambda input_file: not input_file.endswith(DYNAMICLIB_SUFFIXES), input_files) + if not found: logging.warning('emcc: cannot find library "%s"' % lib) + + # If not compiling to JS, then we are compiling to an intermediate bitcode objects or library, so + # ignore dynamic linking, since multiple dynamic linkings can interfere with each other + if not filename_type_suffix(target) in JS_CONTAINING_SUFFIXES or ignore_dynamic_linking: + def check(input_file): + if filename_type_ending(input_file) in DYNAMICLIB_ENDINGS: + if not ignore_dynamic_linking: logging.warning('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 + input_files = filter(lambda input_file: check(input_file), input_files) if len(input_files) == 0: - logging.error('no input files\nnote that input files without a known suffix are ignored, make sure your input files end with one of: ' + str(SOURCE_SUFFIXES + BITCODE_SUFFIXES + DYNAMICLIB_SUFFIXES + STATICLIB_SUFFIXES + ASSEMBLY_SUFFIXES)) + logging.error('no input files\nnote that input files without a known suffix are ignored, make sure your input files end with one of: ' + str(SOURCE_ENDINGS + BITCODE_ENDINGS + DYNAMICLIB_ENDINGS + STATICLIB_ENDINGS + ASSEMBLY_ENDINGS + HEADER_ENDINGS)) exit(0) - newargs += CC_ADDITIONAL_ARGS + newargs = CC_ADDITIONAL_ARGS + newargs assert not (Compression.on and final_suffix != 'html'), 'Compression only works when generating HTML' @@ -995,11 +1191,44 @@ try: key, value = change.split('=') if value[0] == '@': value = '"@' + os.path.abspath(value[1:]) + '"' + value = value.replace('\\\\', '/').replace('\\', '/') # Convert backslash paths to forward slashes on Windows as well, since the JS compiler otherwise needs the backslashes escaped (alternative is to escape all input paths passing to JS, which feels clumsier to read) exec('shared.Settings.' + key + ' = ' + value) # Apply effects from settings + if bind and shared.Settings.ASM_JS: + logging.warning('disabling asm.js since embind is not ready for it yet') + shared.Settings.ASM_JS = 0 + + fastcomp = os.environ.get('EMCC_FAST_COMPILER') == '1' + + if fastcomp: + shared.Settings.ASM_JS = 1 if opt_level > 0 else 2 + assert shared.Settings.ALLOW_MEMORY_GROWTH == 0, 'memory growth not supported in fastcomp yet' + assert shared.Settings.UNALIGNED_MEMORY == 0, 'forced unaligned memory not supported in fastcomp' + assert shared.Settings.CHECK_HEAP_ALIGN == 0, 'check heap align not supported in fastcomp yet' + assert shared.Settings.SAFE_DYNCALLS == 0, 'safe dyncalls not supported in fastcomp' + assert shared.Settings.RESERVED_FUNCTION_POINTERS == 0, 'reserved function pointers not supported in fastcomp' + assert shared.Settings.ASM_HEAP_LOG == 0, 'asm heap log not supported in fastcomp' + assert shared.Settings.LABEL_DEBUG == 0, 'label debug not supported in fastcomp' + assert shared.Settings.EXECUTION_TIMEOUT == -1, 'execution timeout not supported in fastcomp' + assert shared.Settings.NAMED_GLOBALS == 0, 'named globals not supported in fastcomp' + assert shared.Settings.PGO == 0, 'pgo not supported in fastcomp' + assert shared.Settings.TARGET_LE32 == 1, 'fastcomp requires le32' + assert shared.Settings.USE_TYPED_ARRAYS == 2, 'fastcomp assumes ta2' + assert not split_js_file, '--split-js is deprecated and not supported in fastcomp' + assert not bind, 'embind not supported in fastcomp yet' + if jcache: + logging.warning('jcache is not supported in fastcomp (you should not need it anyhow), disabling') + jcache = False + + fastcomp_opts = ['-pnacl-abi-simplify-preopt', '-pnacl-abi-simplify-postopt'] + if shared.Settings.DISABLE_EXCEPTION_CATCHING != 1: + fastcomp_opts += ['-enable-emscripten-cxx-exceptions'] + if len(shared.Settings.EXCEPTION_CATCHING_WHITELIST) > 0: |