aboutsummaryrefslogtreecommitdiff
path: root/emcc
diff options
context:
space:
mode:
Diffstat (limited to 'emcc')
-rwxr-xr-xemcc1256
1 files changed, 737 insertions, 519 deletions
diff --git a/emcc b/emcc
index 65dec978..854a04d4 100755
--- a/emcc
+++ b/emcc
@@ -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: