diff options
Diffstat (limited to 'tools/shared.py')
-rw-r--r-- | tools/shared.py | 96 |
1 files changed, 62 insertions, 34 deletions
diff --git a/tools/shared.py b/tools/shared.py index 0d575fd7..007c2ee8 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -1,6 +1,7 @@ import shutil, time, os, sys, json, tempfile, copy, shlex, atexit, subprocess, hashlib, cPickle, re from subprocess import Popen, PIPE, STDOUT from tempfile import mkstemp +from distutils.spawn import find_executable import jsrun, cache, tempfiles from response_file import create_response_file import logging, platform @@ -204,25 +205,12 @@ else: config_file = '\n'.join(config_file) # autodetect some default paths config_file = config_file.replace('{{{ EMSCRIPTEN_ROOT }}}', __rootpath__) - llvm_root = '/usr/bin' - try: - llvm_root = os.path.dirname(Popen(['which', 'llvm-dis'], stdout=PIPE).communicate()[0].replace('\n', '')) - except: - pass + llvm_root = os.path.dirname(find_executable('llvm-dis') or '/usr/bin/llvm-dis') config_file = config_file.replace('{{{ LLVM_ROOT }}}', llvm_root) - node = 'node' - try: - node = Popen(['which', 'node'], stdout=PIPE).communicate()[0].replace('\n', '') or \ - Popen(['which', 'nodejs'], stdout=PIPE).communicate()[0].replace('\n', '') or node - except: - pass + node = find_executable('node') or find_executable('nodejs') or 'node' config_file = config_file.replace('{{{ NODE }}}', node) - python = sys.executable or 'python' - try: - python = Popen(['which', 'python2'], stdout=PIPE).communicate()[0].replace('\n', '') or \ - Popen(['which', 'python'], stdout=PIPE).communicate()[0].replace('\n', '') or python - except: - pass + python = find_executable('python2') or find_executable('python') or \ + sys.executable or 'python' config_file = config_file.replace('{{{ PYTHON }}}', python) # write @@ -269,7 +257,7 @@ def check_clang_version(): def check_llvm_version(): try: - check_clang_version(); + check_clang_version() except Exception, e: logging.warning('Could not verify LLVM version: %s' % str(e)) @@ -295,7 +283,7 @@ def check_node_version(): # we re-check sanity when the settings are changed) # We also re-check sanity and clear the cache when the version changes -EMSCRIPTEN_VERSION = '1.5.1' +EMSCRIPTEN_VERSION = '1.5.3' def generate_sanity(): return EMSCRIPTEN_VERSION + '|' + get_llvm_target() @@ -324,6 +312,7 @@ def check_sanity(force=False): if reason: logging.warning('(Emscripten: %s, clearing cache)' % reason) Cache.erase() + force = False # the check actually failed, so definitely write out the sanity file, to avoid others later seeing failures too # some warning, not fatal checks - do them even if EM_IGNORE_SANITY is on check_llvm_version() @@ -411,6 +400,7 @@ EMAR = path_from_root('emar') EMRANLIB = path_from_root('emranlib') EMLIBTOOL = path_from_root('emlibtool') EMCONFIG = path_from_root('em-config') +EMLINK = path_from_root('emlink.py') EMMAKEN = path_from_root('tools', 'emmaken.py') AUTODEBUGGER = path_from_root('tools', 'autodebugger.py') BINDINGS_GENERATOR = path_from_root('tools', 'bindings_generator.py') @@ -514,7 +504,8 @@ except: COMPILER_OPTS = COMPILER_OPTS + ['-m32', '-U__i386__', '-U__i386', '-Ui386', '-U__SSE__', '-U__SSE_MATH__', '-U__SSE2__', '-U__SSE2_MATH__', '-U__MMX__', '-DEMSCRIPTEN', '-D__EMSCRIPTEN__', '-U__STRICT_ANSI__', - '-D__IEEE_LITTLE_ENDIAN', '-fno-math-errno', '-fno-threadsafe-statics', + '-D__IEEE_LITTLE_ENDIAN', '-fno-math-errno', + #'-fno-threadsafe-statics', # disabled due to issue 1289 '-target', LLVM_TARGET] if LLVM_TARGET == 'le32-unknown-nacl': @@ -735,7 +726,7 @@ class Building: env['HOST_CXXFLAGS'] = "-W" #if set to nothing, CXXFLAGS is used, which we don't want env['PKG_CONFIG_LIBDIR'] = path_from_root('system', 'local', 'lib', 'pkgconfig') + os.path.pathsep + path_from_root('system', 'lib', 'pkgconfig') env['PKG_CONFIG_PATH'] = os.environ.get ('EM_PKG_CONFIG_PATH') or '' - env['EMSCRIPTEN'] = '1' + env['EMSCRIPTEN'] = path_from_root() return env @staticmethod @@ -877,7 +868,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e return generated_libs @staticmethod - def link(files, target): + def link(files, target, force_archive_contents=False): actual_files = [] unresolved_symbols = set(['main']) # tracking unresolveds is necessary for .a linking, see below. (and main is always a necessary symbol) resolved_symbols = set() @@ -928,7 +919,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e # Link in the .o if it provides symbols, *or* this is a singleton archive (which is apparently an exception in gcc ld) #print >> sys.stderr, 'need', content, '?', unresolved_symbols, 'and we can supply', new_symbols.defs #print >> sys.stderr, content, 'DEF', new_symbols.defs, '\n' - if new_symbols.defs.intersection(unresolved_symbols) or len(files) == 1: + if new_symbols.defs.intersection(unresolved_symbols) or len(files) == 1 or force_archive_contents: if Building.is_bitcode(content): #print >> sys.stderr, ' adding object', content, '\n' resolved_symbols = resolved_symbols.union(new_symbols.defs) @@ -944,7 +935,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e # Finish link actual_files = unique_ordered(actual_files) # tolerate people trying to link a.so a.so etc. - logging.debug('emcc: llvm-linking: %s', actual_files) + logging.debug('emcc: llvm-linking: %s to %s', actual_files, target) # check for too-long command line link_cmd = [LLVM_LINK] + actual_files + ['-o', target] @@ -1111,7 +1102,11 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e @staticmethod def get_safe_internalize(): - exports = ','.join(map(lambda exp: exp[1:], expand_response(Settings.EXPORTED_FUNCTIONS))) + if not Building.can_build_standalone(): return [] # do not internalize anything + exps = expand_response(Settings.EXPORTED_FUNCTIONS) + if '_malloc' not in exps: exps.append('_malloc') # needed internally, even if user did not add to EXPORTED_FUNCTIONS + if '_free' not in exps: exps.append('_free') + exports = ','.join(map(lambda exp: exp[1:], exps)) # internalize carefully, llvm 3.2 will remove even main if not told not to return ['-internalize', '-internalize-public-api-list=' + exports] @@ -1201,7 +1196,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e opts.append('-jump-threading') opts.append('-correlated-propagation') opts.append('-dse') - #addExtensionsToPM(EP_ScalarOptimizerLate, MPM); + #addExtensionsToPM(EP_ScalarOptimizerLate, MPM) opts.append('-adce') opts.append('-simplifycfg') @@ -1218,8 +1213,8 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e return opts @staticmethod - def js_optimizer(filename, passes, jcache): - return js_optimizer.run(filename, passes, listify(NODE_JS), jcache) + def js_optimizer(filename, passes, jcache=False, debug=False, extra_info=None): + return js_optimizer.run(filename, passes, listify(NODE_JS), jcache, debug, extra_info) @staticmethod def closure_compiler(filename, pretty=True): @@ -1293,9 +1288,6 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e emcc_debug = os.environ.get('EMCC_DEBUG') if emcc_debug: del os.environ['EMCC_DEBUG'] - emcc_optimize_normally = os.environ.get('EMCC_OPTIMIZE_NORMALLY') - if emcc_optimize_normally: del os.environ['EMCC_OPTIMIZE_NORMALLY'] - def make(opt_level): raw = relooper + '.raw.js' Building.emcc(os.path.join('relooper', 'Relooper.cpp'), ['-I' + os.path.join('relooper'), '--post-js', @@ -1308,7 +1300,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e '-O' + str(opt_level), '--closure', '0'], raw) f = open(relooper, 'w') f.write("// Relooper, (C) 2012 Alon Zakai, MIT license, https://github.com/kripken/Relooper\n") - f.write("var Relooper = (function() {\n"); + f.write("var Relooper = (function() {\n") f.write(open(raw).read()) f.write('\n return Module.Relooper;\n') f.write('})();\n') @@ -1326,7 +1318,6 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e finally: os.chdir(curr) if emcc_debug: os.environ['EMCC_DEBUG'] = emcc_debug - if emcc_optimize_normally: os.environ['EMCC_OPTIMIZE_NORMALLY'] = emcc_optimize_normally if not ok: logging.error('bootstrapping relooper failed. You may need to manually create relooper.js by compiling it, see src/relooper/emscripten') 1/0 @@ -1363,9 +1354,35 @@ JCache = cache.JCache(Cache) chunkify = cache.chunkify class JS: + memory_initializer_pattern = '/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, ([\dRuntime\.GLOBAL_BASE+]+)\)' + no_memory_initializer_pattern = '/\* no memory initializer \*/' + + memory_staticbump_pattern = 'STATICTOP = STATIC_BASE \+ (\d+);' + + global_initializers_pattern = '/\* global initializers \*/ __ATINIT__.push\((.+)\);' + @staticmethod def to_nice_ident(ident): # limited version of the JS function toNiceIdent - return ident.replace('%', '$').replace('@', '_'); + return ident.replace('%', '$').replace('@', '_') + + @staticmethod + def make_invoke(sig, named=True): + args = ','.join(['a' + str(i) for i in range(1, len(sig))]) + args = 'index' + (',' if args else '') + args + # C++ exceptions are numbers, and longjmp is a string 'longjmp' + return '''function%s(%s) { + try { + %sModule["dynCall_%s"](%s); + } catch(e) { + if (typeof e !== 'number' && e !== 'longjmp') throw e; + asm["setThrew"](1, 0); + } +}''' % ((' invoke_' + sig) if named else '', args, 'return ' if sig[0] != 'v' else '', sig, args) + + @staticmethod + def align(x, by): + while x % by != 0: x += 1 + return x # Compression of code and data for smaller downloads class Compression: @@ -1392,6 +1409,17 @@ def execute(cmd, *args, **kw): logging.error('Invoking Process failed: <<< ' + cmd + ' >>>') raise +def check_execute(cmd, *args, **kw): + # TODO: use in more places. execute doesn't actually check that return values + # are nonzero + try: + kw['stderr'] = STDOUT + subprocess.check_output(cmd, *args, **kw) + logging.debug("Successfuly executed %s" % " ".join(cmd)) + except subprocess.CalledProcessError as e: + logging.error("'%s' failed with output:\n%s" % (" ".join(e.cmd), e.output)) + raise + def suffix(name): parts = name.split('.') if len(parts) > 1: |