~ubuntu-branches/ubuntu/vivid/emscripten/vivid

« back to all changes in this revision

Viewing changes to tools/shared.py

  • Committer: Package Import Robot
  • Author(s): Sylvestre Ledru
  • Date: 2013-09-20 22:44:35 UTC
  • mfrom: (4.1.1 sid)
  • Revision ID: package-import@ubuntu.com-20130920224435-apuwj4fsl3fqv1a6
Tags: 1.5.6~20130920~6010666-1
* New snapshot release
* Update the list of supported architectures to the same as libv8
  (Closes: #723129)
* emlibtool has been removed from upstream.
* Fix warning syntax-error-in-dep5-copyright
* Refresh of the patches

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
import shutil, time, os, sys, json, tempfile, copy, shlex, atexit, subprocess, hashlib, cPickle, re
2
2
from subprocess import Popen, PIPE, STDOUT
3
3
from tempfile import mkstemp
 
4
from distutils.spawn import find_executable
4
5
import jsrun, cache, tempfiles
5
6
from response_file import create_response_file
6
7
import logging, platform
199
200
else:
200
201
  CONFIG_FILE = os.path.expanduser(EM_CONFIG)
201
202
  if not os.path.exists(CONFIG_FILE):
 
203
    # Note: repr is used to ensure the paths are escaped correctly on Windows.
 
204
    # The full string is replaced so that the template stays valid Python.
202
205
    config_file = open(path_from_root('tools', 'settings_template_readonly.py')).read().split('\n')
203
206
    config_file = config_file[1:] # remove "this file will be copied..."
204
207
    config_file = '\n'.join(config_file)
205
208
    # autodetect some default paths
206
 
    config_file = config_file.replace('{{{ EMSCRIPTEN_ROOT }}}', __rootpath__)
207
 
    llvm_root = '/usr/bin'
208
 
    try:
209
 
      llvm_root = os.path.dirname(Popen(['which', 'llvm-dis'], stdout=PIPE).communicate()[0].replace('\n', ''))
210
 
    except:
211
 
      pass
212
 
    config_file = config_file.replace('{{{ LLVM_ROOT }}}', llvm_root)
213
 
    node = 'node'
214
 
    try:
215
 
      node = Popen(['which', 'node'], stdout=PIPE).communicate()[0].replace('\n', '') or \
216
 
             Popen(['which', 'nodejs'], stdout=PIPE).communicate()[0].replace('\n', '') or node
217
 
    except:
218
 
      pass
219
 
    config_file = config_file.replace('{{{ NODE }}}', node)
220
 
    python = sys.executable or 'python'
221
 
    try:
222
 
      python = Popen(['which', 'python2'], stdout=PIPE).communicate()[0].replace('\n', '') or \
223
 
               Popen(['which', 'python'], stdout=PIPE).communicate()[0].replace('\n', '') or python
224
 
    except:
225
 
      pass
226
 
    config_file = config_file.replace('{{{ PYTHON }}}', python)    
 
209
    config_file = config_file.replace('\'{{{ EMSCRIPTEN_ROOT }}}\'', repr(__rootpath__))
 
210
    llvm_root = os.path.dirname(find_executable('llvm-dis') or '/usr/bin/llvm-dis')
 
211
    config_file = config_file.replace('\'{{{ LLVM_ROOT }}}\'', repr(llvm_root))
 
212
    node = find_executable('node') or find_executable('nodejs') or 'node'
 
213
    config_file = config_file.replace('\'{{{ NODE }}}\'', repr(node))
 
214
    python = find_executable('python2') or find_executable('python') or \
 
215
        sys.executable or 'python'
 
216
    config_file = config_file.replace('\'{{{ PYTHON }}}\'', repr(python))
 
217
    if WINDOWS:
 
218
      tempdir = os.environ.get('TEMP') or os.environ.get('TMP') or 'c:\\temp'
 
219
    else:
 
220
      tempdir = '/tmp'
 
221
    config_file = config_file.replace('\'{{{ TEMP }}}\'', repr(tempdir))
227
222
 
228
223
    # write
229
224
    open(CONFIG_FILE, 'w').write(config_file)
269
264
 
270
265
def check_llvm_version():
271
266
  try:
272
 
    check_clang_version();
 
267
    check_clang_version()
273
268
  except Exception, e:
274
269
    logging.warning('Could not verify LLVM version: %s' % str(e))
275
270
 
276
 
EXPECTED_NODE_VERSION = (0,6,8)
 
271
EXPECTED_NODE_VERSION = (0,8,0)
277
272
 
278
273
def check_node_version():
279
274
  try:
288
283
    logging.warning('cannot check node version: %s',  e)
289
284
    return False
290
285
 
 
286
# Finds the system temp directory without resorting to using the one configured in .emscripten
 
287
def find_temp_directory():
 
288
  if WINDOWS:
 
289
    if os.getenv('TEMP') and os.path.isdir(os.getenv('TEMP')):
 
290
      return os.getenv('TEMP')
 
291
    elif os.getenv('TMP') and os.path.isdir(os.getenv('TMP')):
 
292
      return os.getenv('TMP')
 
293
    elif os.path.isdir('C:\\temp'):
 
294
      return os.getenv('C:\\temp')
 
295
    else:
 
296
      return None # No luck!
 
297
  else:
 
298
    return '/tmp'
 
299
 
291
300
# Check that basic stuff we need (a JS engine to compile, Node.js, and Clang and LLVM)
292
301
# exists.
293
302
# The test runner always does this check (through |force|). emcc does this less frequently,
295
304
# we re-check sanity when the settings are changed)
296
305
# We also re-check sanity and clear the cache when the version changes
297
306
 
298
 
EMSCRIPTEN_VERSION = '1.4.9'
 
307
EMSCRIPTEN_VERSION = '1.5.6'
299
308
 
300
309
def generate_sanity():
301
 
  return EMSCRIPTEN_VERSION + '|' + get_llvm_target()
 
310
  return EMSCRIPTEN_VERSION + '|' + get_llvm_target() + '|' + LLVM_ROOT
302
311
 
303
312
def check_sanity(force=False):
304
313
  try:
324
333
    if reason:
325
334
      logging.warning('(Emscripten: %s, clearing cache)' % reason)
326
335
      Cache.erase()
 
336
      force = False # the check actually failed, so definitely write out the sanity file, to avoid others later seeing failures too
327
337
 
328
338
    # some warning, not fatal checks - do them even if EM_IGNORE_SANITY is on
329
339
    check_llvm_version()
344
354
        logging.critical('Node.js (%s) does not seem to work, check the paths in %s' % (NODE_JS, EM_CONFIG))
345
355
        sys.exit(1)
346
356
 
347
 
    for cmd in [CLANG, LLVM_LINK, LLVM_AR, LLVM_OPT, LLVM_AS, LLVM_DIS, LLVM_NM]:
 
357
    for cmd in [CLANG, LINK_CMD[0], LLVM_AR, LLVM_OPT, LLVM_AS, LLVM_DIS, LLVM_NM]:
348
358
      if not os.path.exists(cmd) and not os.path.exists(cmd + '.exe'): # .exe extension required for Windows
349
359
        logging.critical('Cannot find %s, check the paths in %s' % (cmd, EM_CONFIG))
350
360
        sys.exit(1)
371
381
 
372
382
# Tools/paths
373
383
 
374
 
LLVM_ADD_VERSION = os.getenv('LLVM_ADD_VERSION')
375
 
CLANG_ADD_VERSION = os.getenv('CLANG_ADD_VERSION')
 
384
try:
 
385
        LLVM_ADD_VERSION
 
386
except NameError:
 
387
        LLVM_ADD_VERSION = os.getenv('LLVM_ADD_VERSION')
 
388
 
 
389
try:
 
390
        CLANG_ADD_VERSION
 
391
except NameError:
 
392
        CLANG_ADD_VERSION = os.getenv('CLANG_ADD_VERSION')
 
393
 
 
394
USING_PNACL_TOOLCHAIN = os.path.exists(os.path.join(LLVM_ROOT, 'pnacl-clang'))
 
395
 
 
396
def modify_prefix(tool):
 
397
  if USING_PNACL_TOOLCHAIN:
 
398
    if tool.startswith('llvm-'):
 
399
      tool = tool[5:]
 
400
    tool = 'pnacl-' + tool
 
401
    if WINDOWS:
 
402
      tool += '.bat'
 
403
  return tool
376
404
 
377
405
# Some distributions ship with multiple llvm versions so they add
378
406
# the version to the binaries, cope with that
379
407
def build_llvm_tool_path(tool):
 
408
  tool = modify_prefix(tool)
380
409
  if LLVM_ADD_VERSION:
381
410
    return os.path.join(LLVM_ROOT, tool + "-" + LLVM_ADD_VERSION)
382
411
  else:
385
414
# Some distributions ship with multiple clang versions so they add
386
415
# the version to the binaries, cope with that
387
416
def build_clang_tool_path(tool):
 
417
  tool = modify_prefix(tool)
388
418
  if CLANG_ADD_VERSION:
389
419
    return os.path.join(LLVM_ROOT, tool + "-" + CLANG_ADD_VERSION)
390
420
  else:
393
423
CLANG_CC=os.path.expanduser(build_clang_tool_path('clang'))
394
424
CLANG_CPP=os.path.expanduser(build_clang_tool_path('clang++'))
395
425
CLANG=CLANG_CPP
396
 
LLVM_LINK=build_llvm_tool_path('llvm-link')
 
426
if USING_PNACL_TOOLCHAIN:
 
427
  # The PNaCl toolchain doesn't have llvm-link, but we can fake it
 
428
  LINK_CMD = [build_llvm_tool_path('llvm-ld'), '-nostdlib', '-r']
 
429
else:
 
430
  LINK_CMD = [build_llvm_tool_path('llvm-link')]
397
431
LLVM_AR=build_llvm_tool_path('llvm-ar')
398
432
LLVM_OPT=os.path.expanduser(build_llvm_tool_path('opt'))
399
433
LLVM_AS=os.path.expanduser(build_llvm_tool_path('llvm-as'))
409
443
EMXX = path_from_root('em++')
410
444
EMAR = path_from_root('emar')
411
445
EMRANLIB = path_from_root('emranlib')
412
 
EMLIBTOOL = path_from_root('emlibtool')
413
446
EMCONFIG = path_from_root('em-config')
 
447
EMLINK = path_from_root('emlink.py')
414
448
EMMAKEN = path_from_root('tools', 'emmaken.py')
415
449
AUTODEBUGGER = path_from_root('tools', 'autodebugger.py')
416
450
BINDINGS_GENERATOR = path_from_root('tools', 'bindings_generator.py')
420
454
# Temp dir. Create a random one, unless EMCC_DEBUG is set, in which case use TEMP_DIR/emscripten_temp
421
455
 
422
456
class Configuration:
423
 
  def __init__(self, environ):
 
457
  def __init__(self, environ=os.environ):
424
458
    self.DEBUG = environ.get('EMCC_DEBUG')
425
459
    if self.DEBUG == "0":
426
460
      self.DEBUG = None
430
464
    try:
431
465
      self.TEMP_DIR = TEMP_DIR
432
466
    except NameError:
433
 
      logging.debug('TEMP_DIR not defined in ~/.emscripten, using /tmp')
434
 
      self.TEMP_DIR = '/tmp'
 
467
      self.TEMP_DIR = find_temp_directory()
 
468
      if self.TEMP_DIR == None:
 
469
        logging.critical('TEMP_DIR not defined in ' + os.path.expanduser('~\\.emscripten') + ", and could not detect a suitable directory! Please configure .emscripten to contain a variable TEMP_DIR='/path/to/temp/dir'.")
 
470
      logging.debug('TEMP_DIR not defined in ~/.emscripten, using ' + self.TEMP_DIR)
 
471
 
 
472
    if not os.path.isdir(self.TEMP_DIR):
 
473
      logging.critical("The temp directory TEMP_DIR='" + self.TEMP_DIR + "' doesn't seem to exist! Please make sure that the path is correct.")
435
474
 
436
475
    self.CANONICAL_TEMP_DIR = os.path.join(self.TEMP_DIR, 'emscripten_temp')
437
476
 
448
487
      tmp=self.TEMP_DIR if not self.DEBUG else self.EMSCRIPTEN_TEMP_DIR,
449
488
      save_debug_files=os.environ.get('EMCC_DEBUG_SAVE'))
