aboutsummaryrefslogtreecommitdiff
path: root/tools/shared.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/shared.py')
-rw-r--r--tools/shared.py282
1 files changed, 222 insertions, 60 deletions
diff --git a/tools/shared.py b/tools/shared.py
index 532f561f..997c0ad9 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -1,4 +1,4 @@
-import shutil, time, os, sys, json, tempfile, copy, shlex
+import shutil, time, os, sys, json, tempfile, copy, shlex, atexit, subprocess
from subprocess import Popen, PIPE, STDOUT
from tempfile import mkstemp
@@ -6,15 +6,22 @@ __rootpath__ = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
def path_from_root(*pathelems):
return os.path.join(__rootpath__, *pathelems)
-# Config file
+# Emscripten configuration is done through the EM_CONFIG environment variable.
+# If the string value contained in this environment variable contains newline
+# separated definitions, then these definitions will be used to configure
+# Emscripten. Otherwise, the string is understood to be a path to a settings
+# file that contains the required definitions.
EM_CONFIG = os.environ.get('EM_CONFIG')
if not EM_CONFIG:
EM_CONFIG = '~/.emscripten'
-CONFIG_FILE = os.path.expanduser(EM_CONFIG)
-if not os.path.exists(CONFIG_FILE):
- shutil.copy(path_from_root('settings.py'), CONFIG_FILE)
- print >> sys.stderr, '''
+if '\n' in EM_CONFIG:
+ CONFIG_FILE = None
+else:
+ CONFIG_FILE = os.path.expanduser(EM_CONFIG)
+ if not os.path.exists(CONFIG_FILE):
+ shutil.copy(path_from_root('settings.py'), CONFIG_FILE)
+ print >> sys.stderr, '''
==============================================================================
Welcome to Emscripten!
@@ -28,11 +35,12 @@ make sure LLVM_ROOT and NODE_JS are correct.
This command will now exit. When you are done editing those paths, re-run it.
==============================================================================
''' % (EM_CONFIG, CONFIG_FILE)
- sys.exit(0)
+ sys.exit(0)
try:
- exec(open(CONFIG_FILE, 'r').read())
+ config_text = open(CONFIG_FILE, 'r').read() if CONFIG_FILE else EM_CONFIG
+ exec(config_text)
except Exception, e:
- print >> sys.stderr, 'Error in evaluating %s (at %s): %s' % (EM_CONFIG, CONFIG_FILE, str(e))
+ print >> sys.stderr, 'Error in evaluating %s (at %s): %s, text: %s' % (EM_CONFIG, CONFIG_FILE, str(e), config_text)
sys.exit(1)
# Check that basic stuff we need (a JS engine to compile, Node.js, and Clang and LLVM)
@@ -43,6 +51,8 @@ except Exception, e:
def check_sanity(force=False):
try:
if not force:
+ if not CONFIG_FILE:
+ return # config stored directly in EM_CONFIG => skip sanity checks
settings_mtime = os.stat(CONFIG_FILE).st_mtime
sanity_file = CONFIG_FILE + '_sanity'
try:
@@ -52,6 +62,9 @@ def check_sanity(force=False):
except:
pass
+ print >> sys.stderr, '(Emscripten: Config file changed, clearing cache)' # LLVM may have changed, etc.
+ Cache.erase()
+
print >> sys.stderr, '(Emscripten: Running sanity checks)'
if not check_engine(COMPILER_ENGINE):
@@ -68,6 +81,11 @@ def check_sanity(force=False):
print >> sys.stderr, 'FATAL: Cannot find %s, check the paths in %s' % (cmd, EM_CONFIG)
sys.exit(0)
+ try:
+ subprocess.call([JAVA, '-version'], stdout=PIPE, stderr=PIPE)
+ except:
+ print >> sys.stderr, 'WARNING: java does not seem to exist, required for closure compiler. -O2 and above will fail. You need to define JAVA in ~/.emscripten (see settings.py)'
+
if not os.path.exists(CLOSURE_COMPILER):
print >> sys.stderr, 'WARNING: Closure compiler (%s) does not exist, check the paths in %s. -O2 and above will fail' % (CLOSURE_COMPILER, EM_CONFIG)
@@ -89,7 +107,6 @@ CLANG_CC=os.path.expanduser(os.path.join(LLVM_ROOT, 'clang'))
CLANG_CPP=os.path.expanduser(os.path.join(LLVM_ROOT, 'clang++'))
CLANG=CLANG_CPP
LLVM_LINK=os.path.join(LLVM_ROOT, 'llvm-link')
-LLVM_LD=os.path.join(LLVM_ROOT, 'llvm-ld')
LLVM_AR=os.path.join(LLVM_ROOT, 'llvm-ar')
LLVM_OPT=os.path.expanduser(os.path.join(LLVM_ROOT, 'opt'))
LLVM_AS=os.path.expanduser(os.path.join(LLVM_ROOT, 'llvm-as'))
@@ -97,6 +114,7 @@ LLVM_DIS=os.path.expanduser(os.path.join(LLVM_ROOT, 'llvm-dis'))
LLVM_NM=os.path.expanduser(os.path.join(LLVM_ROOT, 'llvm-nm'))
LLVM_INTERPRETER=os.path.expanduser(os.path.join(LLVM_ROOT, 'lli'))
LLVM_COMPILER=os.path.expanduser(os.path.join(LLVM_ROOT, 'llc'))
+LLVM_EXTRACT=os.path.expanduser(os.path.join(LLVM_ROOT, 'llvm-extract'))
COFFEESCRIPT = path_from_root('tools', 'eliminator', 'node_modules', 'coffee-script', 'bin', 'coffee')
EMSCRIPTEN = path_from_root('emscripten.py')
@@ -105,23 +123,31 @@ NAMESPACER = path_from_root('tools', 'namespacer.py')
EMCC = path_from_root('emcc')
EMXX = path_from_root('em++')
EMAR = path_from_root('emar')
-EMLD = path_from_root('emld')
EMRANLIB = path_from_root('emranlib')
EMLIBTOOL = path_from_root('emlibtool')
+EMCONFIG = path_from_root('em-config')
EMMAKEN = path_from_root('tools', 'emmaken.py')
AUTODEBUGGER = path_from_root('tools', 'autodebugger.py')
BINDINGS_GENERATOR = path_from_root('tools', 'bindings_generator.py')
EXEC_LLVM = path_from_root('tools', 'exec_llvm.py')
VARIABLE_ELIMINATOR = path_from_root('tools', 'eliminator', 'eliminator.coffee')
JS_OPTIMIZER = path_from_root('tools', 'js-optimizer.js')
+FILE_PACKAGER = path_from_root('tools', 'file_packager.py')
# Temp dir. Create a random one, unless EMCC_DEBUG is set, in which case use TEMP_DIR/emscripten_temp
+try:
+ TEMP_DIR
+except:
+ print >> sys.stderr, 'TEMP_DIR not defined in ~/.emscripten, using /tmp'
+ TEMP_DIR = '/tmp'
+
+CANONICAL_TEMP_DIR = os.path.join(TEMP_DIR, 'emscripten_temp')
EMSCRIPTEN_TEMP_DIR = None
if os.environ.get('EMCC_DEBUG'):
try:
- EMSCRIPTEN_TEMP_DIR = os.path.join(TEMP_DIR, 'emscripten_temp')
+ EMSCRIPTEN_TEMP_DIR = CANONICAL_TEMP_DIR
if not os.path.exists(EMSCRIPTEN_TEMP_DIR):
os.makedirs(EMSCRIPTEN_TEMP_DIR)
except:
@@ -129,6 +155,9 @@ if os.environ.get('EMCC_DEBUG'):
if not EMSCRIPTEN_TEMP_DIR:
EMSCRIPTEN_TEMP_DIR = tempfile.mkdtemp(prefix='emscripten_temp_')
+ def clean_temp():
+ try_delete(EMSCRIPTEN_TEMP_DIR)
+ atexit.register(clean_temp)
# EM_CONFIG stuff
@@ -146,6 +175,12 @@ try:
except:
CLOSURE_COMPILER = path_from_root('third_party', 'closure-compiler', 'compiler.jar')
+try:
+ JAVA
+except:
+ print >> sys.stderr, 'JAVA not defined in ~/.emscripten, using "java"'
+ JAVA = 'java'
+
# Additional compiler options
try:
@@ -155,7 +190,7 @@ except:
# Force a simple, standard target as much as possible: target 32-bit linux, and disable various flags that hint at other platforms
COMPILER_OPTS = COMPILER_OPTS + ['-m32', '-U__i386__', '-U__x86_64__', '-U__i386', '-U__x86_64', '-U__SSE__', '-U__SSE2__', '-U__MMX__',
'-UX87_DOUBLE_ROUNDING', '-UHAVE_GCC_ASM_FOR_X87', '-DEMSCRIPTEN', '-U__STRICT_ANSI__', '-U__CYGWIN__',
- '-D__STDC__', '-Xclang', '-triple=i386-pc-linux-gnu']
+ '-D__STDC__', '-Xclang', '-triple=i386-pc-linux-gnu', '-D__IEEE_LITTLE_ENDIAN']
USE_EMSDK = not os.environ.get('EMMAKEN_NO_SDK')
@@ -163,8 +198,10 @@ USE_EMSDK = not os.environ.get('EMMAKEN_NO_SDK')
if USE_EMSDK:
# Disable system C and C++ include directories, and add our own (using -idirafter so they are last, like system dirs, which
# allows projects to override them)
- EMSDK_OPTS = ['-nostdinc', '-nostdinc++', '-Xclang', '-nobuiltininc', '-Xclang', '-nostdinc++', '-Xclang', '-nostdsysteminc',
+ # Note that -nostdinc++ is not needed, since -nostdinc implies that!
+ EMSDK_OPTS = ['-nostdinc', '-Xclang', '-nobuiltininc', '-Xclang', '-nostdsysteminc',
'-Xclang', '-isystem' + path_from_root('system', 'include'),
+ '-Xclang', '-isystem' + path_from_root('system', 'include', 'emscripten'),
'-Xclang', '-isystem' + path_from_root('system', 'include', 'bsd'), # posix stuff
'-Xclang', '-isystem' + path_from_root('system', 'include', 'libc'),
'-Xclang', '-isystem' + path_from_root('system', 'include', 'libcxx'),
@@ -172,7 +209,7 @@ if USE_EMSDK:
'-Xclang', '-isystem' + path_from_root('system', 'include', 'net'),
'-Xclang', '-isystem' + path_from_root('system', 'include', 'SDL'),
] + [
- '-U__APPLE__'
+ '-U__APPLE__', '-U__linux__'
]
COMPILER_OPTS += EMSDK_OPTS
else:
@@ -180,13 +217,25 @@ else:
# Engine tweaks
-#if 'strict' not in str(SPIDERMONKEY_ENGINE): # XXX temporarily disable strict mode until we sort out some stuff
-# SPIDERMONKEY_ENGINE += ['-e', "options('strict')"] # Strict mode in SpiderMonkey. With V8 we check that fallback to non-strict works too
-
-if 'gcparam' not in str(SPIDERMONKEY_ENGINE):
- SPIDERMONKEY_ENGINE += ['-e', "gcparam('maxBytes', 1024*1024*1024);"] # Our very large files need lots of gc heap
-
-WINDOWS = sys.platform.startswith ('win')
+try:
+ if 'gcparam' not in str(SPIDERMONKEY_ENGINE):
+ if type(SPIDERMONKEY_ENGINE) is str:
+ SPIDERMONKEY_ENGINE = [SPIDERMONKEY_ENGINE]
+ SPIDERMONKEY_ENGINE += ['-e', "gcparam('maxBytes', 1024*1024*1024);"] # Our very large files need lots of gc heap
+except NameError:
+ pass
+
+WINDOWS = sys.platform.startswith('win')
+
+# If we have 'env', we should use that to find python, because |python| may fail while |env python| may work
+# (For example, if system python is 3.x while we need 2.x, and env gives 2.x if told to do so.)
+ENV_PREFIX = []
+if not WINDOWS:
+ try:
+ assert 'Python' in Popen(['env', 'python', '-V'], stdout=PIPE, stderr=STDOUT).communicate()[0]
+ ENV_PREFIX = ['env']
+ except:
+ pass
# Temp file utilities
@@ -228,6 +277,8 @@ class TempFiles:
def check_engine(engine):
# TODO: we call this several times, perhaps cache the results?
try:
+ if not CONFIG_FILE:
+ return True # config stored directly in EM_CONFIG => skip engine check
return 'hello, world!' in run_js(path_from_root('tests', 'hello_world.js'), engine)
except Exception, e:
print 'Checking JS engine %s failed. Check %s. Details: %s' % (str(engine), EM_CONFIG, str(e))
@@ -243,11 +294,14 @@ def timeout_run(proc, timeout, note='unnamed process'):
raise Exception("Timed out: " + note)
return proc.communicate()[0]
+EM_DEBUG = os.environ.get('EM_DEBUG')
+
def run_js(filename, engine=None, args=[], check_timeout=False, stdout=PIPE, stderr=None, cwd=None):
if engine is None: engine = JS_ENGINES[0]
if type(engine) is not list: engine = [engine]
- return timeout_run(Popen(engine + [filename] + (['--'] if 'd8' in engine[0] else []) + args,
- stdout=stdout, stderr=stderr, cwd=cwd), 15*60 if check_timeout else None, 'Execution')
+ command = engine + [filename] + (['--'] if 'd8' in engine[0] else []) + args
+ if EM_DEBUG: print >> sys.stderr, 'run_js: ' + ' '.join(command)
+ return timeout_run(Popen(command, stdout=stdout, stderr=stderr, cwd=cwd), 15*60 if check_timeout else None, 'Execution')
def to_cc(cxx):
# By default, LLVM_GCC and CLANG are really the C++ versions. This gets an explicit C version
@@ -342,11 +396,12 @@ class Settings:
if opt_level >= 2:
Settings.RELOOP = 1
if opt_level >= 3:
+ Settings.INLINING_LIMIT = 0
+ Settings.DOUBLE_MODE = 0
+ Settings.PRECISE_I64_MATH = 0
Settings.CORRECT_SIGNS = 0
Settings.CORRECT_OVERFLOWS = 0
Settings.CORRECT_ROUNDINGS = 0
- Settings.DOUBLE_MODE = 0
- Settings.PRECISE_I64_MATH = 0
if noisy: print >> sys.stderr, 'Warning: Applying some potentially unsafe optimizations! (Use -O2 if this fails.)'
global Settings
@@ -461,24 +516,95 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
if configure: # Useful in debugging sometimes to comment this out (and the lines below up to and including the |link| call)
Building.configure(configure + configure_args, stdout=open(os.path.join(project_dir, 'configure_'), 'w'),
stderr=open(os.path.join(project_dir, 'configure_err'), 'w'), env=env)
- Building.make(make + make_args, stdout=open(os.path.join(project_dir, 'make_'), 'w'),
- stderr=open(os.path.join(project_dir, 'make_err'), 'w'), env=env)
- if cache is not None:
- cache[cache_name] = []
- for f in generated_libs:
- basename = os.path.basename(f)
- cache[cache_name].append((basename, open(f, 'rb').read()))
+ for i in range(2): # workaround for some build systems that need to be run twice to succeed (e.g. poppler)
+ Building.make(make + make_args, stdout=open(os.path.join(project_dir, 'make_' + str(i)), 'w'),
+ stderr=open(os.path.join(project_dir, 'make_err' + str(i)), 'w'), env=env)
+ try:
+ if cache is not None:
+ cache[cache_name] = []
+ for f in generated_libs:
+ basename = os.path.basename(f)
+ cache[cache_name].append((basename, open(f, 'rb').read()))
+ break
+ except:
+ if i > 0: raise Exception('could not build library ' + name)
if old_dir:
os.chdir(old_dir)
return generated_libs
@staticmethod
- def link(files, target):
+ def link(files, target, remove_duplicates=False):
+ actual_files = []
+ unresolved_symbols = set() # necessary for .a linking, see below
+ resolved_symbols = set()
+ temp_dir = None
+ for f in files:
+ if not Building.is_ar(f):
+ if Building.is_bitcode(f):
+ new_symbols = Building.llvm_nm(f)
+ resolved_symbols = resolved_symbols.union(new_symbols.defs)
+ unresolved_symbols = unresolved_symbols.union(new_symbols.undefs.difference(resolved_symbols)).difference(new_symbols.defs)
+ actual_files.append(f)
+ else:
+ # Extract object files from ar archives, and link according to gnu ld semantics
+ # (link in an entire .o from the archive if it supplies symbols still unresolved)
+ cwd = os.getcwd()
+ try:
+ temp_dir = os.path.join(EMSCRIPTEN_TEMP_DIR, 'ar_output_' + str(os.getpid()))
+ if not os.path.exists(temp_dir):
+ os.makedirs(temp_dir)
+ os.chdir(temp_dir)
+ contents = filter(lambda x: len(x) > 0, Popen([LLVM_AR, 't', f], stdout=PIPE).communicate()[0].split('\n'))
+ if len(contents) == 0:
+ print >> sys.stderr, 'Warning: Archive %s appears to be empty (recommendation: link an .so instead of .a)' % f
+ else:
+ for content in contents: # ar will silently fail if the directory for the file does not exist, so make all the necessary directories
+ dirname = os.path.dirname(content)
+ if dirname and not os.path.exists(dirname):
+ os.makedirs(dirname)
+ Popen([LLVM_AR, 'x', f], stdout=PIPE).communicate() # if absolute paths, files will appear there. otherwise, in this directory
+ contents = map(lambda content: os.path.join(temp_dir, content), contents)
+ contents = filter(os.path.exists, map(os.path.abspath, contents))
+ needed = False # We add or do not add the entire archive. We let llvm dead code eliminate parts we do not need, instead of
+ # doing intra-dependencies between archive contents
+ for content in contents:
+ new_symbols = Building.llvm_nm(content)
+ # Link in the .o if it provides symbols, *or* this is a singleton archive (which is apparently an exception in gcc ld)
+ if new_symbols.defs.intersection(unresolved_symbols) or len(files) == 1:
+ needed = True
+ if needed:
+ for content in contents:
+ if Building.is_bitcode(content):
+ new_symbols = Building.llvm_nm(content)
+ resolved_symbols = resolved_symbols.union(new_symbols.defs)
+ unresolved_symbols = unresolved_symbols.union(new_symbols.undefs.difference(resolved_symbols)).difference(new_symbols.defs)
+ actual_files.append(content)
+ finally:
+ os.chdir(cwd)
try_delete(target)
- stub = os.path.join(EMSCRIPTEN_TEMP_DIR, 'stub_deleteme')
- output = Popen([LLVM_LD, '-disable-opt'] + files + ['-b', target, '-o', stub], stdout=PIPE).communicate()[0]
- try_delete(stub) # clean up stub left by the linker
- assert os.path.exists(target) and (output is None or 'Could not open input file' not in output), 'Linking error: ' + output
+
+ if remove_duplicates:
+ # Remove duplicate symbols. This is a workaround for how we compile .a files, we try to
+ # emulate ld behavior which is permissive TODO: cache llvm-nm results
+ seen_symbols = set()
+ print >> sys.stderr, actual_files
+ for actual in actual_files:
+ symbols = Building.llvm_nm(actual)
+ dupes = seen_symbols.intersection(symbols.defs)
+ if len(dupes) > 0:
+ print >> sys.stderr, 'emcc: warning: removing duplicates in', actual
+ for dupe in dupes:
+ print >> sys.stderr, 'emcc: warning: removing duplicate', dupe
+ Popen([LLVM_EXTRACT, actual, '-delete', '-glob=' + dupe, '-o', actual], stderr=PIPE).communicate()
+ Popen([LLVM_EXTRACT, actual, '-delete', '-func=' + dupe, '-o', actual], stderr=PIPE).communicate()
+ Popen([LLVM_EXTRACT, actual, '-delete', '-glob=.str', '-o', actual], stderr=PIPE).communicate() # garbage that appears here
+ seen_symbols = seen_symbols.union(symbols.defs)
+
+ # Finish link
+ output = Popen([LLVM_LINK] + actual_files + ['-o', target], stdout=PIPE).communicate()[0]
+ assert os.path.exists(target) and (output is None or 'Could not open input file' not in output), 'Linking error: ' + output + '\nemcc: If you get duplicate symbol errors, try --remove-duplicates'
+ if temp_dir:
+ try_delete(temp_dir)
# Emscripten optimizations that we run on the .ll file
@staticmethod
@@ -507,12 +633,6 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
shutil.move(filename + '.o', filename + '.o.pre')
output = Popen([LLVM_OPT, filename + '.o.pre'] + Building.LLVM_OPT_OPTS + ['-o=' + filename + '.o'], stdout=PIPE).communicate()[0]
assert os.path.exists(filename + '.o'), 'Failed to run llvm optimizations: ' + output
- #if Building.LLVM_OPTS == 2:
- # print 'Unsafe LD!'
- # shutil.move(filename + '.o', filename + '.o.pre')
- # output = Popen([LLVM_LD, filename + '.o.pre', '-o=' + filename + '.tmp'], stdout=PIPE).communicate()[0]
- # assert os.path.exists(filename + '.tmp.bc'), 'Failed to run llvm optimizations: ' + output
- # shutil.move(filename + '.tmp.bc', filename + '.o')
@staticmethod
def llvm_dis(input_filename, output_filename=None):
@@ -548,13 +668,15 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
commons = []
for line in output.split('\n'):
if len(line) == 0: continue
- status, symbol = filter(lambda seg: len(seg) > 0, line.split(' '))
- if status == 'U':
- ret.undefs.append(symbol)
- elif status != 'C':
- ret.defs.append(symbol)
- else:
- ret.commons.append(symbol)
+ parts = filter(lambda seg: len(seg) > 0, line.split(' '))
+ if len(parts) == 2: # ignore lines with absolute offsets, these are not bitcode anyhow (e.g. |00000630 t d_source_name|)
+ status, symbol = parts
+ if status == 'U':
+ ret.undefs.append(symbol)
+ elif status != 'C':
+ ret.defs.append(symbol)
+ else:
+ ret.commons.append(symbol)
ret.defs = set(ret.defs)
ret.undefs = set(ret.undefs)
ret.commons = set(ret.commons)
@@ -565,13 +687,13 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
if output_filename is None:
output_filename = filename + '.o'
try_delete(output_filename)
- Popen(['python', EMCC, filename] + args + ['-o', output_filename], stdout=stdout, stderr=stderr, env=env).communicate()
+ Popen(ENV_PREFIX + ['python', EMCC, filename] + args + ['-o', output_filename], stdout=stdout, stderr=stderr, env=env).communicate()
assert os.path.exists(output_filename), 'emcc could not create output file'
@staticmethod
def emar(action, output_filename, filenames, stdout=None, stderr=None, env=None):
try_delete(output_filename)
- Popen(['python', EMAR, action, output_filename] + filenames, stdout=stdout, stderr=stderr, env=env).communicate()
+ Popen(ENV_PREFIX + ['python', EMAR, action, output_filename] + filenames, stdout=stdout, stderr=stderr, env=env).communicate()
if 'c' in action:
assert os.path.exists(output_filename), 'emar could not create output file'
@@ -582,7 +704,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
# Run Emscripten
settings = Settings.serialize()
- compiler_output = timeout_run(Popen(['python', EMSCRIPTEN, filename + ('.o.ll' if append_ext else ''), '-o', filename + '.o.js'] + settings + extra_args, stdout=PIPE), None, 'Compiling')
+ compiler_output = timeout_run(Popen(ENV_PREFIX + ['python', EMSCRIPTEN, filename + ('.o.ll' if append_ext else ''), '-o', filename + '.o.js'] + settings + extra_args, stdout=PIPE), None, 'Compiling')
#print compiler_output
# Detect compilation crashes and errors
@@ -713,8 +835,8 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
if type(passes) == str:
passes = [passes]
# XXX Disable crankshaft to work around v8 bug 1895
- output, err = Popen([NODE_JS, '--nocrankshaft', JS_OPTIMIZER, filename] + passes, stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate()
- assert len(output) > 0 and not output.startswith('Assertion failed'), 'Error in js optimizer: ' + err + '\n\n' + output
+ output = Popen([NODE_JS, '--nocrankshaft', JS_OPTIMIZER, filename] + passes, stdout=PIPE).communicate()[0]
+ assert len(output) > 0 and not output.startswith('Assertion failed'), 'Error in js optimizer: ' + output
filename += '.jo.js'
f = open(filename, 'w')
f.write(output)
@@ -729,8 +851,8 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
coffee = path_from_root('tools', 'eliminator', 'node_modules', 'coffee-script', 'bin', 'coffee')
eliminator = path_from_root('tools', 'eliminator', 'eliminator.coffee')
input = open(filename, 'r').read()
- output, err = Popen([NODE_JS, coffee, eliminator], stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate(input)
- assert len(output) > 0, 'Error in eliminator: ' + err + '\n\n' + output
+ output = Popen([NODE_JS, coffee, eliminator, filename], stdout=PIPE).communicate()[0]
+ assert len(output) > 0, 'Error in eliminator: ' + output
filename += '.el.js'
f = open(filename, 'w')
f.write(output)
@@ -744,7 +866,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
# Something like this (adjust memory as needed):
# java -Xmx1024m -jar CLOSURE_COMPILER --compilation_level ADVANCED_OPTIMIZATIONS --variable_map_output_file src.cpp.o.js.vars --js src.cpp.o.js --js_output_file src.cpp.o.cc.js
- args = ['java',
+ args = [JAVA,
'-Xmx1024m',
'-jar', CLOSURE_COMPILER,
'--compilation_level', 'ADVANCED_OPTIMIZATIONS',
@@ -810,7 +932,9 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
# Permanent cache for dlmalloc and stdlibc++
class Cache:
- dirname = os.path.expanduser(os.path.join('~', '.emscripten_cache'))
+ dirname = os.environ.get('EM_CACHE')
+ if not dirname:
+ dirname = os.path.expanduser(os.path.join('~', '.emscripten_cache'))
@staticmethod
def erase():
@@ -832,3 +956,41 @@ class Cache:
shutil.copyfile(creator(), cachename)
return cachename
+# Compression of code and data for smaller downloads
+class Compression:
+ on = False
+
+ @staticmethod
+ def compressed_name(filename):
+ return filename + '.compress'
+
+ @staticmethod
+ def compress(filename):
+ execute(Compression.encoder, stdin=open(filename, 'rb'), stdout=open(Compression.compressed_name(filename), 'wb'))
+
+ @staticmethod
+ def worth_it(original, compressed):
+ return compressed < original - 1500 # save at least one TCP packet or so
+
+def execute(cmd, *args, **kw):
+ try:
+ return subprocess.Popen(cmd, *args, **kw).communicate() # let compiler frontend print directly, so colors are saved (PIPE kills that)
+ except:
+ if not isinstance(cmd, str):
+ cmd = ' '.join(cmd)
+ print >> sys.stderr, 'Invoking Process failed: <<< ' + cmd + ' >>>'
+ raise
+
+def suffix(name):
+ parts = name.split('.')
+ if len(parts) > 1:
+ return parts[-1]
+ else:
+ return None
+
+def unsuffixed(name):
+ return '.'.join(name.split('.')[:-1])
+
+def unsuffixed_basename(name):
+ return os.path.basename(unsuffixed(name))
+