aboutsummaryrefslogtreecommitdiff
path: root/tools/shared.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/shared.py')
-rw-r--r--tools/shared.py212
1 files changed, 139 insertions, 73 deletions
diff --git a/tools/shared.py b/tools/shared.py
index c0df227d..0d0f20d4 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -200,18 +200,25 @@ if '\n' in EM_CONFIG:
else:
CONFIG_FILE = os.path.expanduser(EM_CONFIG)
if not os.path.exists(CONFIG_FILE):
+ # Note: repr is used to ensure the paths are escaped correctly on Windows.
+ # The full string is replaced so that the template stays valid Python.
config_file = open(path_from_root('tools', 'settings_template_readonly.py')).read().split('\n')
config_file = config_file[1:] # remove "this file will be copied..."
config_file = '\n'.join(config_file)
# autodetect some default paths
- config_file = config_file.replace('{{{ EMSCRIPTEN_ROOT }}}', __rootpath__)
+ config_file = config_file.replace('\'{{{ EMSCRIPTEN_ROOT }}}\'', repr(__rootpath__))
llvm_root = os.path.dirname(find_executable('llvm-dis') or '/usr/bin/llvm-dis')
- config_file = config_file.replace('{{{ LLVM_ROOT }}}', llvm_root)
+ config_file = config_file.replace('\'{{{ LLVM_ROOT }}}\'', repr(llvm_root))
node = find_executable('node') or find_executable('nodejs') or 'node'
- config_file = config_file.replace('{{{ NODE }}}', node)
+ config_file = config_file.replace('\'{{{ NODE }}}\'', repr(node))
python = find_executable('python2') or find_executable('python') or \
sys.executable or 'python'
- config_file = config_file.replace('{{{ PYTHON }}}', python)
+ config_file = config_file.replace('\'{{{ PYTHON }}}\'', repr(python))
+ if WINDOWS:
+ tempdir = os.environ.get('TEMP') or os.environ.get('TMP') or 'c:\\temp'
+ else:
+ tempdir = '/tmp'
+ config_file = config_file.replace('\'{{{ TEMP }}}\'', repr(tempdir))
# write
open(CONFIG_FILE, 'w').write(config_file)
@@ -283,7 +290,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.3'
+EMSCRIPTEN_VERSION = '1.5.5'
def generate_sanity():
return EMSCRIPTEN_VERSION + '|' + get_llvm_target() + '|' + LLVM_ROOT
@@ -333,7 +340,7 @@ def check_sanity(force=False):
logging.critical('Node.js (%s) does not seem to work, check the paths in %s' % (NODE_JS, EM_CONFIG))
sys.exit(1)
- for cmd in [CLANG, LLVM_LINK, LLVM_AR, LLVM_OPT, LLVM_AS, LLVM_DIS, LLVM_NM]:
+ for cmd in [CLANG, LINK_CMD[0], LLVM_AR, LLVM_OPT, LLVM_AS, LLVM_DIS, LLVM_NM]:
if not os.path.exists(cmd) and not os.path.exists(cmd + '.exe'): # .exe extension required for Windows
logging.critical('Cannot find %s, check the paths in %s' % (cmd, EM_CONFIG))
sys.exit(1)
@@ -360,12 +367,31 @@ def check_sanity(force=False):
# Tools/paths
-LLVM_ADD_VERSION = os.getenv('LLVM_ADD_VERSION')
-CLANG_ADD_VERSION = os.getenv('CLANG_ADD_VERSION')
+try:
+ LLVM_ADD_VERSION
+except NameError:
+ LLVM_ADD_VERSION = os.getenv('LLVM_ADD_VERSION')
+
+try:
+ CLANG_ADD_VERSION
+except NameError:
+ CLANG_ADD_VERSION = os.getenv('CLANG_ADD_VERSION')
+
+USING_PNACL_TOOLCHAIN = os.path.exists(os.path.join(LLVM_ROOT, 'pnacl-clang'))
+
+def modify_prefix(tool):
+ if USING_PNACL_TOOLCHAIN:
+ if tool.startswith('llvm-'):
+ tool = tool[5:]
+ tool = 'pnacl-' + tool
+ if WINDOWS:
+ tool += '.bat'
+ return tool
# Some distributions ship with multiple llvm versions so they add
# the version to the binaries, cope with that
def build_llvm_tool_path(tool):
+ tool = modify_prefix(tool)
if LLVM_ADD_VERSION:
return os.path.join(LLVM_ROOT, tool + "-" + LLVM_ADD_VERSION)
else:
@@ -374,6 +400,7 @@ def build_llvm_tool_path(tool):
# Some distributions ship with multiple clang versions so they add
# the version to the binaries, cope with that
def build_clang_tool_path(tool):
+ tool = modify_prefix(tool)
if CLANG_ADD_VERSION:
return os.path.join(LLVM_ROOT, tool + "-" + CLANG_ADD_VERSION)
else:
@@ -382,7 +409,11 @@ def build_clang_tool_path(tool):
CLANG_CC=os.path.expanduser(build_clang_tool_path('clang'))
CLANG_CPP=os.path.expanduser(build_clang_tool_path('clang++'))
CLANG=CLANG_CPP
-LLVM_LINK=build_llvm_tool_path('llvm-link')
+if USING_PNACL_TOOLCHAIN:
+ # The PNaCl toolchain doesn't have llvm-link, but we can fake it
+ LINK_CMD = [build_llvm_tool_path('llvm-ld'), '-nostdlib', '-r']
+else:
+ LINK_CMD = [build_llvm_tool_path('llvm-link')]
LLVM_AR=build_llvm_tool_path('llvm-ar')
LLVM_OPT=os.path.expanduser(build_llvm_tool_path('opt'))
LLVM_AS=os.path.expanduser(build_llvm_tool_path('llvm-as'))
@@ -519,6 +550,7 @@ if USE_EMSDK:
# allows projects to override them)
EMSDK_OPTS = ['-nostdinc', '-Xclang', '-nobuiltininc', '-Xclang', '-nostdsysteminc',
'-Xclang', '-isystem' + path_from_root('system', 'local', 'include'),
+ '-Xclang', '-isystem' + path_from_root('system', 'include', 'compat'),
'-Xclang', '-isystem' + path_from_root('system', 'include', 'libcxx'),
'-Xclang', '-isystem' + path_from_root('system', 'include'),
'-Xclang', '-isystem' + path_from_root('system', 'include', 'emscripten'),
@@ -599,7 +631,7 @@ def line_splitter(data):
return out
-def limit_size(string, MAX=120*20):
+def limit_size(string, MAX=12000*20):
if len(string) < MAX: return string
return string[0:MAX/2] + '\n[..]\n' + string[-MAX/2:]
@@ -643,62 +675,89 @@ def expand_response(data):
# Settings. A global singleton. Not pretty, but nicer than passing |, settings| everywhere
-class Settings:
- @classmethod
- def reset(self):
- class Settings2:
- QUANTUM_SIZE = 4
- reset = Settings.reset
-
- # Given some emcc-type args (-O3, -s X=Y, etc.), fill Settings with the right settings
- @classmethod
- def load(self, args=[]):
- # Load the JS defaults into python
- settings = open(path_from_root('src', 'settings.js')).read().replace('var ', 'Settings.').replace('//', '#')
- exec settings in globals()
-
- # Apply additional settings. First -O, then -s
- for i in range(len(args)):
- if args[i].startswith('-O'):
- level = eval(args[i][2])
- Settings.apply_opt_level(level)
- for i in range(len(args)):
- if args[i] == '-s':
- exec 'Settings.' + args[i+1] in globals() # execute the setting
-
- # Transforms the Settings information into emcc-compatible args (-s X=Y, etc.). Basically
- # the reverse of load_settings, except for -Ox which is relevant there but not here
- @classmethod
- def serialize(self):
- ret = []
- for key, value in Settings.__dict__.iteritems():
- if key == key.upper(): # this is a hack. all of our settings are ALL_CAPS, python internals are not
- jsoned = json.dumps(value, sort_keys=True)
- ret += ['-s', key + '=' + jsoned]
- return ret
-
- @classmethod
- def apply_opt_level(self, opt_level, noisy=False):
- if opt_level >= 1:
- Settings.ASM_JS = 1
- Settings.ASSERTIONS = 0
- Settings.DISABLE_EXCEPTION_CATCHING = 1
- Settings.EMIT_GENERATED_FUNCTIONS = 1
- if opt_level >= 2:
- Settings.RELOOP = 1
- Settings.ALIASING_FUNCTION_POINTERS = 1
- if opt_level >= 3:
- # Aside from these, -O3 also runs closure compiler and llvm lto
- Settings.FORCE_ALIGNED_MEMORY = 1
- Settings.DOUBLE_MODE = 0
- Settings.PRECISE_I64_MATH = 0
- if noisy: logging.warning('Applying some potentially unsafe optimizations! (Use -O2 if this fails.)')
-
- global Settings
- Settings = Settings2
- Settings.load() # load defaults
-
-Settings.reset()
+class Settings2(type):
+ class __impl:
+ attrs = {}
+
+ def __init__(self):
+ self.reset()
+
+ @classmethod
+ def reset(self):
+ self.attrs = { 'QUANTUM_SIZE': 4 }
+ self.load()
+
+ # Given some emcc-type args (-O3, -s X=Y, etc.), fill Settings with the right settings
+ @classmethod
+ def load(self, args=[]):
+ # Load the JS defaults into python
+ settings = open(path_from_root('src', 'settings.js')).read().replace('//', '#')
+ settings = re.sub(r'var ([\w\d]+)', r'self.attrs["\1"]', settings)
+ exec settings
+
+ # Apply additional settings. First -O, then -s
+ for i in range(len(args)):
+ if args[i].startswith('-O'):
+ level = eval(args[i][2])
+ self.apply_opt_level(level)
+ for i in range(len(args)):
+ if args[i] == '-s':
+ declare = re.sub(r'([\w\d]+)\s*=\s*(.+)', r'self.attrs["\1"]=\2;', args[i+1])
+ exec declare
+
+ # Transforms the Settings information into emcc-compatible args (-s X=Y, etc.). Basically
+ # the reverse of load_settings, except for -Ox which is relevant there but not here
+ @classmethod
+ def serialize(self):
+ ret = []
+ for key, value in self.attrs.iteritems():
+ if key == key.upper(): # this is a hack. all of our settings are ALL_CAPS, python internals are not
+ jsoned = json.dumps(value, sort_keys=True)
+ ret += ['-s', key + '=' + jsoned]
+ return ret
+
+ @classmethod
+ def apply_opt_level(self, opt_level, noisy=False):
+ if opt_level >= 1:
+ self.attrs['ASM_JS'] = 1
+ self.attrs['ASSERTIONS'] = 0
+ self.attrs['DISABLE_EXCEPTION_CATCHING'] = 1
+ self.attrs['EMIT_GENERATED_FUNCTIONS'] = 1
+ if opt_level >= 2:
+ self.attrs['RELOOP'] = 1
+ self.attrs['ALIASING_FUNCTION_POINTERS'] = 1
+ if opt_level >= 3:
+ # Aside from these, -O3 also runs closure compiler and llvm lto
+ self.attrs['FORCE_ALIGNED_MEMORY'] = 1
+ self.attrs['DOUBLE_MODE'] = 0
+ self.attrs['PRECISE_I64_MATH'] = 0
+ if noisy: logging.warning('Applying some potentially unsafe optimizations! (Use -O2 if this fails.)')
+
+ def __getattr__(self, attr):
+ if attr in self.attrs:
+ return self.attrs[attr]
+ else:
+ raise AttributeError
+
+ def __setattr__(self, attr, value):
+ self.attrs[attr] = value
+
+ __instance = None
+
+ @staticmethod
+ def instance():
+ if Settings2.__instance is None:
+ Settings2.__instance = Settings2.__impl()
+ return Settings2.__instance
+
+ def __getattr__(self, attr):
+ return getattr(self.instance(), attr)
+
+ def __setattr__(self, attr, value):
+ return setattr(self.instance(), attr, value)
+
+class Settings(object):
+ __metaclass__ = Settings2
# Building
@@ -706,6 +765,7 @@ class Building:
COMPILER = CLANG
LLVM_OPTS = False
COMPILER_TEST_OPTS = [] # For use of the test runner
+ JS_ENGINE_OVERRIDE = None # Used to pass the JS engine override from runner.py -> test_benchmark.py
@staticmethod
def get_building_env(native=False):
@@ -876,7 +936,10 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
@staticmethod
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)
+ # Tracking unresolveds is necessary for .a linking, see below.
+ # Specify all possible entry points to seed the linking process.
+ # For a simple application, this would just be "main".
+ unresolved_symbols = set([func[1:] for func in Settings.EXPORTED_FUNCTIONS])
resolved_symbols = set()
temp_dirs = []
files = map(os.path.abspath, files)
@@ -944,7 +1007,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
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]
+ link_cmd = LINK_CMD + actual_files + ['-o', target]
# 8k is a bit of an arbitrary limit, but a reasonable one
# for max command line size before we use a respose file
response_file = None
@@ -952,7 +1015,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
logging.debug('using response file for llvm-link')
[response_fd, response_file] = mkstemp(suffix='.response', dir=TEMP_DIR)
- link_cmd = [LLVM_LINK, "@" + response_file]
+ link_cmd = LINK_CMD + ["@" + response_file]
response_fh = os.fdopen(response_fd, 'w')
for arg in actual_files:
@@ -996,7 +1059,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
opts = Building.pick_llvm_opts(opts)
#opts += ['-debug-pass=Arguments']
logging.debug('emcc: LLVM opts: ' + str(opts))
- output = Popen([LLVM_OPT, filename] + opts + ['-o=' + filename + '.opt.bc'], stdout=PIPE).communicate()[0]
+ output = Popen([LLVM_OPT, filename] + opts + ['-o', filename + '.opt.bc'], stdout=PIPE).communicate()[0]
assert os.path.exists(filename + '.opt.bc'), 'Failed to run llvm optimizations: ' + output
shutil.move(filename + '.opt.bc', filename)
@@ -1004,7 +1067,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
def llvm_opts(filename): # deprecated version, only for test runner. TODO: remove
if Building.LLVM_OPTS:
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]
+ 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
@staticmethod
@@ -1015,7 +1078,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
output_filename = input_filename + '.o.ll'
input_filename = input_filename + '.o'
try_delete(output_filename)
- output = Popen([LLVM_DIS, input_filename, '-o=' + output_filename], stdout=PIPE).communicate()[0]
+ output = Popen([LLVM_DIS, input_filename, '-o', output_filename], stdout=PIPE).communicate()[0]
assert os.path.exists(output_filename), 'Could not create .ll file: ' + output
return output_filename
@@ -1027,7 +1090,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
output_filename = input_filename + '.o'
input_filename = input_filename + '.o.ll'
try_delete(output_filename)
- output = Popen([LLVM_AS, input_filename, '-o=' + output_filename], stdout=PIPE).communicate()[0]
+ output = Popen([LLVM_AS, input_filename, '-o', output_filename], stdout=PIPE).communicate()[0]
assert os.path.exists(output_filename), 'Could not create bc file: ' + output
return output_filename
@@ -1048,6 +1111,9 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
for line in output.split('\n'):
if len(line) == 0: continue
parts = filter(lambda seg: len(seg) > 0, line.split(' '))
+ # pnacl-nm will print zero offsets for bitcode
+ if len(parts) == 3 and parts[0] == "00000000":
+ parts.pop(0)
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':