aboutsummaryrefslogtreecommitdiff
path: root/emcc
diff options
context:
space:
mode:
Diffstat (limited to 'emcc')
-rwxr-xr-xemcc196
1 files changed, 121 insertions, 75 deletions
diff --git a/emcc b/emcc
index db3d58ac..e4def1cc 100755
--- a/emcc
+++ b/emcc
@@ -73,16 +73,15 @@ 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
+import os, sys, shutil, tempfile
from subprocess import Popen, PIPE, STDOUT
from tools import shared
DEBUG = os.environ.get('EMCC_DEBUG')
-SAVE_FILES = os.environ.get('EMCC_SAVE_FILES') # saves some of the intermediate files
+TEMP_DIR = os.environ.get('EMCC_TEMP_DIR')
################### XXX
print >> sys.stderr, '\n***This is a WORK IN PROGRESS***'
-print >> sys.stderr, '***[%s]***\n' % str(sys.argv)
################### XXX
if DEBUG: print >> sys.stderr, 'emcc: ', ' '.join(sys.argv)
@@ -113,7 +112,11 @@ Options that are modified or new in %s include:
-O0 No optimizations (default)
-O1 Simple optimizations, including safe LLVM
optimizations, and no runtime assertions
- -O2 As -O1, plus code flow optimization (relooper)
+ or C++ exception catching (to re-enable
+ C++ exception catching, use
+ -s DISABLE_EXCEPTION_CATCHING=0 )
+ -O2 As -O1, plus the relooper (loop recreation),
+ plus closure compiler
Warning: Compiling with this takes a long time!
-O3 As -O2, plus dangerous optimizations that may
break the generated code! If that happens, try
@@ -124,11 +127,14 @@ Options that are modified or new in %s include:
--typed-arrays <mode> 0: No typed arrays
1: Parallel typed arrays
2: Shared (C-like) typed arrays (default)
- --llvm-opts <level> 0: No LLVM optimizations (default)
+ --llvm-opts <level> 0: No LLVM optimizations (default in -O0)
1: Safe/portable LLVM optimizations
+ (default in -O1 and above)
2: Full, unsafe/unportable LLVM optimizations;
this will almost certainly break the
generated code!
+ --closure <on> 0: No closure compiler (default in -O0, -O1)
+ 1: Run closure compiler (default in -O2, -O3)
The target file, if specified (-o <target>), defines what will
be generated:
@@ -150,7 +156,7 @@ CONFIGURE_CONFIG = os.environ.get('EMMAKEN_JUST_CONFIGURE')
CMAKE_CONFIG = 'CMakeFiles/cmTryCompileExec.dir' in ' '.join(sys.argv)# or 'CMakeCCompilerId' in ' '.join(sys.argv)
if CONFIGURE_CONFIG or CMAKE_CONFIG:
compiler = 'g++' if 'CXXCompiler' in ' '.join(sys.argv) or os.environ.get('EMMAKEN_CXX') else 'gcc'
- cmd = [compiler] + EMSDK_OPTS + sys.argv[1:]
+ cmd = [compiler] + shared.EMSDK_OPTS + sys.argv[1:]
if DEBUG: print >> sys.stderr, 'emcc, just configuring: ', cmd
exit(os.execvp(compiler, cmd))
@@ -172,6 +178,8 @@ if EMMAKEN_CFLAGS: CC_ADDITIONAL_ARGS += EMMAKEN_CFLAGS.split(' ')
# ---------------- Utilities ---------------
+SOURCE_SUFFIXES = ('.c', '.cpp', '.cxx', '.cc')
+
def unsuffixed(name):
return '.'.join(name.split('.')[:-1])
@@ -207,7 +215,23 @@ for i in range(len(sys.argv)-1):
sys.argv = sys.argv[:i] + sys.argv[i+2:]
break
-if not header:
+if header: # header or such
+ if DEBUG: print >> sys.stderr, 'Just copy.'
+ shutil.copy(sys.argv[-1], sys.argv[-2])
+ exit(0)
+
+if TEMP_DIR:
+ temp_dir = TEMP_DIR
+ if os.path.exists(temp_dir):
+ shutil.rmtree(temp_dir) # clear it
+ os.makedirs(temp_dir)
+else:
+ temp_dir = tempfile.mkdtemp()
+
+def in_temp(name):
+ return os.path.join(temp_dir, name)
+
+try:
call = CXX if use_cxx else CC
## Parse args
@@ -215,7 +239,11 @@ if not header:
newargs = sys.argv[1:]
opt_level = 0
- llvm_opt_level = 0
+ llvm_opt_level = None
+ closure = None
+
+ def check_bad_eq(arg):
+ assert '=' not in arg, 'Invalid parameter (do not use "=" with "--" options)'
for i in range(len(newargs)):
if newargs[i].startswith('-O'):
@@ -224,17 +252,23 @@ if not header:
assert 0 <= opt_level <= 3
except:
raise Exception('Invalid optimization level: ' + newargs[i])
- if opt_level >= 1:
- llvm_opt_level = 1
newargs[i] = ''
elif newargs[i].startswith('--llvm-opts'):
- assert '=' not in newargs[i], 'Invalid llvm opts parameter (do not use "=")'
+ check_bad_eq(newargs[i])
llvm_opt_level = eval(newargs[i+1])
assert 0 <= llvm_opt_level <= 1, 'Only two levels of LLVM optimizations are supported so far, 0 (none) and 1 (safe)'
newargs[i] = ''
newargs[i+1] = ''
+ elif newargs[i].startswith('--closure'):
+ check_bad_eq(newargs[i])
+ closure = int(newargs[i+1])
+ newargs[i] = ''
+ newargs[i+1] = ''
newargs = [ arg for arg in newargs if arg is not '' ]
+ if llvm_opt_level is None: llvm_opt_level = 1 if opt_level >= 1 else 0
+ if closure is None: closure = 1 if opt_level >= 2 else 0
+
settings_changes = []
for i in range(len(newargs)):
if newargs[i] == '-s':
@@ -248,12 +282,15 @@ if not header:
newargs = [ arg for arg in newargs if arg is not '' ]
input_files = []
+ has_source_inputs = False
for i in range(len(newargs)): # find input files XXX this 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
arg = newargs[i]
- if arg.endswith(('.c', '.cpp', '.cxx', '.bc', '.o')): # we already removed -o <target>, so all these should be inputs
+ if arg.endswith(SOURCE_SUFFIXES + ('.bc', '.o')): # we already removed -o <target>, so all these should be inputs
input_files.append(arg)
newargs[i] = ''
+ if arg.endswith(SOURCE_SUFFIXES):
+ has_source_inputs = True
newargs = [ arg for arg in newargs if arg is not '' ]
assert len(input_files) > 0, 'emcc: no input files specified'
@@ -265,62 +302,74 @@ if not header:
target_basename = unsuffixed_basename(target)
- if '-c' in newargs: # -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
- target = target_basename + '.bc'
+ # -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'
+ target = target_basename + '.o'
final_suffix = target.split('.')[-1]
# Apply optimization level settings
if opt_level >= 1:
shared.Settings.ASSERTIONS = 0
+ shared.Settings.DISABLE_EXCEPTION_CATCHING = 1
if opt_level >= 2:
shared.Settings.RELOOP = 1
- print >> sys.stderr, 'Warning: The relooper optimization can be very slow.'
if opt_level >= 3:
shared.Settings.CORRECT_SIGNS = 0
shared.Settings.CORRECT_OVERFLOWS = 0
shared.Settings.CORRECT_ROUNDINGS = 0
shared.Settings.I64_MODE = 0
shared.Settings.DOUBLE_MODE = 0
- shared.Settings.DISABLE_EXCEPTION_CATCHING = 1
print >> sys.stderr, 'Warning: Applying some potentially unsafe optimizations! (Use -O2 if this fails.)'
## Compile source code to bitcode
+ if DEBUG: print >> sys.stderr, 'emcc: compiling to bitcode'
+
# First, generate LLVM bitcode. For each input file, we get base.o with bitcode
newargs = newargs + ['-emit-llvm', '-c']
for input_file in input_files:
- if input_file.endswith(('.c', '.cpp', '.cxx')):
- if DEBUG: print >> sys.stderr, "Running:", call, ' '.join(newargs)
- Popen([call] + newargs + [input_file]).communicate()
+ if input_file.endswith(SOURCE_SUFFIXES):
+ args = newargs + [input_file, '-o', in_temp(unsuffixed_basename(input_file) + '.o')]
+ if DEBUG: print >> sys.stderr, "emcc running:", call, ' '.join(args)
+ Popen([call] + args).communicate()
else:
- shutil.copyfile(input_file, unsuffixed_basename(input_file) + '.o')
+ shutil.copyfile(input_file, in_temp(unsuffixed_basename(input_file) + '.o'))
# Optimize, if asked to
if llvm_opt_level > 0:
+ if DEBUG: print >> sys.stderr, 'emcc: LLVM opts'
for input_file in input_files:
- shared.Building.llvm_opt(unsuffixed_basename(input_file) + '.o', 2, safe=llvm_opt_level < 2)
+ shared.Building.llvm_opt(in_temp(unsuffixed_basename(input_file) + '.o'), 2, safe=llvm_opt_level < 2)
# If we were just asked to generate bitcode, stop there
- if final_suffix in ['o', 'bc']:
- if final_suffix == 'bc':
+ if final_suffix not in ['js', 'html']:
+ if not specified_target:
for input_file in input_files:
- shutil.move(unsuffixed_basename(input_file) + '.o', unsuffixed_basename(input_file) + '.bc')
-
- if specified_target:
- assert len(input_files) == 1, 'fatal error: cannot specify -o with -c with multiple files'
- shutil.move(unsuffixed_basename(input_files[0]) + '.' + final_suffix, unsuffixed_basename(specified_target) + '.' + final_suffix)
+ shutil.move(in_temp(unsuffixed_basename(input_file) + '.o'), unsuffixed_basename(input_file) + '.' + final_suffix)
+ else:
+ if len(input_files) == 1:
+ shutil.move(in_temp(unsuffixed_basename(input_files[0]) + '.o'), specified_target)
+ else:
+ assert not has_dash_c, 'fatal error: cannot specify -o with -c with multiple files'
+ # We have a specified target (-o <target>), which is not JavaScript or HTML, and
+ # we have multiple files: Link them. TODO: Pass complex linker args along
+ shared.Building.link(map(lambda input_file: in_temp(unsuffixed_basename(input_file) + '.o'), input_files), specified_target)
exit(0)
## Continue on to create JavaScript
+ if DEBUG: print >> sys.stderr, 'emcc: generating JavaScript'
+
# First, combine the bitcode files if there are several
if len(input_files) > 1:
- shared.Building.link(map(lambda input_file: unsuffixed_basename(input_file) + '.o', input_files), target_basename + '.bc')
+ shared.Building.link(map(lambda input_file: in_temp(unsuffixed_basename(input_file) + '.o'), input_files), in_temp(target_basename + '.bc'))
else:
- shutil.move(unsuffixed_basename(input_files[0]) + '.o', target_basename + '.bc')
+ shutil.move(in_temp(unsuffixed_basename(input_files[0]) + '.o'), in_temp(target_basename + '.bc'))
# Apply -s settings in newargs here (after -Ox, so they can override it)
@@ -328,51 +377,48 @@ if not header:
key, value = change.split('=')
exec('shared.Settings.' + key + ' = ' + value)
- temp_files = shared.TempFiles()
- temp_files.note(target_basename + '.bc')
- try:
- shared.Building.emscripten(target_basename + '.bc', append_ext=False)
- shutil.move(target_basename + '.bc.o.js', target_basename + '.js')
- if SAVE_FILES: shutil.copyfile(target_basename + '.js', 'save_' + target_basename + '.js')
-
- if opt_level >= 1:
- # js optimizer
- shared.Building.js_optimizer(target_basename + '.js', 'loopOptimizer')
- shutil.move(target_basename + '.js.jo.js', target_basename + '.js')
- if SAVE_FILES: shutil.copyfile(target_basename + '.js', 'save_' + target_basename + '.jo.js')
-
- # eliminator
- shared.Building.eliminator(target_basename + '.js')
- shutil.move(target_basename + '.js.el.js', target_basename + '.js')
- if SAVE_FILES: shutil.copyfile(target_basename + '.js', 'save_' + target_basename + '.jo.el.js')
-
- if opt_level >= 3:
- # closure
- shared.Building.closure_compiler(target_basename + '.js')
- shutil.move(target_basename + '.js.cc.js', target_basename + '.js')
- if SAVE_FILES: shutil.copyfile(target_basename + '.js', 'save_' + target_basename + '.jo.el.cc.js')
-
- if opt_level >= 1:
- # js optimizer
- shared.Building.js_optimizer(target_basename + '.js', 'simplifyExpressions')
- shutil.move(target_basename + '.js.jo.js', target_basename + '.js')
- if SAVE_FILES: shutil.copyfile(target_basename + '.js', 'save_' + target_basename + '.jo.el.cc.jo.js')
-
- # If we were asked to also generate HTML, do that
- if final_suffix == 'html':
- shell = open(shared.path_from_root('src', 'shell.html')).read()
- html = open(target_basename + '.html', 'w')
- html.write(shell.replace('{{{ SCRIPT_CODE }}}', open(target_basename + '.js').read()))
- html.close()
- temp_files.note(target_basename + '.js')
-
- finally:
- temp_files.clean()
+ if opt_level >= 2:
+ print >> sys.stderr, 'Warning: The relooper optimization can be very slow.'
- exit(0)
+ final = shared.Building.emscripten(in_temp(target_basename + '.bc'), append_ext=False)
-else: # header or such
- if DEBUG: print >> sys.stderr, 'Just copy.'
- shutil.copy(sys.argv[-1], sys.argv[-2])
- exit(0)
+ if opt_level >= 1:
+ # js optimizer
+ if DEBUG: print >> sys.stderr, 'emcc: running pre-closure post-opts'
+ final = shared.Building.js_optimizer(final, 'loopOptimizer')
+
+ # eliminator
+ final = shared.Building.eliminator(final)
+
+ # js optimizer pre-pass
+ final = shared.Building.js_optimizer(final, 'simplifyExpressionsPre')
+
+ if closure:
+ if DEBUG: print >> sys.stderr, 'emcc: running closure'
+ final = shared.Building.closure_compiler(final)
+
+ if opt_level >= 1:
+ # js optimizer post-pass
+ if DEBUG: print >> sys.stderr, 'emcc: running post-closure post-opts'
+ final = shared.Building.js_optimizer(final, 'simplifyExpressionsPost')
+
+ # If we were asked to also generate HTML, do that
+ if final_suffix == 'html':
+ if DEBUG: print >> sys.stderr, 'emcc: generating HTML'
+ shell = open(shared.path_from_root('src', 'shell.html')).read()
+ html = open(target_basename + '.html', 'w')
+ html.write(shell.replace('{{{ SCRIPT_CODE }}}', open(final).read()))
+ html.close()
+ else:
+ # copy final JS to output
+ shutil.move(final, target_basename + '.js')
+
+finally:
+ if not TEMP_DIR:
+ try:
+ shutil.rmtree(temp_dir)
+ except:
+ pass
+ else:
+ print >> sys.stderr, 'emcc saved files are in:', temp_dir