450
489
 
451
 
configuration = Configuration(environ=os.environ)
452
 
DEBUG = configuration.DEBUG
453
 
EMSCRIPTEN_TEMP_DIR = configuration.EMSCRIPTEN_TEMP_DIR
454
 
DEBUG_CACHE = configuration.DEBUG_CACHE
455
 
CANONICAL_TEMP_DIR = configuration.CANONICAL_TEMP_DIR
456
 
 
457
 
level = logging.DEBUG if os.environ.get('EMCC_DEBUG') else logging.INFO
458
 
logging.basicConfig(level=level, format='%(levelname)-8s %(name)s: %(message)s')
459
 
  
 
490
def apply_configuration():
 
491
  global configuration, DEBUG, EMSCRIPTEN_TEMP_DIR, DEBUG_CACHE, CANONICAL_TEMP_DIR, TEMP_DIR
 
492
  configuration = Configuration()
 
493
  DEBUG = configuration.DEBUG
 
494
  EMSCRIPTEN_TEMP_DIR = configuration.EMSCRIPTEN_TEMP_DIR
 
495
  DEBUG_CACHE = configuration.DEBUG_CACHE
 
496
  CANONICAL_TEMP_DIR = configuration.CANONICAL_TEMP_DIR
 
497
  TEMP_DIR = configuration.TEMP_DIR
 
