1# Copyright 2011 The Emscripten Authors.  All rights reserved.
2# Emscripten is available under two separate licenses, the MIT license and the
3# University of Illinois/NCSA Open Source License.  Both these licenses can be
4# found in the LICENSE file.
5
6from __future__ import print_function
7
8from subprocess import PIPE
9import atexit
10import binascii
11import base64
12import difflib
13import json
14import logging
15import math
16import os
17import re
18import shutil
19import subprocess
20import time
21import sys
22import tempfile
23
24if sys.version_info < (3, 5):
25  print('error: emscripten requires python 3.5 or above', file=sys.stderr)
26  sys.exit(1)
27
28from .toolchain_profiler import ToolchainProfiler
29from .tempfiles import try_delete
30from . import cache, tempfiles, colored_logger
31from . import diagnostics
32
33
34__rootpath__ = '/usr/local/lib/emscripten'
35WINDOWS = sys.platform.startswith('win')
36MACOS = sys.platform == 'darwin'
37LINUX = sys.platform.startswith('linux')
38DEBUG = int(os.environ.get('EMCC_DEBUG', '0'))
39EXPECTED_NODE_VERSION = (4, 1, 1)
40EXPECTED_BINARYEN_VERSION = 95
41EXPECTED_LLVM_VERSION = "12.0"
42SIMD_INTEL_FEATURE_TOWER = ['-msse', '-msse2', '-msse3', '-mssse3', '-msse4.1', '-msse4.2', '-mavx']
43SIMD_NEON_FLAGS = ['-mfpu=neon']
44
45# can add  %(asctime)s  to see timestamps
46logging.basicConfig(format='%(name)s:%(levelname)s: %(message)s',
47                    level=logging.DEBUG if DEBUG else logging.INFO)
48colored_logger.enable()
49logger = logging.getLogger('shared')
50
51if sys.version_info < (2, 7, 12):
52  logger.debug('python versions older than 2.7.12 are known to run into outdated SSL certificate related issues, https://github.com/emscripten-core/emscripten/issues/6275')
53
54# warning about absolute-paths is disabled by default, and not enabled by -Wall
55diagnostics.add_warning('absolute-paths', enabled=False, part_of_all=False)
56diagnostics.add_warning('almost-asm')
57diagnostics.add_warning('invalid-input')
58# Don't show legacy settings warnings by default
59diagnostics.add_warning('legacy-settings', enabled=False, part_of_all=False)
60# Catch-all for other emcc warnings
61diagnostics.add_warning('linkflags')
62diagnostics.add_warning('emcc')
63diagnostics.add_warning('undefined', error=True)
64diagnostics.add_warning('deprecated')
65diagnostics.add_warning('version-check')
66diagnostics.add_warning('export-main')
67diagnostics.add_warning('unused-command-line-argument', shared=True)
68diagnostics.add_warning('pthreads-mem-growth')
69
70
71def exit_with_error(msg, *args):
72  diagnostics.error(msg, *args)
73
74
75# On Windows python suffers from a particularly nasty bug if python is spawning
76# new processes while python itself is spawned from some other non-console
77# process.
78# Use a custom replacement for Popen on Windows to avoid the "WindowsError:
79# [Error 6] The handle is invalid" errors when emcc is driven through cmake or
80# mingw32-make.
81# See http://bugs.python.org/issue3905
82class WindowsPopen(object):
83  def __init__(self, args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False,
84               shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0):
85    self.stdin = stdin
86    self.stdout = stdout
87    self.stderr = stderr
88
89    # (stdin, stdout, stderr) store what the caller originally wanted to be done with the streams.
90    # (stdin_, stdout_, stderr_) will store the fixed set of streams that workaround the bug.
91    self.stdin_ = stdin
92    self.stdout_ = stdout
93    self.stderr_ = stderr
94
95    # If the caller wants one of these PIPEd, we must PIPE them all to avoid the 'handle is invalid' bug.
96    if self.stdin_ == PIPE or self.stdout_ == PIPE or self.stderr_ == PIPE:
97      if self.stdin_ is None:
98        self.stdin_ = PIPE
99      if self.stdout_ is None:
100        self.stdout_ = PIPE
101      if self.stderr_ is None:
102        self.stderr_ = PIPE
103
104    try:
105      # Call the process with fixed streams.
106      self.process = subprocess.Popen(args, bufsize, executable, self.stdin_, self.stdout_, self.stderr_, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags)
107      self.pid = self.process.pid
108    except Exception as e:
109      logger.error('\nsubprocess.Popen(args=%s) failed! Exception %s\n' % (shlex_join(args), str(e)))
110      raise
111
112  def communicate(self, input=None):
113    output = self.process.communicate(input)
114    self.returncode = self.process.returncode
115
116    # If caller never wanted to PIPE stdout or stderr, route the output back to screen to avoid swallowing output.
117    if self.stdout is None and self.stdout_ == PIPE and len(output[0].strip()):
118      print(output[0], file=sys.stdout)
119    if self.stderr is None and self.stderr_ == PIPE and len(output[1].strip()):
120      print(output[1], file=sys.stderr)
121
122    # Return a mock object to the caller. This works as long as all emscripten code immediately .communicate()s the result, and doesn't
123    # leave the process object around for longer/more exotic uses.
124    if self.stdout is None and self.stderr is None:
125      return (None, None)
126    if self.stdout is None:
127      return (None, output[1])
128    if self.stderr is None:
129      return (output[0], None)
130    return (output[0], output[1])
131
132  def poll(self):
133    return self.process.poll()
134
135  def kill(self):
136    return self.process.kill()
137
138
139def path_from_root(*pathelems):
140  return os.path.join(__rootpath__, *pathelems)
141
142
143def root_is_writable():
144  return os.access(__rootpath__, os.W_OK)
145
146
147# Switch to shlex.quote once we can depend on python 3
148def shlex_quote(arg):
149  if ' ' in arg and (not (arg.startswith('"') and arg.endswith('"'))) and (not (arg.startswith("'") and arg.endswith("'"))):
150    return '"' + arg.replace('"', '\\"') + '"'
151
152  return arg
153
154
155# Switch to shlex.join once we can depend on python 3.8:
156# https://docs.python.org/3/library/shlex.html#shlex.join
157def shlex_join(cmd):
158  return ' '.join(shlex_quote(x) for x in cmd)
159
160
161# This is a workaround for https://bugs.python.org/issue9400
162class Py2CalledProcessError(subprocess.CalledProcessError):
163  def __init__(self, returncode, cmd, output=None, stderr=None):
164    super(Exception, self).__init__(returncode, cmd, output, stderr)
165    self.returncode = returncode
166    self.cmd = cmd
167    self.output = output
168    self.stderr = stderr
169
170
171# https://docs.python.org/3/library/subprocess.html#subprocess.CompletedProcess
172class Py2CompletedProcess:
173  def __init__(self, args, returncode, stdout, stderr):
174    self.args = args
175    self.returncode = returncode
176    self.stdout = stdout
177    self.stderr = stderr
178
179  def __repr__(self):
180    _repr = ['args=%s' % repr(self.args), 'returncode=%s' % self.returncode]
181    if self.stdout is not None:
182      _repr.append('stdout=' + repr(self.stdout))
183    if self.stderr is not None:
184      _repr.append('stderr=' + repr(self.stderr))
185    return 'CompletedProcess(%s)' % ', '.join(_repr)
186
187  def check_returncode(self):
188    if self.returncode != 0:
189      raise Py2CalledProcessError(returncode=self.returncode, cmd=self.args, output=self.stdout, stderr=self.stderr)
190
191
192def run_process(cmd, check=True, input=None, *args, **kw):
193  """Runs a subpocess returning the exit code.
194
195  By default this function will raise an exception on failure.  Therefor this should only be
196  used if you want to handle such failures.  For most subprocesses, failures are not recoverable
197  and should be fatal.  In those cases the `check_call` wrapper should be preferred.
198  """
199
200  kw.setdefault('universal_newlines', True)
201
202  debug_text = '%sexecuted %s' % ('successfully ' if check else '', shlex_join(cmd))
203
204  if hasattr(subprocess, "run"):
205    ret = subprocess.run(cmd, check=check, input=input, *args, **kw)
206    logger.debug(debug_text)
207    return ret
208
209  # Python 2 compatibility: Introduce Python 3 subprocess.run-like behavior
210  if input is not None:
211    kw['stdin'] = subprocess.PIPE
212  proc = Popen(cmd, *args, **kw)
213  stdout, stderr = proc.communicate(input)
214  result = Py2CompletedProcess(cmd, proc.returncode, stdout, stderr)
215  if check:
216    result.check_returncode()
217  logger.debug(debug_text)
218  return result
219
220
221def check_call(cmd, *args, **kw):
222  """Like `run_process` above but treat failures as fatal and exit_with_error."""
223  try:
224    return run_process(cmd, *args, **kw)
225  except subprocess.CalledProcessError as e:
226    exit_with_error("'%s' failed (%d)", shlex_join(cmd), e.returncode)
227  except OSError as e:
228    exit_with_error("'%s' failed: %s", shlex_join(cmd), str(e))
229
230
231def run_js_tool(filename, jsargs=[], *args, **kw):
232  """Execute a javascript tool.
233
234  This is used by emcc to run parts of the build process that are written
235  implemented in javascript.
236  """
237  command = NODE_JS + [filename] + jsargs
238  print_compiler_stage(command)
239  return check_call(command, *args, **kw).stdout
240
241
242# Finds the given executable 'program' in PATH. Operates like the Unix tool 'which'.
243def which(program):
244  def is_exe(fpath):
245    return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
246
247  if os.path.isabs(program):
248    if os.path.isfile(program):
249      return program
250
251    if WINDOWS:
252      for suffix in ['.exe', '.cmd', '.bat']:
253        if is_exe(program + suffix):
254          return program + suffix
255
256  fpath, fname = os.path.split(program)
257  if fpath:
258    if is_exe(program):
259      return program
260  else:
261    for path in os.environ["PATH"].split(os.pathsep):
262      path = path.strip('"')
263      exe_file = os.path.join(path, program)
264      if is_exe(exe_file):
265        return exe_file
266      if WINDOWS:
267        for suffix in ('.exe', '.cmd', '.bat'):
268          if is_exe(exe_file + suffix):
269            return exe_file + suffix
270
271  return None
272
273
274# Only used by tests and by ctor_evaller.py.   Once fastcomp is removed
275# this can most likely be moved into the tests/jsrun.py.
276def timeout_run(proc, timeout=None, full_output=False, check=True):
277  start = time.time()
278  if timeout is not None:
279    while time.time() - start < timeout and proc.poll() is None:
280      time.sleep(0.1)
281    if proc.poll() is None:
282      proc.kill() # XXX bug: killing emscripten.py does not kill it's child process!
283      raise Exception("Timed out")
284  stdout, stderr = proc.communicate()
285  out = ['' if o is None else o for o in (stdout, stderr)]
286  if check and proc.returncode != 0:
287    raise subprocess.CalledProcessError(proc.returncode, '', stdout, stderr)
288  if TRACK_PROCESS_SPAWNS:
289    logging.info('Process ' + str(proc.pid) + ' finished after ' + str(time.time() - start) + ' seconds. Exit code: ' + str(proc.returncode))
290  return '\n'.join(out) if full_output else out[0]
291
292
293def generate_config(path, first_time=False):
294  # Note: repr is used to ensure the paths are escaped correctly on Windows.
295  # The full string is replaced so that the template stays valid Python.
296  config_file = open(path_from_root('tools', 'settings_template.py')).read().splitlines()
297  config_file = config_file[3:] # remove the initial comment
298  config_file = '\n'.join(config_file)
299  # autodetect some default paths
300  config_file = config_file.replace('\'{{{ EMSCRIPTEN_ROOT }}}\'', repr(EMSCRIPTEN_ROOT))
301  llvm_root = os.path.dirname(which('llvm-dis') or '/usr/bin/llvm-dis')
302  config_file = config_file.replace('\'{{{ LLVM_ROOT }}}\'', repr(llvm_root))
303
304  node = which('nodejs') or which('node') or 'node'
305  config_file = config_file.replace('\'{{{ NODE }}}\'', repr(node))
306
307  abspath = os.path.abspath(os.path.expanduser(path))
308  # write
309  with open(abspath, 'w') as f:
310    f.write(config_file)
311
312  if first_time:
313    print('''
314==============================================================================
315Welcome to Emscripten!
316
317This is the first time any of the Emscripten tools has been run.
318
319A settings file has been copied to %s, at absolute path: %s
320
321It contains our best guesses for the important paths, which are:
322
323  LLVM_ROOT       = %s
324  NODE_JS         = %s
325  EMSCRIPTEN_ROOT = %s
326
327Please edit the file if any of those are incorrect.
328
329This command will now exit. When you are done editing those paths, re-run it.
330==============================================================================
331''' % (path, abspath, llvm_root, node, EMSCRIPTEN_ROOT), file=sys.stderr)
332
333
334def parse_config_file():
335  """Parse the emscripten config file using python's exec.
336
337  Also also EM_<KEY> environment variables to override specific config keys.
338  """
339  config = {}
340  config_text = open(CONFIG_FILE, 'r').read() if CONFIG_FILE else EM_CONFIG
341  try:
342    exec(config_text, config)
343  except Exception as e:
344    exit_with_error('Error in evaluating %s (at %s): %s, text: %s', EM_CONFIG, CONFIG_FILE, str(e), config_text)
345
346  CONFIG_KEYS = (
347    'NODE_JS',
348    'BINARYEN_ROOT',
349    'POPEN_WORKAROUND',
350    'SPIDERMONKEY_ENGINE',
351    'V8_ENGINE',
352    'LLVM_ROOT',
353    'LLVM_ADD_VERSION',
354    'CLANG_ADD_VERSION',
355    'CLOSURE_COMPILER',
356    'JAVA',
357    'JS_ENGINE',
358    'JS_ENGINES',
359    'WASMER',
360    'WASMTIME',
361    'WASM_ENGINES',
362    'FROZEN_CACHE',
363    'CACHE',
364    'PORTS',
365  )
366
367  # Only propagate certain settings from the config file.
368  for key in CONFIG_KEYS:
369    env_var = 'EM_' + key
370    env_value = os.environ.get(env_var)
371    if env_value is not None:
372      globals()[key] = env_value
373    elif key in config:
374      globals()[key] = config[key]
375
376  # Certain keys are mandatory
377  for key in ('LLVM_ROOT', 'NODE_JS', 'BINARYEN_ROOT'):
378    if key not in config:
379      exit_with_error('%s is not defined in %s', key, config_file_location())
380    if not globals()[key]:
381      exit_with_error('%s is set to empty value in %s', key, config_file_location())
382
383  if not NODE_JS:
384    exit_with_error('NODE_JS is not defined in %s', config_file_location())
385
386
387def listify(x):
388  if type(x) is not list:
389    return [x]
390  return x
391
392
393def fix_js_engine(old, new):
394  global JS_ENGINES
395  if old is None:
396    return
397  JS_ENGINES = [new if x == old else x for x in JS_ENGINES]
398  return new
399
400
401def get_npm_cmd(name):
402  if WINDOWS:
403    cmd = [path_from_root('node_modules', '.bin', name + '.cmd')]
404  else:
405    cmd = NODE_JS + [path_from_root('node_modules', '.bin', name)]
406  if not os.path.exists(cmd[-1]):
407    exit_with_error('%s was not found! Please run "npm install" in Emscripten root directory to set up npm dependencies' % name)
408  return cmd
409
410
411def normalize_config_settings():
412  global CACHE, PORTS, JAVA
413  global NODE_JS, V8_ENGINE, JS_ENGINE, JS_ENGINES, SPIDERMONKEY_ENGINE, WASM_ENGINES
414
415  # EM_CONFIG stuff
416  if not JS_ENGINES:
417    JS_ENGINES = [NODE_JS]
418  if not JS_ENGINE:
419    JS_ENGINE = JS_ENGINES[0]
420
421  # Engine tweaks
422  if SPIDERMONKEY_ENGINE:
423    new_spidermonkey = SPIDERMONKEY_ENGINE
424    if '-w' not in str(new_spidermonkey):
425      new_spidermonkey += ['-w']
426    SPIDERMONKEY_ENGINE = fix_js_engine(SPIDERMONKEY_ENGINE, new_spidermonkey)
427  NODE_JS = fix_js_engine(NODE_JS, listify(NODE_JS))
428  V8_ENGINE = fix_js_engine(V8_ENGINE, listify(V8_ENGINE))
429  JS_ENGINE = fix_js_engine(JS_ENGINE, listify(JS_ENGINE))
430  JS_ENGINES = [listify(engine) for engine in JS_ENGINES]
431  WASM_ENGINES = [listify(engine) for engine in WASM_ENGINES]
432  if not CACHE:
433    if root_is_writable():
434      CACHE = path_from_root('cache')
435    else:
436      # Use the legacy method of putting the cache in the user's home directory
437      # if the emscripten root is not writable.
438      # This is useful mostly for read-only installation and perhaps could
439      # be removed in the future since such installations should probably be
440      # setting a specific cache location.
441      logger.debug('Using home-directory for emscripten cache due to read-only root')
442      CACHE = os.path.expanduser(os.path.join('~', '.emscripten_cache'))
443  if not PORTS:
444    PORTS = os.path.join(CACHE, 'ports')
445
446  if JAVA is None:
447    logger.debug('JAVA not defined in ' + config_file_location() + ', using "java"')
448    JAVA = 'java'
449
450
451# Returns the location of the emscripten config file.
452def config_file_location():
453  # Handle the case where there is no config file at all (i.e. If EM_CONFIG is passed as python code
454  # direclty on the command line).
455  if not CONFIG_FILE:
456    return '<inline config>'
457
458  return CONFIG_FILE
459
460
461def get_clang_version():
462  if not hasattr(get_clang_version, 'found_version'):
463    if not os.path.exists(CLANG_CC):
464      exit_with_error('clang executable not found at `%s`' % CLANG_CC)
465    proc = check_call([CLANG_CC, '--version'], stdout=PIPE)
466    m = re.search(r'[Vv]ersion\s+(\d+\.\d+)', proc.stdout)
467    get_clang_version.found_version = m and m.group(1)
468  return get_clang_version.found_version
469
470
471def check_llvm_version():
472  actual = get_clang_version()
473  if EXPECTED_LLVM_VERSION in actual:
474    return True
475  diagnostics.warning('version-check', 'LLVM version appears incorrect (seeing "%s", expected "%s")', actual, EXPECTED_LLVM_VERSION)
476  return False
477
478
479def get_llc_targets():
480  if not os.path.exists(LLVM_COMPILER):
481    exit_with_error('llc executable not found at `%s`' % LLVM_COMPILER)
482  try:
483    llc_version_info = run_process([LLVM_COMPILER, '--version'], stdout=PIPE).stdout
484  except subprocess.CalledProcessError:
485    exit_with_error('error running `llc --version`.  Check your llvm installation (%s)' % LLVM_COMPILER)
486  if 'Registered Targets:' not in llc_version_info:
487    exit_with_error('error parsing output of `llc --version`.  Check your llvm installation (%s)' % LLVM_COMPILER)
488  pre, targets = llc_version_info.split('Registered Targets:')
489  return targets
490
491
492def check_llvm():
493  targets = get_llc_targets()
494  if 'wasm32' not in targets and 'WebAssembly 32-bit' not in targets:
495    logger.critical('LLVM has not been built with the WebAssembly backend, llc reports:')
496    print('===========================================================================', file=sys.stderr)
497    print(targets, file=sys.stderr)
498    print('===========================================================================', file=sys.stderr)
499    return False
500
501  return True
502
503
504def get_node_directory():
505  return os.path.dirname(NODE_JS[0] if type(NODE_JS) is list else NODE_JS)
506
507
508# When we run some tools from npm (closure, html-minifier-terser), those
509# expect that the tools have node.js accessible in PATH. Place our node
510# there when invoking those tools.
511def env_with_node_in_path():
512  env = os.environ.copy()
513  env['PATH'] = get_node_directory() + os.pathsep + env['PATH']
514  return env
515
516
517def check_node_version():
518  try:
519    actual = run_process(NODE_JS + ['--version'], stdout=PIPE).stdout.strip()
520    version = tuple(map(int, actual.replace('v', '').replace('-pre', '').split('.')))
521  except Exception as e:
522    diagnostics.warning('version-check', 'cannot check node version: %s', e)
523    return False
524
525  if version < EXPECTED_NODE_VERSION:
526    diagnostics.warning('version-check', 'node version appears too old (seeing "%s", expected "%s")', actual, 'v' + ('.'.join(map(str, EXPECTED_NODE_VERSION))))
527    return False
528
529  return True
530
531
532def set_version_globals():
533  global EMSCRIPTEN_VERSION, EMSCRIPTEN_VERSION_MAJOR, EMSCRIPTEN_VERSION_MINOR, EMSCRIPTEN_VERSION_TINY
534  filename = path_from_root('emscripten-version.txt')
535  with open(filename) as f:
536    EMSCRIPTEN_VERSION = f.read().strip().replace('"', '')
537  parts = [int(x) for x in EMSCRIPTEN_VERSION.split('.')]
538  EMSCRIPTEN_VERSION_MAJOR, EMSCRIPTEN_VERSION_MINOR, EMSCRIPTEN_VERSION_TINY = parts
539
540
541def generate_sanity():
542  sanity_file_content = EMSCRIPTEN_VERSION + '|' + LLVM_ROOT + '|' + get_clang_version()
543  if CONFIG_FILE:
544    config = open(CONFIG_FILE).read()
545  else:
546    config = EM_CONFIG
547  # Convert to unsigned for python2 and python3 compat
548  checksum = binascii.crc32(config.encode()) & 0xffffffff
549  sanity_file_content += '|%#x\n' % checksum
550  return sanity_file_content
551
552
553def perform_sanify_checks():
554  logger.info('(Emscripten: Running sanity checks)')
555
556  with ToolchainProfiler.profile_block('sanity compiler_engine'):
557    try:
558      run_process(NODE_JS + ['-e', 'console.log("hello")'], stdout=PIPE)
559    except Exception as e:
560      exit_with_error('The configured node executable (%s) does not seem to work, check the paths in %s (%s)', NODE_JS, config_file_location, str(e))
561
562  with ToolchainProfiler.profile_block('sanity LLVM'):
563    for cmd in [CLANG_CC, LLVM_AR, LLVM_AS, LLVM_NM]:
564      if not os.path.exists(cmd) and not os.path.exists(cmd + '.exe'):  # .exe extension required for Windows
565        exit_with_error('Cannot find %s, check the paths in %s', cmd, EM_CONFIG)
566
567
568def check_sanity(force=False):
569  """Check that basic stuff we need (a JS engine to compile, Node.js, and Clang
570  and LLVM) exists.
571
572  The test runner always does this check (through |force|). emcc does this less
573  frequently, only when ${EM_CONFIG}_sanity does not exist or is older than
574  EM_CONFIG (so, we re-check sanity when the settings are changed).  We also
575  re-check sanity and clear the cache when the version changes.
576  """
577  if not force and os.environ.get('EMCC_SKIP_SANITY_CHECK') == '1':
578    return
579  # We set EMCC_SKIP_SANITY_CHECK so that any subprocesses that we launch will
580  # not re-run the tests.
581  os.environ['EMCC_SKIP_SANITY_CHECK'] = '1'
582  with ToolchainProfiler.profile_block('sanity'):
583    check_llvm_version()
584    if not CONFIG_FILE:
585      return # config stored directly in EM_CONFIG => skip sanity checks
586    expected = generate_sanity()
587
588    sanity_file = Cache.get_path('sanity.txt', root=True)
589    if os.path.exists(sanity_file):
590      sanity_data = open(sanity_file).read()
591      if sanity_data != expected:
592        logger.debug('old sanity: %s' % sanity_data)
593        logger.debug('new sanity: %s' % expected)
594        if FROZEN_CACHE:
595          logger.info('(Emscripten: config changed, cache may need to be cleared, but FROZEN_CACHE is set)')
596        else:
597          logger.info('(Emscripten: config changed, clearing cache)')
598          Cache.erase()
599          # the check actually failed, so definitely write out the sanity file, to
600          # avoid others later seeing failures too
601          force = False
602      elif not force:
603        return # all is well
604
605    # some warning, mostly not fatal checks - do them even if EM_IGNORE_SANITY is on
606    check_node_version()
607
608    llvm_ok = check_llvm()
609
610    if os.environ.get('EM_IGNORE_SANITY'):
611      logger.info('EM_IGNORE_SANITY set, ignoring sanity checks')
612      return
613
614    if not llvm_ok:
615      exit_with_error('failing sanity checks due to previous llvm failure')
616
617    perform_sanify_checks()
618
619    if not force:
620      # Only create/update this file if the sanity check succeeded, i.e., we got here
621      Cache.ensure()
622      with open(sanity_file, 'w') as f:
623        f.write(expected)
624
625
626# Some distributions ship with multiple llvm versions so they add
627# the version to the binaries, cope with that
628def build_llvm_tool_path(tool):
629  if LLVM_ADD_VERSION:
630    return os.path.join(LLVM_ROOT, tool + "-" + LLVM_ADD_VERSION)
631  else:
632    return os.path.join(LLVM_ROOT, tool)
633
634
635# Some distributions ship with multiple clang versions so they add
636# the version to the binaries, cope with that
637def build_clang_tool_path(tool):
638  if CLANG_ADD_VERSION:
639    return os.path.join(LLVM_ROOT, tool + "-" + CLANG_ADD_VERSION)
640  else:
641    return os.path.join(LLVM_ROOT, tool)
642
643
644def exe_suffix(cmd):
645  return cmd + '.exe' if WINDOWS else cmd
646
647
648def bat_suffix(cmd):
649  return cmd + '.bat' if WINDOWS else cmd
650
651
652def replace_suffix(filename, new_suffix):
653  assert new_suffix[0] == '.'
654  return os.path.splitext(filename)[0] + new_suffix
655
656
657# In MINIMAL_RUNTIME mode, keep suffixes of generated files simple
658# ('.mem' instead of '.js.mem'; .'symbols' instead of '.js.symbols' etc)
659# Retain the original naming scheme in traditional runtime.
660def replace_or_append_suffix(filename, new_suffix):
661  assert new_suffix[0] == '.'
662  return replace_suffix(filename, new_suffix) if Settings.MINIMAL_RUNTIME else filename + new_suffix
663
664
665def safe_ensure_dirs(dirname):
666  try:
667    os.makedirs(dirname)
668  except OSError:
669    # Python 2 compatibility: makedirs does not support exist_ok parameter
670    # Ignore error for already existing dirname as exist_ok does
671    if not os.path.isdir(dirname):
672      raise
673
674
675# Temp dir. Create a random one, unless EMCC_DEBUG is set, in which case use the canonical
676# temp directory (TEMP_DIR/emscripten_temp).
677def get_emscripten_temp_dir():
678  """Returns a path to EMSCRIPTEN_TEMP_DIR, creating one if it didn't exist."""
679  global configuration, EMSCRIPTEN_TEMP_DIR
680  if not EMSCRIPTEN_TEMP_DIR:
681    EMSCRIPTEN_TEMP_DIR = tempfile.mkdtemp(prefix='emscripten_temp_', dir=configuration.TEMP_DIR)
682
683    def prepare_to_clean_temp(d):
684      def clean_temp():
685        try_delete(d)
686
687      atexit.register(clean_temp)
688    prepare_to_clean_temp(EMSCRIPTEN_TEMP_DIR) # this global var might change later
689  return EMSCRIPTEN_TEMP_DIR
690
691
692def get_canonical_temp_dir(temp_dir):
693  return os.path.join(temp_dir, 'emscripten_temp')
694
695
696class Configuration(object):
697  def __init__(self):
698    self.EMSCRIPTEN_TEMP_DIR = None
699
700    self.TEMP_DIR = os.environ.get("EMCC_TEMP_DIR", tempfile.gettempdir())
701    if not os.path.isdir(self.TEMP_DIR):
702      exit_with_error("The temporary directory `" + self.TEMP_DIR + "` does not exist! Please make sure that the path is correct.")
703
704    self.CANONICAL_TEMP_DIR = get_canonical_temp_dir(self.TEMP_DIR)
705
706    if DEBUG:
707      self.EMSCRIPTEN_TEMP_DIR = self.CANONICAL_TEMP_DIR
708      try:
709        safe_ensure_dirs(self.EMSCRIPTEN_TEMP_DIR)
710      except Exception as e:
711        exit_with_error(str(e) + 'Could not create canonical temp dir. Check definition of TEMP_DIR in ' + config_file_location())
712
713  def get_temp_files(self):
714    return tempfiles.TempFiles(
715      tmp=self.TEMP_DIR if not DEBUG else get_emscripten_temp_dir(),
716      save_debug_files=os.environ.get('EMCC_DEBUG_SAVE'))
717
718
719def apply_configuration():
720  global configuration, EMSCRIPTEN_TEMP_DIR, CANONICAL_TEMP_DIR, TEMP_DIR
721  configuration = Configuration()
722  EMSCRIPTEN_TEMP_DIR = configuration.EMSCRIPTEN_TEMP_DIR
723  CANONICAL_TEMP_DIR = configuration.CANONICAL_TEMP_DIR
724  TEMP_DIR = configuration.TEMP_DIR
725
726
727def get_llvm_target():
728  return LLVM_TARGET
729
730
731def emsdk_ldflags(user_args):
732  if os.environ.get('EMMAKEN_NO_SDK'):
733    return []
734
735  library_paths = [
736      path_from_root('system', 'local', 'lib'),
737      path_from_root('system', 'lib'),
738      Cache.dirname
739  ]
740  ldflags = ['-L' + l for l in library_paths]
741
742  if '-nostdlib' in user_args:
743    return ldflags
744
745  # TODO(sbc): Add system libraries here rather than conditionally including
746  # them via .symbols files.
747  libraries = []
748  ldflags += ['-l' + l for l in libraries]
749
750  return ldflags
751
752
753def emsdk_cflags(user_args, cxx):
754  # Disable system C and C++ include directories, and add our own (using
755  # -isystem so they are last, like system dirs, which allows projects to
756  # override them)
757
758  c_opts = ['-Xclang', '-nostdsysteminc']
759
760  c_include_paths = [
761    path_from_root('system', 'include', 'compat'),
762    path_from_root('system', 'include'),
763    path_from_root('system', 'include', 'libc'),
764    path_from_root('system', 'lib', 'libc', 'musl', 'arch', 'emscripten'),
765    path_from_root('system', 'local', 'include'),
766    path_from_root('system', 'include', 'SSE'),
767    path_from_root('system', 'include', 'neon'),
768    path_from_root('system', 'lib', 'compiler-rt', 'include'),
769    path_from_root('system', 'lib', 'libunwind', 'include'),
770    Cache.get_path('include')
771  ]
772
773  cxx_include_paths = [
774    path_from_root('system', 'include', 'libcxx'),
775    path_from_root('system', 'lib', 'libcxxabi', 'include'),
776  ]
777
778  def include_directive(paths):
779    result = []
780    for path in paths:
781      result += ['-Xclang', '-isystem' + path]
782    return result
783
784  def array_contains_any_of(hay, needles):
785    for n in needles:
786      if n in hay:
787        return True
788
789  if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER) or array_contains_any_of(user_args, SIMD_NEON_FLAGS):
790    if '-msimd128' not in user_args:
791      exit_with_error('Passing any of ' + ', '.join(SIMD_INTEL_FEATURE_TOWER + SIMD_NEON_FLAGS) + ' flags also requires passing -msimd128!')
792    c_opts += ['-D__SSE__=1']
793
794  if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[1:]):
795    c_opts += ['-D__SSE2__=1']
796
797  if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[2:]):
798    c_opts += ['-D__SSE3__=1']
799
800  if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[3:]):
801    c_opts += ['-D__SSSE3__=1']
802
803  if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[4:]):
804    c_opts += ['-D__SSE4_1__=1']
805
806  if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[5:]):
807    c_opts += ['-D__SSE4_2__=1']
808
809  if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[6:]):
810    c_opts += ['-D__AVX__=1']
811
812  if array_contains_any_of(user_args, SIMD_NEON_FLAGS):
813    c_opts += ['-D__ARM_NEON__=1']
814
815  # libcxx include paths must be defined before libc's include paths otherwise libcxx will not build
816  if cxx:
817    c_opts += include_directive(cxx_include_paths)
818  return c_opts + include_directive(c_include_paths)
819
820
821def get_asmflags(user_args):
822  return ['-target', get_llvm_target()]
823
824
825def get_cflags(user_args, cxx):
826  # Set the LIBCPP ABI version to at least 2 so that we get nicely aligned string
827  # data and other nice fixes.
828  c_opts = [# '-fno-threadsafe-statics', # disabled due to issue 1289
829            '-target', get_llvm_target(),
830            '-D__EMSCRIPTEN_major__=' + str(EMSCRIPTEN_VERSION_MAJOR),
831            '-D__EMSCRIPTEN_minor__=' + str(EMSCRIPTEN_VERSION_MINOR),
832            '-D__EMSCRIPTEN_tiny__=' + str(EMSCRIPTEN_VERSION_TINY),
833            '-D_LIBCPP_ABI_VERSION=2']
834
835  # For compatability with the fastcomp compiler that defined these
836  c_opts += ['-Dunix',
837             '-D__unix',
838             '-D__unix__']
839
840  # Changes to default clang behavior
841
842  # Implicit functions can cause horribly confusing function pointer type errors, see #2175
843  # If your codebase really needs them - very unrecommended! - you can disable the error with
844  #   -Wno-error=implicit-function-declaration
845  # or disable even a warning about it with
846  #   -Wno-implicit-function-declaration
847  c_opts += ['-Werror=implicit-function-declaration']
848
849  if os.environ.get('EMMAKEN_NO_SDK') or '-nostdinc' in user_args:
850    return c_opts
851
852  return c_opts + emsdk_cflags(user_args, cxx)
853
854
855# Settings. A global singleton. Not pretty, but nicer than passing |, settings| everywhere
856class SettingsManager(object):
857
858  class __impl(object):
859    attrs = {}
860    internal_settings = set()
861
862    def __init__(self):
863      self.reset()
864
865    @classmethod
866    def reset(cls):
867      cls.attrs = {}
868
869      # Load the JS defaults into python.
870      settings = open(path_from_root('src', 'settings.js')).read().replace('//', '#')
871      settings = re.sub(r'var ([\w\d]+)', r'attrs["\1"]', settings)
872      # Variable TARGET_NOT_SUPPORTED is referenced by value settings.js (also beyond declaring it),
873      # so must pass it there explicitly.
874      exec(settings, {'attrs': cls.attrs})
875
876      settings = open(path_from_root('src', 'settings_internal.js')).read().replace('//', '#')
877      settings = re.sub(r'var ([\w\d]+)', r'attrs["\1"]', settings)
878      internal_attrs = {}
879      exec(settings, {'attrs': internal_attrs})
880      cls.attrs.update(internal_attrs)
881
882      if 'EMCC_STRICT' in os.environ:
883        cls.attrs['STRICT'] = int(os.environ.get('EMCC_STRICT'))
884
885      # Special handling for LEGACY_SETTINGS.  See src/setting.js for more
886      # details
887      cls.legacy_settings = {}
888      cls.alt_names = {}
889      for legacy in cls.attrs['LEGACY_SETTINGS']:
890        if len(legacy) == 2:
891          name, new_name = legacy
892          cls.legacy_settings[name] = (None, 'setting renamed to ' + new_name)
893          cls.alt_names[name] = new_name
894          cls.alt_names[new_name] = name
895          default_value = cls.attrs[new_name]
896        else:
897          name, fixed_values, err = legacy
898          cls.legacy_settings[name] = (fixed_values, err)
899          default_value = fixed_values[0]
900        assert name not in cls.attrs, 'legacy setting (%s) cannot also be a regular setting' % name
901        if not cls.attrs['STRICT']:
902          cls.attrs[name] = default_value
903
904      cls.internal_settings = set(internal_attrs.keys())
905
906    # Transforms the Settings information into emcc-compatible args (-s X=Y, etc.). Basically
907    # the reverse of load_settings, except for -Ox which is relevant there but not here
908    @classmethod
909    def serialize(cls):
910      ret = []
911      for key, value in cls.attrs.items():
912        if key == key.upper():  # this is a hack. all of our settings are ALL_CAPS, python internals are not
913          jsoned = json.dumps(value, sort_keys=True)
914          ret += ['-s', key + '=' + jsoned]
915      return ret
916
917    @classmethod
918    def to_dict(cls):
919      return cls.attrs.copy()
920
921    @classmethod
922    def copy(cls, values):
923      cls.attrs = values
924
925    @classmethod
926    def apply_opt_level(cls, opt_level, shrink_level=0, noisy=False):
927      if opt_level >= 1:
928        cls.attrs['ASSERTIONS'] = 0
929      if shrink_level >= 2:
930        cls.attrs['EVAL_CTORS'] = 1
931
932    def keys(self):
933      return self.attrs.keys()
934
935    def __getattr__(self, attr):
936      if attr in self.attrs:
937        return self.attrs[attr]
938      else:
939        raise AttributeError("Settings object has no attribute '%s'" % attr)
940
941    def __setattr__(self, attr, value):
942      if attr == 'STRICT' and value:
943        for a in self.legacy_settings:
944          self.attrs.pop(a, None)
945
946      if attr in self.legacy_settings:
947        # TODO(sbc): Rather then special case this we should have STRICT turn on the
948        # legacy-settings warning below
949        if self.attrs['STRICT']:
950          exit_with_error('legacy setting used in strict mode: %s', attr)
951        fixed_values, error_message = self.legacy_settings[attr]
952        if fixed_values and value not in fixed_values:
953          exit_with_error('Invalid command line option -s ' + attr + '=' + str(value) + ': ' + error_message)
954        diagnostics.warning('legacy-settings', 'use of legacy setting: %s (%s)', attr, error_message)
955
956      if attr in self.alt_names:
957        alt_name = self.alt_names[attr]
958        self.attrs[alt_name] = value
959
960      if attr not in self.attrs:
961        msg = "Attempt to set a non-existent setting: '%s'\n" % attr
962        suggestions = difflib.get_close_matches(attr, list(self.attrs.keys()))
963        suggestions = [s for s in suggestions if s not in self.legacy_settings]
964        suggestions = ', '.join(suggestions)
965        if suggestions:
966          msg += ' - did you mean one of %s?\n' % suggestions
967        msg += " - perhaps a typo in emcc's  -s X=Y  notation?\n"
968        msg += ' - (see src/settings.js for valid values)'
969        exit_with_error(msg)
970
971      self.attrs[attr] = value
972
973    @classmethod
974    def get(cls, key):
975      return cls.attrs.get(key)
976
977    @classmethod
978    def __getitem__(cls, key):
979      return cls.attrs[key]
980
981    @classmethod
982    def target_environment_may_be(self, environment):
983      return self.attrs['ENVIRONMENT'] == '' or environment in self.attrs['ENVIRONMENT'].split(',')
984
985  __instance = None
986
987  @staticmethod
988  def instance():
989    if SettingsManager.__instance is None:
990      SettingsManager.__instance = SettingsManager.__impl()
991    return SettingsManager.__instance
992
993  def __getattr__(self, attr):
994    return getattr(self.instance(), attr)
995
996  def __setattr__(self, attr, value):
997    return setattr(self.instance(), attr, value)
998
999  def get(self, key):
1000    return self.instance().get(key)
1001
1002  def __getitem__(self, key):
1003    return self.instance()[key]
1004
1005
1006def verify_settings():
1007  if Settings.SAFE_HEAP not in [0, 1]:
1008    exit_with_error('emcc: SAFE_HEAP must be 0 or 1 in fastcomp')
1009
1010  if not Settings.WASM:
1011    # When the user requests non-wasm output, we enable wasm2js. that is,
1012    # we still compile to wasm normally, but we compile the final output
1013    # to js.
1014    Settings.WASM = 1
1015    Settings.WASM2JS = 1
1016  if Settings.WASM == 2:
1017    # Requesting both Wasm and Wasm2JS support
1018    Settings.WASM2JS = 1
1019
1020
1021def print_compiler_stage(cmd):
1022  """Emulate the '-v' of clang/gcc by printing the name of the sub-command
1023  before executing it."""
1024  if PRINT_STAGES:
1025    print(' "%s" %s' % (cmd[0], shlex_join(cmd[1:])), file=sys.stderr)
1026    sys.stderr.flush()
1027
1028
1029def mangle_c_symbol_name(name):
1030  return '_' + name if not name.startswith('$') else name[1:]
1031
1032
1033def demangle_c_symbol_name(name):
1034  return name[1:] if name.startswith('_') else '$' + name
1035
1036
1037def is_c_symbol(name):
1038  return name.startswith('_')
1039
1040
1041def treat_as_user_function(name):
1042  if name.startswith('dynCall_'):
1043    return False
1044  if name in Settings.WASM_FUNCTIONS_THAT_ARE_NOT_NAME_MANGLED:
1045    return False
1046  return True
1047
1048
1049def asmjs_mangle(name):
1050  """Mangle a name the way asm.js/JSBackend globals are mangled.
1051
1052  Prepends '_' and replaces non-alphanumerics with '_'.
1053  Used by wasm backend for JS library consistency with asm.js.
1054  """
1055  if treat_as_user_function(name):
1056    return '_' + name
1057  else:
1058    return name
1059
1060
1061def reconfigure_cache():
1062  global Cache
1063  Cache = cache.Cache(CACHE)
1064
1065
1066# Placeholder strings used for SINGLE_FILE
1067class FilenameReplacementStrings:
1068  WASM_TEXT_FILE = '{{{ FILENAME_REPLACEMENT_STRINGS_WASM_TEXT_FILE }}}'
1069  WASM_BINARY_FILE = '{{{ FILENAME_REPLACEMENT_STRINGS_WASM_BINARY_FILE }}}'
1070  ASMJS_CODE_FILE = '{{{ FILENAME_REPLACEMENT_STRINGS_ASMJS_CODE_FILE }}}'
1071
1072
1073class JS(object):
1074  memory_initializer_pattern = r'/\* memory initializer \*/ allocate\(\[([\d, ]*)\], "i8", ALLOC_NONE, ([\d+\.GLOBAL_BASEHgb]+)\);'
1075  no_memory_initializer_pattern = r'/\* no memory initializer \*/'
1076
1077  memory_staticbump_pattern = r'STATICTOP = STATIC_BASE \+ (\d+);'
1078
1079  global_initializers_pattern = r'/\* global initializers \*/ __ATINIT__.push\((.+)\);'
1080
1081  emscripten_license = '''\
1082/**
1083 * @license
1084 * Copyright 2010 The Emscripten Authors
1085 * SPDX-License-Identifier: MIT
1086 */
1087'''
1088
1089  # handle the above form, and also what closure can emit which is stuff like
1090  #  /*
1091  #
1092  #   Copyright 2019 The Emscripten Authors
1093  #   SPDX-License-Identifier: MIT
1094  #
1095  #   Copyright 2017 The Emscripten Authors
1096  #   SPDX-License-Identifier: MIT
1097  #  */
1098  emscripten_license_regex = r'\/\*\*?(\s*\*?\s*@license)?(\s*\*?\s*Copyright \d+ The Emscripten Authors\s*\*?\s*SPDX-License-Identifier: MIT)+\s*\*\/'
1099
1100  @staticmethod
1101  def handle_license(js_target):
1102    # ensure we emit the license if and only if we need to, and exactly once
1103    with open(js_target) as f:
1104      js = f.read()
1105    # first, remove the license as there may be more than once
1106    processed_js = re.sub(JS.emscripten_license_regex, '', js)
1107    if Settings.EMIT_EMSCRIPTEN_LICENSE:
1108      processed_js = JS.emscripten_license + processed_js
1109    if processed_js != js:
1110      with open(js_target, 'w') as f:
1111        f.write(processed_js)
1112
1113  @staticmethod
1114  def to_nice_ident(ident): # limited version of the JS function toNiceIdent
1115    return ident.replace('%', '$').replace('@', '_').replace('.', '_')
1116
1117  # Returns the given string with escapes added so that it can safely be placed inside a string in JS code.
1118  @staticmethod
1119  def escape_for_js_string(s):
1120    s = s.replace('\\', '/').replace("'", "\\'").replace('"', '\\"')
1121    return s
1122
1123  # Returns the subresource location for run-time access
1124  @staticmethod
1125  def get_subresource_location(path, data_uri=None):
1126    if data_uri is None:
1127      data_uri = Settings.SINGLE_FILE
1128    if data_uri:
1129      # if the path does not exist, then there is no data to encode
1130      if not os.path.exists(path):
1131        return ''
1132      with open(path, 'rb') as f:
1133        data = base64.b64encode(f.read())
1134      return 'data:application/octet-stream;base64,' + asstr(data)
1135    else:
1136      return os.path.basename(path)
1137
1138  @staticmethod
1139  def make_initializer(sig, settings=None):
1140    settings = settings or Settings
1141    if sig == 'i':
1142      return '0'
1143    elif sig == 'f':
1144      return 'Math_fround(0)'
1145    elif sig == 'j':
1146      if settings:
1147        assert settings['WASM'], 'j aka i64 only makes sense in wasm-only mode in binaryen'
1148      return 'i64(0)'
1149    else:
1150      return '+0'
1151
1152  FLOAT_SIGS = ['f', 'd']
1153
1154  @staticmethod
1155  def make_coercion(value, sig, settings=None, ffi_arg=False, ffi_result=False, convert_from=None):
1156    settings = settings or Settings
1157    if sig == 'i':
1158      if convert_from in JS.FLOAT_SIGS:
1159        value = '(~~' + value + ')'
1160      return value + '|0'
1161    if sig in JS.FLOAT_SIGS and convert_from == 'i':
1162      value = '(' + value + '|0)'
1163    if sig == 'f':
1164      if ffi_arg:
1165        return '+Math_fround(' + value + ')'
1166      elif ffi_result:
1167        return 'Math_fround(+(' + value + '))'
1168      else:
1169        return 'Math_fround(' + value + ')'
1170    elif sig == 'd' or sig == 'f':
1171      return '+' + value
1172    elif sig == 'j':
1173      if settings:
1174        assert settings['WASM'], 'j aka i64 only makes sense in wasm-only mode in binaryen'
1175      return 'i64(' + value + ')'
1176    else:
1177      return value
1178
1179  @staticmethod
1180  def legalize_sig(sig):
1181    # with BigInt support all sigs are legal since we can use i64s.
1182    if Settings.WASM_BIGINT:
1183      return sig
1184    legal = [sig[0]]
1185    # a return of i64 is legalized into an i32 (and the high bits are
1186    # accessible on the side through getTempRet0).
1187    if legal[0] == 'j':
1188      legal[0] = 'i'
1189    # a parameter of i64 is legalized into i32, i32
1190    for s in sig[1:]:
1191      if s != 'j':
1192        legal.append(s)
1193      else:
1194        legal.append('i')
1195        legal.append('i')
1196    return ''.join(legal)
1197
1198  @staticmethod
1199  def is_legal_sig(sig):
1200    # with BigInt support all sigs are legal since we can use i64s.
1201    if Settings.WASM_BIGINT:
1202      return True
1203    return sig == JS.legalize_sig(sig)
1204
1205  @staticmethod
1206  def make_jscall(sig):
1207    fnargs = ','.join('a' + str(i) for i in range(1, len(sig)))
1208    args = (',' if fnargs else '') + fnargs
1209    ret = '''\
1210function jsCall_%s(index%s) {
1211    %sfunctionPointers[index](%s);
1212}''' % (sig, args, 'return ' if sig[0] != 'v' else '', fnargs)
1213    return ret
1214
1215  @staticmethod
1216  def make_dynCall(sig, args):
1217    # wasm2c and asyncify are not yet compatible with direct wasm table calls
1218    if Settings.USE_LEGACY_DYNCALLS or not JS.is_legal_sig(sig):
1219      args = ','.join(args)
1220      if not Settings.MAIN_MODULE and not Settings.SIDE_MODULE:
1221        # Optimize dynCall accesses in the case when not building with dynamic
1222        # linking enabled.
1223        return 'dynCall_%s(%s)' % (sig, args)
1224      else:
1225        return 'Module["dynCall_%s"](%s)' % (sig, args)
1226    else:
1227      return 'wasmTable.get(%s)(%s)' % (args[0], ','.join(args[1:]))
1228
1229  @staticmethod
1230  def make_invoke(sig, named=True):
1231    legal_sig = JS.legalize_sig(sig) # TODO: do this in extcall, jscall?
1232    args = ['index'] + ['a' + str(i) for i in range(1, len(legal_sig))]
1233    ret = 'return ' if sig[0] != 'v' else ''
1234    body = '%s%s;' % (ret, JS.make_dynCall(sig, args))
1235    # C++ exceptions are numbers, and longjmp is a string 'longjmp'
1236    if Settings.SUPPORT_LONGJMP:
1237      rethrow = "if (e !== e+0 && e !== 'longjmp') throw e;"
1238    else:
1239      rethrow = "if (e !== e+0) throw e;"
1240
1241    name = (' invoke_' + sig) if named else ''
1242    ret = '''\
1243function%s(%s) {
1244  var sp = stackSave();
1245  try {
1246    %s
1247  } catch(e) {
1248    stackRestore(sp);
1249    %s
1250    _setThrew(1, 0);
1251  }
1252}''' % (name, ','.join(args), body, rethrow)
1253
1254    return ret
1255
1256  @staticmethod
1257  def align(x, by):
1258    while x % by != 0:
1259      x += 1
1260    return x
1261
1262  @staticmethod
1263  def generate_string_initializer(s):
1264    if Settings.ASSERTIONS:
1265      # append checksum of length and content
1266      crcTable = []
1267      for i in range(256):
1268        crc = i
1269        for bit in range(8):
1270          crc = (crc >> 1) ^ ((crc & 1) * 0xedb88320)
1271        crcTable.append(crc)
1272      crc = 0xffffffff
1273      n = len(s)
1274      crc = crcTable[(crc ^ n) & 0xff] ^ (crc >> 8)
1275      crc = crcTable[(crc ^ (n >> 8)) & 0xff] ^ (crc >> 8)
1276      for i in s:
1277        crc = crcTable[(crc ^ i) & 0xff] ^ (crc >> 8)
1278      for i in range(4):
1279        s.append((crc >> (8 * i)) & 0xff)
1280    s = ''.join(map(chr, s))
1281    s = s.replace('\\', '\\\\').replace("'", "\\'")
1282    s = s.replace('\n', '\\n').replace('\r', '\\r')
1283
1284    # Escape the ^Z (= 0x1a = substitute) ASCII character and all characters higher than 7-bit ASCII.
1285    def escape(x):
1286      return '\\x{:02x}'.format(ord(x.group()))
1287
1288    return re.sub('[\x1a\x80-\xff]', escape, s)
1289
1290  @staticmethod
1291  def is_dyn_call(func):
1292    return func.startswith('dynCall_')
1293
1294  @staticmethod
1295  def is_function_table(name):
1296    return name.startswith('FUNCTION_TABLE_')
1297
1298
1299class WebAssembly(object):
1300  @staticmethod
1301  def toLEB(x):
1302    assert x >= 0, 'TODO: signed'
1303    ret = []
1304    while 1:
1305      byte = x & 127
1306      x >>= 7
1307      more = x != 0
1308      if more:
1309        byte = byte | 128
1310      ret.append(byte)
1311      if not more:
1312        break
1313    return bytearray(ret)
1314
1315  @staticmethod
1316  def readLEB(buf, offset):
1317    result = 0
1318    shift = 0
1319    while True:
1320      byte = bytearray(buf[offset:offset + 1])[0]
1321      offset += 1
1322      result |= (byte & 0x7f) << shift
1323      if not (byte & 0x80):
1324        break
1325      shift += 7
1326    return (result, offset)
1327
1328  @staticmethod
1329  def add_emscripten_metadata(js_file, wasm_file):
1330    WASM_PAGE_SIZE = 65536
1331
1332    mem_size = Settings.INITIAL_MEMORY // WASM_PAGE_SIZE
1333    table_size = Settings.WASM_TABLE_SIZE
1334    global_base = Settings.GLOBAL_BASE
1335
1336    js = open(js_file).read()
1337    m = re.search(r"(^|\s)DYNAMIC_BASE\s+=\s+(\d+)", js)
1338    dynamic_base = int(m.group(2))
1339
1340    logger.debug('creating wasm emscripten metadata section with mem size %d, table size %d' % (mem_size, table_size,))
1341    name = b'\x13emscripten_metadata' # section name, including prefixed size
1342    contents = (
1343      # metadata section version
1344      WebAssembly.toLEB(EMSCRIPTEN_METADATA_MAJOR) +
1345      WebAssembly.toLEB(EMSCRIPTEN_METADATA_MINOR) +
1346
1347      # NB: The structure of the following should only be changed
1348      #     if EMSCRIPTEN_METADATA_MAJOR is incremented
1349      # Minimum ABI version
1350      WebAssembly.toLEB(EMSCRIPTEN_ABI_MAJOR) +
1351      WebAssembly.toLEB(EMSCRIPTEN_ABI_MINOR) +
1352
1353      # Wasm backend, always 1 now
1354      WebAssembly.toLEB(1) +
1355
1356      WebAssembly.toLEB(mem_size) +
1357      WebAssembly.toLEB(table_size) +
1358      WebAssembly.toLEB(global_base) +
1359      WebAssembly.toLEB(dynamic_base) +
1360      # dynamictopPtr, always 0 now
1361      WebAssembly.toLEB(0) +
1362
1363      # tempDoublePtr, always 0 in wasm backend
1364      WebAssembly.toLEB(0) +
1365
1366      WebAssembly.toLEB(int(Settings.STANDALONE_WASM))
1367
1368      # NB: more data can be appended here as long as you increase
1369      #     the EMSCRIPTEN_METADATA_MINOR
1370    )
1371
1372    orig = open(wasm_file, 'rb').read()
1373    with open(wasm_file, 'wb') as f:
1374      f.write(orig[0:8]) # copy magic number and version
1375      # write the special section
1376      f.write(b'\0') # user section is code 0
1377      # need to find the size of this section
1378      size = len(name) + len(contents)
1379      f.write(WebAssembly.toLEB(size))
1380      f.write(name)
1381      f.write(contents)
1382      f.write(orig[8:])
1383
1384  @staticmethod
1385  def add_dylink_section(wasm_file, needed_dynlibs):
1386    # a wasm shared library has a special "dylink" section, see tools-conventions repo
1387    # TODO: use this in the wasm backend
1388    assert False
1389    mem_align = Settings.MAX_GLOBAL_ALIGN
1390    mem_size = Settings.STATIC_BUMP
1391    table_size = Settings.WASM_TABLE_SIZE
1392    mem_align = int(math.log(mem_align, 2))
1393    logger.debug('creating wasm dynamic library with mem size %d, table size %d, align %d' % (mem_size, table_size, mem_align))
1394
1395    # Write new wasm binary with 'dylink' section
1396    wasm = open(wasm_file, 'rb').read()
1397    section_name = b"\06dylink" # section name, including prefixed size
1398    contents = (WebAssembly.toLEB(mem_size) + WebAssembly.toLEB(mem_align) +
1399                WebAssembly.toLEB(table_size) + WebAssembly.toLEB(0))
1400
1401    # we extend "dylink" section with information about which shared libraries
1402    # our shared library needs. This is similar to DT_NEEDED entries in ELF.
1403    #
1404    # In theory we could avoid doing this, since every import in wasm has
1405    # "module" and "name" attributes, but currently emscripten almost always
1406    # uses just "env" for "module". This way we have to embed information about
1407    # required libraries for the dynamic linker somewhere, and "dylink" section
1408    # seems to be the most relevant place.
1409    #
1410    # Binary format of the extension:
1411    #
1412    #   needed_dynlibs_count        varuint32       ; number of needed shared libraries
1413    #   needed_dynlibs_entries      dynlib_entry*   ; repeated dynamic library entries as described below
1414    #
1415    # dynlib_entry:
1416    #
1417    #   dynlib_name_len             varuint32       ; length of dynlib_name_str in bytes
1418    #   dynlib_name_str             bytes           ; name of a needed dynamic library: valid UTF-8 byte sequence
1419    #
1420    # a proposal has been filed to include the extension into "dylink" specification:
1421    # https://github.com/WebAssembly/tool-conventions/pull/77
1422    contents += WebAssembly.toLEB(len(needed_dynlibs))
1423    for dyn_needed in needed_dynlibs:
1424      dyn_needed = bytes(asbytes(dyn_needed))
1425      contents += WebAssembly.toLEB(len(dyn_needed))
1426      contents += dyn_needed
1427
1428    section_size = len(section_name) + len(contents)
1429    with open(wasm_file, 'wb') as f:
1430      # copy magic number and version
1431      f.write(wasm[0:8])
1432      # write the special section
1433      f.write(b'\0') # user section is code 0
1434      f.write(WebAssembly.toLEB(section_size))
1435      f.write(section_name)
1436      f.write(contents)
1437      # copy rest of binary
1438      f.write(wasm[8:])
1439
1440
1441# Python 2-3 compatibility helper function:
1442# Converts a string to the native str type.
1443def asstr(s):
1444  if str is bytes:
1445    if isinstance(s, type(u'')):
1446      return s.encode('utf-8')
1447  elif isinstance(s, bytes):
1448    return s.decode('utf-8')
1449  return s
1450
1451
1452def asbytes(s):
1453  if isinstance(s, bytes):
1454    # Do not attempt to encode bytes
1455    return s
1456  return s.encode('utf-8')
1457
1458
1459def suffix(name):
1460  """Return the file extension"""
1461  return os.path.splitext(name)[1]
1462
1463
1464def unsuffixed(name):
1465  """Return the filename without the extention.
1466
1467  If there are multiple extensions this strips only the final one.
1468  """
1469  return os.path.splitext(name)[0]
1470
1471
1472def unsuffixed_basename(name):
1473  return os.path.basename(unsuffixed(name))
1474
1475
1476def safe_move(src, dst):
1477  src = os.path.abspath(src)
1478  dst = os.path.abspath(dst)
1479  if os.path.isdir(dst):
1480    dst = os.path.join(dst, os.path.basename(src))
1481  if src == dst:
1482    return
1483  if dst == os.devnull:
1484    return
1485  logging.debug('move: %s -> %s', src, dst)
1486  shutil.move(src, dst)
1487
1488
1489def safe_copy(src, dst):
1490  src = os.path.abspath(src)
1491  dst = os.path.abspath(dst)
1492  if os.path.isdir(dst):
1493    dst = os.path.join(dst, os.path.basename(src))
1494  if src == dst:
1495    return
1496  if dst == os.devnull:
1497    return
1498  shutil.copyfile(src, dst)
1499
1500
1501def read_and_preprocess(filename, expand_macros=False):
1502  temp_dir = get_emscripten_temp_dir()
1503  # Create a settings file with the current settings to pass to the JS preprocessor
1504  # Note: Settings.serialize returns an array of -s options i.e. ['-s', '<setting1>', '-s', '<setting2>', ...]
1505  #       we only want the actual settings, hence the [1::2] slice operation.
1506  settings_str = "var " + ";\nvar ".join(Settings.serialize()[1::2])
1507  settings_file = os.path.join(temp_dir, 'settings.js')
1508  with open(settings_file, 'w') as f:
1509    f.write(settings_str)
1510
1511  # Run the JS preprocessor
1512  # N.B. We can't use the default stdout=PIPE here as it only allows 64K of output before it hangs
1513  # and shell.html is bigger than that!
1514  # See https://thraxil.org/users/anders/posts/2008/03/13/Subprocess-Hanging-PIPE-is-your-enemy/
1515  dirname, filename = os.path.split(filename)
1516  if not dirname:
1517    dirname = None
1518  stdout = os.path.join(temp_dir, 'stdout')
1519  args = [settings_file, filename]
1520  if expand_macros:
1521    args += ['--expandMacros']
1522
1523  run_js_tool(path_from_root('tools/preprocessor.js'), args, True, stdout=open(stdout, 'w'), cwd=dirname)
1524  out = open(stdout, 'r').read()
1525
1526  return out
1527
1528
1529# ============================================================================
1530# End declarations.
1531# ============================================================================
1532
1533# Everything below this point is top level code that get run when importing this
1534# file.  TODO(sbc): We should try to reduce that amount we do here and instead
1535# have consumers explicitly call initialization functions.
1536
1537# Emscripten configuration is done through the --em-config command line option
1538# or the EM_CONFIG environment variable. If the specified string value contains
1539# newline or semicolon-separated definitions, then these definitions will be
1540# used to configure Emscripten.  Otherwise, the string is understood to be a
1541# path to a settings file that contains the required definitions.
1542# The search order from the config file is as follows:
1543# 1. Specified on the command line (--em-config)
1544# 2. Specified via EM_CONFIG environment variable
1545# 3. Local .emscripten file, if found
1546# 4. Local .emscripten file, as used by `emsdk --embedded` (two levels above,
1547#    see below)
1548# 5. User home directory config (~/.emscripten), if found.
1549
1550embedded_config = path_from_root('.emscripten')
1551# For compatibility with `emsdk --embedded` mode also look two levels up.  The
1552# layout of the emsdk puts emcc two levels below emsdk.  For exmaple:
1553#  - emsdk/upstream/emscripten/emcc
1554#  - emsdk/emscipten/1.38.31/emcc
1555# However `emsdk --embedded` stores the config file in the emsdk root.
1556# Without this check, when emcc is run from within the emsdk in embedded mode
1557# and the user forgets to first run `emsdk_env.sh` (which sets EM_CONFIG) emcc
1558# will not see any config file at all and fall back to creating a new/emtpy
1559# one.
1560# We could remove this special case if emsdk were to write its embedded config
1561# file into the emscripten directory itself.
1562# See: https://github.com/emscripten-core/emsdk/pull/367
1563emsdk_root = os.path.dirname(os.path.dirname(__rootpath__))
1564emsdk_embedded_config = os.path.join(emsdk_root, '.emscripten')
1565user_home_config = os.path.expanduser('~/.emscripten')
1566
1567EMSCRIPTEN_ROOT = __rootpath__
1568
1569if '--em-config' in sys.argv:
1570  EM_CONFIG = sys.argv[sys.argv.index('--em-config') + 1]
1571  # And now remove it from sys.argv
1572  skip = False
1573  newargs = []
1574  for arg in sys.argv:
1575    if not skip and arg != '--em-config':
1576      newargs += [arg]
1577    elif arg == '--em-config':
1578      skip = True
1579    elif skip:
1580      skip = False
1581  sys.argv = newargs
1582  if not os.path.isfile(EM_CONFIG):
1583    if EM_CONFIG.startswith('-'):
1584      exit_with_error('Passed --em-config without an argument. Usage: --em-config /path/to/.emscripten or --em-config LLVM_ROOT=/path;...')
1585    if '=' not in EM_CONFIG:
1586      exit_with_error('File ' + EM_CONFIG + ' passed to --em-config does not exist!')
1587    else:
1588      EM_CONFIG = EM_CONFIG.replace(';', '\n') + '\n'
1589elif 'EM_CONFIG' in os.environ:
1590  EM_CONFIG = os.environ['EM_CONFIG']
1591elif os.path.exists(embedded_config):
1592  EM_CONFIG = embedded_config
1593elif os.path.exists(emsdk_embedded_config):
1594  EM_CONFIG = emsdk_embedded_config
1595elif os.path.exists(user_home_config):
1596  EM_CONFIG = user_home_config
1597else:
1598  if root_is_writable():
1599    generate_config(embedded_config, first_time=True)
1600  else:
1601    generate_config(user_home_config, first_time=True)
1602  sys.exit(0)
1603
1604PYTHON = sys.executable
1605
1606# The following globals can be overridden by the config file.
1607# See parse_config_file below.
1608NODE_JS = None
1609BINARYEN_ROOT = '/usr/local'
1610EM_POPEN_WORKAROUND = None
1611SPIDERMONKEY_ENGINE = None
1612V8_ENGINE = None
1613LLVM_ROOT = '/usr/local'
1614LLVM_ADD_VERSION = 'devel'
1615CLANG_ADD_VERSION = 'devel'
1616CLOSURE_COMPILER = None
1617JAVA = None
1618JS_ENGINE = None
1619JS_ENGINES = []
1620WASMER = None
1621WASMTIME = None
1622WASM_ENGINES = []
1623CACHE = None
1624PORTS = None
1625FROZEN_CACHE = False
1626
1627# Emscripten compiler spawns other processes, which can reimport shared.py, so
1628# make sure that those child processes get the same configuration file by
1629# setting it to the currently active environment.
1630os.environ['EM_CONFIG'] = EM_CONFIG
1631
1632if '\n' in EM_CONFIG:
1633  CONFIG_FILE = None
1634  logger.debug('EM_CONFIG is specified inline without a file')
1635else:
1636  CONFIG_FILE = os.path.expanduser(EM_CONFIG)
1637  logger.debug('EM_CONFIG is located in ' + CONFIG_FILE)
1638  if not os.path.exists(CONFIG_FILE):
1639    exit_with_error('emscripten config file not found: ' + CONFIG_FILE)
1640
1641parse_config_file()
1642normalize_config_settings()
1643
1644# Install our replacement Popen handler if we are running on Windows to avoid
1645# python spawn process function.
1646# nb. This is by default disabled since it has the adverse effect of buffering
1647# up all logging messages, which makes builds look unresponsive (messages are
1648# printed only after the whole build finishes). Whether this workaround is
1649# needed seems to depend on how the host application that invokes emcc has set
1650# up its stdout and stderr.
1651if EM_POPEN_WORKAROUND and os.name == 'nt':
1652  logger.debug('Installing Popen workaround handler to avoid bug http://bugs.python.org/issue3905')
1653  Popen = WindowsPopen
1654else:
1655  Popen = subprocess.Popen
1656
1657# Verbosity level control for any intermediate subprocess spawns from the compiler. Useful for internal debugging.
1658# 0: disabled.
1659# 1: Log stderr of subprocess spawns.
1660# 2: Log stdout and stderr of subprocess spawns. Print out subprocess commands that were executed.
1661# 3: Log stdout and stderr, and pass VERBOSE=1 to CMake configure steps.
1662EM_BUILD_VERBOSE = int(os.getenv('EM_BUILD_VERBOSE', '0'))
1663TRACK_PROCESS_SPAWNS = EM_BUILD_VERBOSE >= 3
1664
1665set_version_globals()
1666
1667# For the Emscripten-specific WASM metadata section, follows semver, changes
1668# whenever metadata section changes structure.
1669# NB: major version 0 implies no compatibility
1670# NB: when changing the metadata format, we should only append new fields, not
1671#     reorder, modify, or remove existing ones.
1672EMSCRIPTEN_METADATA_MAJOR, EMSCRIPTEN_METADATA_MINOR = (0, 3)
1673# For the JS/WASM ABI, specifies the minimum ABI version required of
1674# the WASM runtime implementation by the generated WASM binary. It follows
1675# semver and changes whenever C types change size/signedness or
1676# syscalls change signature. By semver, the maximum ABI version is
1677# implied to be less than (EMSCRIPTEN_ABI_MAJOR + 1, 0). On an ABI
1678# change, increment EMSCRIPTEN_ABI_MINOR if EMSCRIPTEN_ABI_MAJOR == 0
1679# or the ABI change is backwards compatible, otherwise increment
1680# EMSCRIPTEN_ABI_MAJOR and set EMSCRIPTEN_ABI_MINOR = 0.
1681EMSCRIPTEN_ABI_MAJOR, EMSCRIPTEN_ABI_MINOR = (0, 27)
1682
1683# Tools/paths
1684if LLVM_ADD_VERSION is None:
1685  LLVM_ADD_VERSION = os.getenv('LLVM_ADD_VERSION')
1686
1687if CLANG_ADD_VERSION is None:
1688  CLANG_ADD_VERSION = os.getenv('CLANG_ADD_VERSION')
1689
1690CLANG_CC = os.path.expanduser(build_clang_tool_path(exe_suffix('clang')))
1691CLANG_CXX = os.path.expanduser(build_clang_tool_path(exe_suffix('clang++')))
1692LLVM_LINK = build_llvm_tool_path(exe_suffix('llvm-link'))
1693LLVM_AR = build_llvm_tool_path(exe_suffix('llvm-ar'))
1694LLVM_RANLIB = build_llvm_tool_path(exe_suffix('llvm-ranlib'))
1695LLVM_OPT = os.path.expanduser(build_llvm_tool_path(exe_suffix('opt')))
1696LLVM_AS = os.path.expanduser(build_llvm_tool_path(exe_suffix('llvm-as')))
1697LLVM_DIS = os.path.expanduser(build_llvm_tool_path(exe_suffix('llvm-dis')))
1698LLVM_NM = os.path.expanduser(build_llvm_tool_path(exe_suffix('llvm-nm')))
1699LLVM_INTERPRETER = os.path.expanduser(build_llvm_tool_path(exe_suffix('lli')))
1700LLVM_COMPILER = os.path.expanduser(build_llvm_tool_path(exe_suffix('llc')))
1701LLVM_DWARFDUMP = os.path.expanduser(build_llvm_tool_path(exe_suffix('llvm-dwarfdump')))
1702LLVM_OBJCOPY = os.path.expanduser(build_llvm_tool_path(exe_suffix('llvm-objcopy')))
1703WASM_LD = os.path.expanduser(build_llvm_tool_path(exe_suffix('wasm-ld')))
1704
1705EMCC = bat_suffix(path_from_root('emcc'))
1706EMXX = bat_suffix(path_from_root('em++'))
1707EMAR = bat_suffix(path_from_root('emar'))
1708EMRANLIB = bat_suffix(path_from_root('emranlib'))
1709FILE_PACKAGER = path_from_root('tools', 'file_packager.py')
1710
1711apply_configuration()
1712
1713# Target choice.
1714LLVM_TARGET = 'wasm32-unknown-emscripten'
1715
1716Settings = SettingsManager()
1717verify_settings()
1718Cache = cache.Cache(CACHE)
1719
1720PRINT_STAGES = int(os.getenv('EMCC_VERBOSE', '0'))
1721
1722# compatibility with existing emcc, etc. scripts
1723chunkify = cache.chunkify
1724