498
apply_configuration()
 
499
 
 
500
logging.basicConfig(format='%(levelname)-8s %(name)s: %(message)s')
 
501
def set_logging():
 
502
  logger = logging.getLogger()
 
503
  logger.setLevel(logging.DEBUG if os.environ.get('EMCC_DEBUG') else logging.INFO)
 
504
set_logging()
 
505
 
460
506
if not EMSCRIPTEN_TEMP_DIR:
461
507
  EMSCRIPTEN_TEMP_DIR = tempfile.mkdtemp(prefix='emscripten_temp_', dir=configuration.TEMP_DIR)
462
 
  def clean_temp():
463
 
    try_delete(EMSCRIPTEN_TEMP_DIR)
464
 
  atexit.register(clean_temp)
 
508
  def prepare_to_clean_temp(d):
 
509
    def clean_temp():
 
510
      try_delete(d)
 
511
    atexit.register(clean_temp)
 
512
  prepare_to_clean_temp(EMSCRIPTEN_TEMP_DIR) # this global var might change later
465
513
 
466
514
# EM_CONFIG stuff
467
515
 
498
546
  return os.environ.get('EMCC_LLVM_TARGET') or 'le32-unknown-nacl' # 'i386-pc-linux-gnu'
499
547
LLVM_TARGET = get_llvm_target()
500
548
 
 
549
# COMPILER_OPTS: options passed to clang when generating bitcode for us
501
550
try:
502
551
  COMPILER_OPTS # Can be set in EM_CONFIG, optionally
503
552
except:
504
553
  COMPILER_OPTS = []
505
 
# Force a simple, standard target as much as possible: target 32-bit linux, and disable various flags that hint at other platforms
506
 
COMPILER_OPTS = COMPILER_OPTS + ['-m32', '-U__i386__', '-U__i386', '-Ui386',
507
 
                                 '-U__SSE__', '-U__SSE_MATH__', '-U__SSE2__', '-U__SSE2_MATH__', '-U__MMX__',
508
 
                                 '-DEMSCRIPTEN', '-D__EMSCRIPTEN__', '-U__STRICT_ANSI__',
509
 
                                 '-D__IEEE_LITTLE_ENDIAN', '-fno-math-errno', '-fno-threadsafe-statics',
 
554
COMPILER_OPTS = COMPILER_OPTS + ['-m32', '-DEMSCRIPTEN', '-D__EMSCRIPTEN__',
 
555
                                 '-fno-math-errno',
 
556
                                 #'-fno-threadsafe-statics', # disabled due to issue 1289
510
557
                                 '-target', LLVM_TARGET]
511
558
 
512
559
if LLVM_TARGET == 'le32-unknown-nacl':
 
560
  COMPILER_OPTS = filter(lambda opt: opt != '-m32', COMPILER_OPTS) # le32 target is 32-bit anyhow, no need for -m32
513
561
  COMPILER_OPTS += ['-U__native_client__', '-U__pnacl__', '-U__ELF__'] # The nacl target is originally used for Google Native Client. Emscripten is not NaCl, so remove the platform #define, when using their triple.
514
562
 
 
563
# Remove various platform specific defines, and set little endian
 
564
COMPILER_STANDARDIZATION_OPTS = ['-U__i386__', '-U__i386', '-Ui386', '-U__STRICT_ANSI__', '-D__IEEE_LITTLE_ENDIAN',
 
565
                                 '-U__SSE__', '-U__SSE_MATH__', '-U__SSE2__', '-U__SSE2_MATH__', '-U__MMX__',
 
566
                                 '-U__APPLE__', '-U__linux__']
 
567
 
515
568
USE_EMSDK = not os.environ.get('EMMAKEN_NO_SDK')
516
569
 
517
570
if USE_EMSDK:
518
571
  # Disable system C and C++ include directories, and add our own (using -idirafter so they are last, like system dirs, which
519
572
  # allows projects to override them)
520
 
  EMSDK_OPTS = ['-nostdinc', '-nostdinc++', '-Xclang', '-nobuiltininc', '-Xclang', '-nostdsysteminc',
 
573
  EMSDK_OPTS = ['-nostdinc', '-Xclang', '-nobuiltininc', '-Xclang', '-nostdsysteminc',
521
574
    '-Xclang', '-isystem' + path_from_root('system', 'local', 'include'),
 
575
    '-Xclang', '-isystem' + path_from_root('system', 'include', 'compat'),
522
576
    '-Xclang', '-isystem' + path_from_root('system', 'include', 'libcxx'),
523
577
    '-Xclang', '-isystem' + path_from_root('system', 'include'),
524
578
    '-Xclang', '-isystem' + path_from_root('system', 'include', 'emscripten'),
527
581
    '-Xclang', '-isystem' + path_from_root('system', 'include', 'gfx'),
528
582
    '-Xclang', '-isystem' + path_from_root('system', 'include', 'net'),
529
583
    '-Xclang', '-isystem' + path_from_root('system', 'include', 'SDL'),
530
 
  ] + [
531
 
    '-U__APPLE__', '-U__linux__'
532
584
  ]
 
585
  EMSDK_OPTS += COMPILER_STANDARDIZATION_OPTS
 
586
  if LLVM_TARGET != 'le32-unknown-nacl':
 
587
    EMSDK_CXX_OPTS = ['-nostdinc++'] # le32 target does not need -nostdinc++
 
588
  else:
 
589
    EMSDK_CXX_OPTS = []
533
590
  COMPILER_OPTS += EMSDK_OPTS
534
591
else:
535
592
  EMSDK_OPTS = []
 
593
  EMSDK_CXX_OPTS = []
 
594
  COMPILER_OPTS += COMPILER_STANDARDIZATION_OPTS
536
595
 
537
596
#print >> sys.stderr, 'SDK opts', ' '.join(EMSDK_OPTS)
538
597
#print >> sys.stderr, 'Compiler opts', ' '.join(COMPILER_OPTS)
594
653
 
595
654
  return out
596
655
 
597
 
def limit_size(string, MAX=80*20):
 
656
def limit_size(string, MAX=12000*20):
598
657
  if len(string) < MAX: return string
599
658
  return string[0:MAX/2] + '\n[..]\n' + string[-MAX/2:]
600
659
 
638
697
 
639
698
# Settings. A global singleton. Not pretty, but nicer than passing |, settings| everywhere
640
699
 
641
 
class Settings:
642
 
  @classmethod
643
 
  def reset(self):
644
 
    class Settings2:
645
 
      QUANTUM_SIZE = 4
646
 
      reset = Settings.reset
647
 
 
648
 
      # Given some emcc-type args (-O3, -s X=Y, etc.), fill Settings with the right settings
649
 
      @classmethod
650
 
      def load(self, args=[]):
651
 
        # Load the JS defaults into python
652
 
        settings = open(path_from_root('src', 'settings.js')).read().replace('var ', 'Settings.').replace('//', '#')
653
 
        exec settings in globals()
654
 
 
655
 
        # Apply additional settings. First -O, then -s
656
 
        for i in range(len(args)):
657
 
          if args[i].startswith('-O'):
658
 
            level = eval(args[i][2])
659
 
            Settings.apply_opt_level(level)
660
 
        for i in range(len(args)):
661
 
          if args[i] == '-s':
662
 
            exec 'Settings.' + args[i+1] in globals() # execute the setting
663
 
 
664
 
      # Transforms the Settings information into emcc-compatible args (-s X=Y, etc.). Basically
665
 
      # the reverse of load_settings, except for -Ox which is relevant there but not here
666
 
      @classmethod
667
 
      def serialize(self):
668
 
        ret = []
669
 
        for key, value in Settings.__dict__.iteritems():
670
 
          if key == key.upper(): # this is a hack. all of our settings are ALL_CAPS, python internals are not
671
 
            jsoned = json.dumps(value, sort_keys=True)
672
 
            ret += ['-s', key + '=' + jsoned]
673
 
        return ret
674
 
 
675
 
      @classmethod
676
 
      def apply_opt_level(self, opt_level, noisy=False):
677
 
        if opt_level >= 1:
678
 
          Settings.ASM_JS = 1
679
 
          Settings.ASSERTIONS = 0
680
 
          Settings.DISABLE_EXCEPTION_CATCHING = 1
681
 
          Settings.EMIT_GENERATED_FUNCTIONS = 1
682
 
        if opt_level >= 2:
683
 
          Settings.RELOOP = 1
684
 
          Settings.ALIASING_FUNCTION_POINTERS = 1
685
 
        if opt_level >= 3:
686
 
          # Aside from these, -O3 also runs closure compiler and llvm lto
687
 
          Settings.FORCE_ALIGNED_MEMORY = 1
688
 
          Settings.DOUBLE_MODE = 0
689
 
          Settings.PRECISE_I64_MATH = 0
690
 
          if noisy: logging.warning('Applying some potentially unsafe optimizations! (Use -O2 if this fails.)')
691
 
 
692
 
    global Settings
693
 
    Settings = Settings2
694
 
    Settings.load() # load defaults
695
 
 
696
 
Settings.reset()
 
700
class Settings2(type):
 
701
  class __impl:
 
702
    attrs = {}
 
703
 
 
704
    def __init__(self):
 
705
      self.reset()
 
706
 
 
707
    @classmethod
 
708
    def reset(self):
 
709
      self.attrs = { 'QUANTUM_SIZE': 4 }
 
710
      self.load()
 
711
 
 
712
    # Given some emcc-type args (-O3, -s X=Y, etc.), fill Settings with the right settings
 
713
    @classmethod
 
714
    def load(self, args=[]):
 
715
      # Load the JS defaults into python
 
716
      settings = open(path_from_root('src', 'settings.js')).read().replace('//', '#')
 
717
      settings = re.sub(r'var ([\w\d]+)', r'self.attrs["\1"]', settings)
 
718
      exec settings
 
719
 
 
720
      # Apply additional settings. First -O, then -s
 
721
      for i in range(len(args)):
 
722
        if args[i].startswith('-O'):
 
723
          level = eval(args[i][2])
 
724
          self.apply_opt_level(level)
 
725
      for i in range(len(args)):
 
726
        if args[i] == '-s':
 
727
          declare = re.sub(r'([\w\d]+)\s*=\s*(.+)', r'self.attrs["\1"]=\2;', args[i+1])
 
728
          exec declare
 
729
 
 
730
    # Transforms the Settings information into emcc-compatible args (-s X=Y, etc.). Basically
 
731
    # the reverse of load_settings, except for -Ox which is relevant there but not here
 
732
    @classmethod
 
733
    def serialize(self):
 
734
      ret = []
 
735
      for key, value in self.attrs.iteritems():
 
736
        if key == key.upper(): # this is a hack. all of our settings are ALL_CAPS, python internals are not
 
737
          jsoned = json.dumps(value, sort_keys=True)
 
738
          ret += ['-s', key + '=' + jsoned]
 
739
      return ret
 
740
 
 
741
    @classmethod
 
742
    def copy(self, values):
 
743
      self.attrs = values
 
744
 
 
745
    @classmethod
 
746
    def apply_opt_level(self, opt_level, noisy=False):
 
747
      if opt_level >= 1:
 
748
        self.attrs['ASM_JS'] = 1
 
749
        self.attrs['ASSERTIONS'] = 0
 
750
        self.attrs['DISABLE_EXCEPTION_CATCHING'] = 1
 
751
        self.attrs['EMIT_GENERATED_FUNCTIONS'] = 1
 
752
      if opt_level >= 2:
 
753
        self.attrs['RELOOP'] = 1
 
754
        self.attrs['ALIASING_FUNCTION_POINTERS'] = 1
 
755
      if opt_level >= 3:
 
756
        # Aside from these, -O3 also runs closure compiler and llvm lto
 
757
        self.attrs['FORCE_ALIGNED_MEMORY'] = 1
 
758
        self.attrs['DOUBLE_MODE'] = 0
 
759
        self.attrs['PRECISE_I64_MATH'] = 0
 
760
        if noisy: logging.warning('Applying some potentially unsafe optimizations! (Use -O2 if this fails.)')
 
761
 
 
762
    def __getattr__(self, attr):
 
763
      if attr in self.attrs:
 
764
        return self.attrs[attr]
 
765
      else:
 
766
        raise AttributeError
 
767
 
 
768
    def __setattr__(self, attr, value):
 
769
      self.attrs[attr] = value
 
770
 
 
771
  __instance = None
 
772
 
 
773
  @staticmethod
 
774
  def instance():
 
775
    if Settings2.__instance is None:
 
776
      Settings2.__instance = Settings2.__impl()
 
777
    return Settings2.__instance
 
778
 
 
779
  def __getattr__(self, attr):
 
780
    return getattr(self.instance(), attr)
 
781
 
 
782
  def __setattr__(self, attr, value):
 
783
    return setattr(self.instance(), attr, value)
 
784
 
 
785
class Settings(object):
 
786
  __metaclass__ = Settings2
697
787
 
698
788
# Building
699
789
 
701
791
  COMPILER = CLANG
702
792
  LLVM_OPTS = False
703
793
  COMPILER_TEST_OPTS = [] # For use of the test runner
 
794
  JS_ENGINE_OVERRIDE = None # Used to pass the JS engine override from runner.py -> test_benchmark.py
704
795
 
705
796
  @staticmethod
706
797
  def get_building_env(native=False):
717
808
    env['LD'] = EMCC if not WINDOWS else 'python %r' % EMCC
718
809
    env['LDSHARED'] = EMCC if not WINDOWS else 'python %r' % EMCC
719
810
    env['RANLIB'] = EMRANLIB if not WINDOWS else 'python %r' % EMRANLIB
720
 
    #env['LIBTOOL'] = EMLIBTOOL if not WINDOWS else 'python %r' % EMLIBTOOL
721
811
    env['EMMAKEN_COMPILER'] = Building.COMPILER
722
812
    env['EMSCRIPTEN_TOOLS'] = path_from_root('tools')
723
813
    env['CFLAGS'] = env['EMMAKEN_CFLAGS'] = ' '.join(Building.COMPILER_TEST_OPTS)
727
817
    env['HOST_CXXFLAGS'] = "-W" #if set to nothing, CXXFLAGS is used, which we don't want
728
818
    env['PKG_CONFIG_LIBDIR'] = path_from_root('system', 'local', 'lib', 'pkgconfig') + os.path.pathsep + path_from_root('system', 'lib', 'pkgconfig')
729
819
    env['PKG_CONFIG_PATH'] = os.environ.get ('EM_PKG_CONFIG_PATH') or ''
730
 
    env['EMSCRIPTEN'] = '1'
 
820
    env['EMSCRIPTEN'] = path_from_root()
731
821
    return env
732
822
 
 
823
  # Finds the given executable 'program' in PATH. Operates like the Unix tool 'which'.
 
824
  @staticmethod
 
825
  def which(program):
 
826
    import os
 
827
    def is_exe(fpath):
 
828
      return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
 
829
 
 
830
    fpath, fname = os.path.split(program)
 
831
    if fpath:
 
832
      if is_exe(program):
 
833
        return program
 
834
    else:
 
835
      for path in os.environ["PATH"].split(os.pathsep):
 
836
        path = path.strip('"')
 
837
        exe_file = os.path.join(path, program)
 
838
        if is_exe(exe_file):
 
839
          return exe_file
 
840
 
 
841
        if WINDOWS and not '.' in fname:
 
842
          if is_exe(exe_file + '.exe'):
 
843
            return exe_file + '.exe'
 
844
          if is_exe(exe_file + '.cmd'):
 
845
            return exe_file + '.cmd'
 
846
          if is_exe(exe_file + '.bat'):
 
847
            return exe_file + '.bat'
 
848
 
 
849
    return None
 
850
 
733
851
  @staticmethod
734
852
  def handle_CMake_toolchain(args, env):
735
 
    CMakeToolchain = ('''# the name of the target operating system
736
 
SET(CMAKE_SYSTEM_NAME Linux)
737
 
 
738
 
# which C and C++ compiler to use
739
 
SET(CMAKE_C_COMPILER   %(winfix)s$EMSCRIPTEN_ROOT/emcc)
740
 
SET(CMAKE_CXX_COMPILER %(winfix)s$EMSCRIPTEN_ROOT/em++)
741
 
SET(CMAKE_AR           %(winfix)s$EMSCRIPTEN_ROOT/emar)
742
 
SET(CMAKE_RANLIB       %(winfix)s$EMSCRIPTEN_ROOT/emranlib)
743
 
SET(CMAKE_C_FLAGS      $CFLAGS)
744
 
SET(CMAKE_CXX_FLAGS    $CXXFLAGS)
745
 
 
746
 
# here is the target environment located
747
 
SET(CMAKE_FIND_ROOT_PATH  $EMSCRIPTEN_ROOT/system/include )
748
 
 
749
 
# adjust the default behaviour of the FIND_XXX() commands:
750
 
# search headers and libraries in the target environment, search
751
 
# programs in the host environment
752
 
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
753
 
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
754
 
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)
755
 
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS else 'python ' }) \
756
 
      .replace('$EMSCRIPTEN_ROOT', path_from_root('').replace('\\', '/')) \
757
 
      .replace('$CFLAGS', env['CFLAGS']) \
758
 
      .replace('$CXXFLAGS', env['CFLAGS'])
759
 
    toolchainFile = mkstemp(suffix='.cmaketoolchain.txt', dir=configuration.TEMP_DIR)[1]
760
 
    open(toolchainFile, 'w').write(CMakeToolchain)
761
 
    args.append('-DCMAKE_TOOLCHAIN_FILE=%s' % os.path.abspath(toolchainFile))
 
853
 
 
854
    def has_substr(array, substr):
 
855
      for arg in array:
 
856
        if substr in arg:
 
857
          return True
 
858
      return False
 
859
 
 
860
    # Append the Emscripten toolchain file if the user didn't specify one.
 
861
    if not has_substr(args, '-DCMAKE_TOOLCHAIN_FILE'):
 
862
      args.append('-DCMAKE_TOOLCHAIN_FILE=' + path_from_root('cmake', 'Platform', 'Emscripten.cmake'))
 
863
 
 
864
    # On Windows specify MinGW Makefiles if we have MinGW and no other toolchain was specified, to avoid CMake
 
865
    # pulling in a native Visual Studio, or Unix Makefiles.
 
866
    if WINDOWS and not '-G' in args and Building.which('mingw32-make'):
 
867
      args += ['-G', 'MinGW Makefiles']
 
868
 
762
869
    return args
763
870
 
764
871
  @staticmethod
778
885
      raise
779
886
    del env['EMMAKEN_JUST_CONFIGURE']
780
887
    if process.returncode is not 0:
 
888
      logging.error('Configure step failed with non-zero return code ' + str(process.returncode) + '! Command line: ' + str(args))
781
889
      raise subprocess.CalledProcessError(cmd=args, returncode=process.returncode)
782
890
 
783
891
  @staticmethod
788
896
      logging.error('Executable to run not specified.')
789
897
      sys.exit(1)
790
898
    #args += ['VERBOSE=1']
 
899
 
 
900
    # On Windows prefer building with mingw32-make instead of make, if it exists.
 
901
    if WINDOWS and args[0] == 'make':
 
902
      mingw32_make = Building.which('mingw32-make')
 
903
      if mingw32_make:
 
904
        args[0] = mingw32_make
 
905
 
791
906
    try:
792
907
      process = Popen(args, stdout=stdout, stderr=stderr, env=env)
793
908
      process.communicate()
828
943
    #  except:
829
944
    #    pass
830
945
    env = Building.get_building_env(native)
 
946
    verbose_level = int(os.getenv('EM_BUILD_VERBOSE')) if os.getenv('EM_BUILD_VERBOSE') != None else 0
831
947
    for k, v in env_init.iteritems():
832
948
      env[k] = v
833
949
    if configure: # Useful in debugging sometimes to comment this out (and the lines below up to and including the |link| call)
834
950
      try:
835
 
        Building.configure(configure + configure_args, stdout=open(os.path.join(project_dir, 'configure_'), 'w'),
836
 
                                                       stderr=open(os.path.join(project_dir, 'configure_err'), 'w'), env=env)
 
951
        Building.configure(configure + configure_args, env=env, stdout=open(os.path.join(project_dir, 'configure_'), 'w') if verbose_level < 2 else None,
 
952
                                                                stderr=open(os.path.join(project_dir, 'configure_err'), 'w') if verbose_level < 1 else None)
837
953
      except subprocess.CalledProcessError, e:
838
954
        pass # Ignore exit code != 0
839
955
    def open_make_out(i, mode='r'):
841
957
    
842
958
    def open_make_err(i, mode='r'):
843
959
      return open(os.path.join(project_dir, 'make_err' + str(i)), mode)
844
 
    
 
960
 
 
961
    if verbose_level >= 3:
 
962
      make_args += ['VERBOSE=1']
 
963
 
845
964
    for i in range(2): # FIXME: Sad workaround for some build systems that need to be run twice to succeed (e.g. poppler)
846
965
      with open_make_out(i, 'w') as make_out:
847
966
        with open_make_err(i, 'w') as make_err:
848
967
          try:
849
 
            Building.make(make + make_args, stdout=make_out,
850
 
                                            stderr=make_err, env=env)
 
968
            Building.make(make + make_args, stdout=make_out if verbose_level < 2 else None,
 
969
                                            stderr=make_err if verbose_level < 1 else None, env=env)
851
970
          except subprocess.CalledProcessError, e:
852
971
            pass # Ignore exit code != 0
853
972
      try:
859
978
        break
860
979
      except Exception, e:
861
980
        if i > 0:
862
 
          # Due to the ugly hack above our best guess is to output the first run
863
 
          with open_make_err(0) as ferr:
864
 
            for line in ferr:
865
 
              sys.stderr.write(line)
 
981
          if verbose_level == 0:
 
982
            # Due to the ugly hack above our best guess is to output the first run
 
983
            with open_make_err(0) as ferr:
 
984
              for line in ferr:
 
985
                sys.stderr.write(line)
866
986
          raise Exception('could not build library ' + name + ' due to exception ' + str(e))
867
987
    if old_dir:
868
988
      os.chdir(old_dir)
869
989
    return generated_libs
870
990
 
871
991
  @staticmethod
872
 
  def link(files, target):
 
992
  def link(files, target, force_archive_contents=False):
873
993
    actual_files = []
874
 
    unresolved_symbols = set(['main']) # tracking unresolveds is necessary for .a linking, see below. (and main is always a necessary symbol)
 
994
    # Tracking unresolveds is necessary for .a linking, see below.
 
995
    # Specify all possible entry points to seed the linking process.
 
996
    # For a simple application, this would just be "main".
 
997
    unresolved_symbols = set([func[1:] for func in Settings.EXPORTED_FUNCTIONS])
875
998
    resolved_symbols = set()
876
999
    temp_dirs = []
877
1000
    files = map(os.path.abspath, files)
920
1043
                # Link in the .o if it provides symbols, *or* this is a singleton archive (which is apparently an exception in gcc ld)
921
1044
                #print >> sys.stderr, 'need', content, '?', unresolved_symbols, 'and we can supply', new_symbols.defs
922
1045
                #print >> sys.stderr, content, 'DEF', new_symbols.defs, '\n'
923
 
                if new_symbols.defs.intersection(unresolved_symbols) or len(files) == 1:
 
1046
                if new_symbols.defs.intersection(unresolved_symbols) or len(files) == 1 or force_archive_contents:
924
1047
                  if Building.is_bitcode(content):
925
1048
                    #print >> sys.stderr, '  adding object', content, '\n'
926
1049
                    resolved_symbols = resolved_symbols.union(new_symbols.defs)
936
1059
 
937
1060
    # Finish link
938
1061
    actual_files = unique_ordered(actual_files) # tolerate people trying to link a.so a.so etc.
939
 
    logging.debug('emcc: llvm-linking: %s', actual_files)
 
1062
    logging.debug('emcc: llvm-linking: %s to %s', actual_files, target)
940
1063
 
941
1064
    # check for too-long command line
942
 
    link_cmd = [LLVM_LINK] + actual_files + ['-o', target]
 
1065
    link_cmd = LINK_CMD + actual_files + ['-o', target]
943
1066
    # 8k is a bit of an arbitrary limit, but a reasonable one
944
1067
    # for max command line size before we use a respose file
945
1068
    response_file = None
947
1070
      logging.debug('using response file for llvm-link')
948
1071
      [response_fd, response_file] = mkstemp(suffix='.response', dir=TEMP_DIR)
949
1072
 
950
 
      link_cmd = [LLVM_LINK, "@" + response_file]
 
1073
      link_cmd = LINK_CMD + ["@" + response_file]
951
1074
 
952
1075
      response_fh = os.fdopen(response_fd, 'w')
953
1076
      for arg in actual_files:
991
1114
      opts = Building.pick_llvm_opts(opts)
992
1115
    #opts += ['-debug-pass=Arguments']
993
1116
    logging.debug('emcc: LLVM opts: ' + str(opts))
994
 
    output = Popen([LLVM_OPT, filename] + opts + ['-o=' + filename + '.opt.bc'], stdout=PIPE).communicate()[0]
 
1117
    output = Popen([LLVM_OPT, filename] + opts + ['-o', filename + '.opt.bc'], stdout=PIPE).communicate()[0]
995
1118
    assert os.path.exists(filename + '.opt.bc'), 'Failed to run llvm optimizations: ' + output
996
1119
    shutil.move(filename + '.opt.bc', filename)
997
1120
 
999
1122
  def llvm_opts(filename): # deprecated version, only for test runner. TODO: remove
1000
1123
    if Building.LLVM_OPTS:
1001
1124
      shutil.move(filename + '.o', filename + '.o.pre')
1002
 
      output = Popen([LLVM_OPT, filename + '.o.pre'] + Building.LLVM_OPT_OPTS + ['-o=' + filename + '.o'], stdout=PIPE).communicate()[0]
 
1125
      output = Popen([LLVM_OPT, filename + '.o.pre'] + Building.LLVM_OPT_OPTS + ['-o', filename + '.o'], stdout=PIPE).communicate()[0]
1003
1126
      assert os.path.exists(filename + '.o'), 'Failed to run llvm optimizations: ' + output
1004
1127
 
1005
1128
  @staticmethod
1010
1133
      output_filename = input_filename + '.o.ll'
1011
1134
      input_filename = input_filename + '.o'
1012
1135
    try_delete(output_filename)
1013
 
    output = Popen([LLVM_DIS, input_filename, '-o=' + output_filename], stdout=PIPE).communicate()[0]
 
1136
    output = Popen([LLVM_DIS, input_filename, '-o', output_filename], stdout=PIPE).communicate()[0]
1014
1137
    assert os.path.exists(output_filename), 'Could not create .ll file: ' + output
1015
1138
    return output_filename
1016
1139
 
1022
1145
      output_filename = input_filename + '.o'
1023
1146
      input_filename = input_filename + '.o.ll'
1024
1147
    try_delete(output_filename)
1025
 
    output = Popen([LLVM_AS, input_filename, '-o=' + output_filename], stdout=PIPE).communicate()[0]
 
1148
    output = Popen([LLVM_AS, input_filename, '-o', output_filename], stdout=PIPE).communicate()[0]
1026
1149
    assert os.path.exists(output_filename), 'Could not create bc file: ' + output
1027
1150
    return output_filename
1028
1151
 
1043
1166
    for line in output.split('\n'):
1044
1167
      if len(line) == 0: continue
1045
1168
      parts = filter(lambda seg: len(seg) > 0, line.split(' '))
 
1169
      # pnacl-nm will print zero offsets for bitcode
 
1170
      if len(parts) == 3 and parts[0] == "00000000":
 
1171
        parts.pop(0)
1046
1172
      if len(parts) == 2: # ignore lines with absolute offsets, these are not bitcode anyhow (e.g. |00000630 t d_source_name|)
1047
1173
        status, symbol = parts
1048
1174
        if status == 'U':
1091
1217
 
1092
1218
  @staticmethod
1093
1219
  def can_build_standalone():
1094
 
    return not Settings.BUILD_AS_SHARED_LIB and not Settings.LINKABLE
 
1220
    return not Settings.BUILD_AS_SHARED_LIB and not Settings.LINKABLE and not Settings.EXPORT_ALL
1095
1221
 
1096
1222
  @staticmethod
1097
1223
  def can_use_unsafe_opts():
1103
1229
 
1104
1230
  @staticmethod
1105
1231
  def get_safe_internalize():
1106
 
    exports = ','.join(map(lambda exp: exp[1:], expand_response(Settings.EXPORTED_FUNCTIONS)))
 
1232
    if not Building.can_build_standalone(): return [] # do not internalize anything
 
1233
    exps = expand_response(Settings.EXPORTED_FUNCTIONS)
 
1234
    exports = ','.join(map(lambda exp: exp[1:], exps))
1107
1235
    # internalize carefully, llvm 3.2 will remove even main if not told not to
1108
1236
    return ['-internalize', '-internalize-public-api-list=' + exports]
1109
1237
 
1193
1321
        opts.append('-jump-threading')
1194
1322
        opts.append('-correlated-propagation')
1195
1323
        opts.append('-dse')
1196
 
        #addExtensionsToPM(EP_ScalarOptimizerLate, MPM);
 
1324
        #addExtensionsToPM(EP_ScalarOptimizerLate, MPM)
1197
1325
 
1198
1326
        opts.append('-adce')
1199
1327
        opts.append('-simplifycfg')
1210
1338
    return opts
1211
1339
 
1212
1340
  @staticmethod
1213
 
  def js_optimizer(filename, passes, jcache):
1214
 
    return js_optimizer.run(filename, passes, listify(NODE_JS), jcache)
 
1341
  def js_optimizer(filename, passes, jcache=False, debug=False, extra_info=None):
 
1342
    return js_optimizer.run(filename, passes, listify(NODE_JS), jcache, debug, extra_info)
1215
1343
 
1216
1344
  @staticmethod
1217
1345
  def closure_compiler(filename, pretty=True):
1297
1425
          '-O' + str(opt_level), '--closure', '0'], raw)
1298
1426
        f = open(relooper, 'w')
1299
1427
        f.write("// Relooper, (C) 2012 Alon Zakai, MIT license, https://github.com/kripken/Relooper\n")
1300
 
        f.write("var Relooper = (function() {\n");
 
1428
        f.write("var Relooper = (function() {\n")
1301
1429
        f.write(open(raw).read())
1302
1430
        f.write('\n  return Module.Relooper;\n')
1303
1431
        f.write('})();\n')
1351
1479
chunkify = cache.chunkify
1352
1480
 
1353
1481
class JS:
 
1482
  memory_initializer_pattern = '/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, ([\dRuntime\.GLOBAL_BASEH+]+)\)'
 
1483
  no_memory_initializer_pattern = '/\* no memory initializer \*/'
 
1484
 
 
1485
  memory_staticbump_pattern = 'STATICTOP = STATIC_BASE \+ (\d+);'
 
1486
 
 
1487
  global_initializers_pattern = '/\* global initializers \*/ __ATINIT__.push\((.+)\);'
 
1488
 
1354
1489
  @staticmethod
1355
1490
  def to_nice_ident(ident): # limited version of the JS function toNiceIdent
1356
 
    return ident.replace('%', '$').replace('@', '_');
 
1491
    return ident.replace('%', '$').replace('@', '_')
 
1492
 
 
1493
  @staticmethod
 
1494
  def make_extcall(sig, named=True):
 
1495
    args = ','.join(['a' + str(i) for i in range(1, len(sig))])
 
1496
    args = 'index' + (',' if args else '') + args
 
1497
    # C++ exceptions are numbers, and longjmp is a string 'longjmp'
 
1498
    ret = '''function%s(%s) {
 
1499
  %sModule["dynCall_%s"](%s);
 
1500
}''' % ((' extCall_' + sig) if named else '', args, 'return ' if sig[0] != 'v' else '', sig, args)
 
1501
 
 
1502
    if Settings.DLOPEN_SUPPORT and Settings.ASSERTIONS:
 
1503
      # guard against cross-module stack leaks
 
1504
      ret = ret.replace(') {\n', ''') {
 
1505
  try {
 
1506
    var preStack = asm.stackSave();
 
1507
''').replace(';\n}', ''';
 
1508
  } finally {
 
1509
    assert(asm.stackSave() == preStack);
 
1510
  }
 
1511
}''')
 
1512
    return ret
 
1513
 
 
1514
  @staticmethod
 
1515
  def make_invoke(sig, named=True):
 
1516
    args = ','.join(['a' + str(i) for i in range(1, len(sig))])
 
1517
    args = 'index' + (',' if args else '') + args
 
1518
    # C++ exceptions are numbers, and longjmp is a string 'longjmp'
 
1519
    ret = '''function%s(%s) {
 
1520
  try {
 
1521
    %sModule["dynCall_%s"](%s);
 
1522
  } catch(e) {
 
1523
    if (typeof e !== 'number' && e !== 'longjmp') throw e;
 
1524
    asm["setThrew"](1, 0);
 
1525
  }
 
1526
}''' % ((' invoke_' + sig) if named else '', args, 'return ' if sig[0] != 'v' else '', sig, args)
 
1527
 
 
1528
    if Settings.DLOPEN_SUPPORT and Settings.ASSERTIONS:
 
1529
      # guard against cross-module stack leaks
 
1530
      ret = ret.replace('  try {', '''  var preStack = asm.stackSave();
 
1531
  try {
 
1532
''').replace('  }\n}', '''  } finally {
 
1533
    assert(asm.stackSave() == preStack);
 
1534
  }
 
1535
}''')
 
1536
 
 
1537
    return ret
 
1538
 
 
1539
  @staticmethod
 
1540
  def align(x, by):
 
1541
    while x % by != 0: x += 1
 
1542
    return x
1357
1543
 
1358
1544
# Compression of code and data for smaller downloads
1359
1545
class Compression:
1380
1566
    logging.error('Invoking Process failed: <<< ' + cmd + ' >>>')
1381
1567
    raise
1382
1568
 
 
1569
def check_execute(cmd, *args, **kw):
 
1570
  # TODO: use in more places. execute doesn't actually check that return values
 
1571
  # are nonzero
 
1572
  try:
 
1573
    kw['stderr'] = STDOUT
 
1574
    subprocess.check_output(cmd, *args, **kw)
 
1575
    logging.debug("Successfuly executed %s" % " ".join(cmd))
 
1576
  except subprocess.CalledProcessError as e:
 
1577
    logging.error("'%s' failed with output:\n%s" % (" ".join(e.cmd), e.output))
 
1578
    raise
 
1579
 
1383
1580
def suffix(name):
1384
1581
  parts = name.split('.')
1385
1582
  if len(parts) > 1: