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'
209
llvm_root = os.path.dirname(Popen(['which', 'llvm-dis'], stdout=PIPE).communicate()[0].replace('\n', ''))
212
config_file = config_file.replace('{{{ LLVM_ROOT }}}', llvm_root)
215
node = Popen(['which', 'node'], stdout=PIPE).communicate()[0].replace('\n', '') or \
216
Popen(['which', 'nodejs'], stdout=PIPE).communicate()[0].replace('\n', '') or node
219
config_file = config_file.replace('{{{ NODE }}}', node)
220
python = sys.executable or 'python'
222
python = Popen(['which', 'python2'], stdout=PIPE).communicate()[0].replace('\n', '') or \
223
Popen(['which', 'python'], stdout=PIPE).communicate()[0].replace('\n', '') or python
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))
218
tempdir = os.environ.get('TEMP') or os.environ.get('TMP') or 'c:\\temp'
221
config_file = config_file.replace('\'{{{ TEMP }}}\'', repr(tempdir))
229
224
open(CONFIG_FILE, 'w').write(config_file)
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'))
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
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')
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()
500
logging.basicConfig(format='%(levelname)-8s %(name)s: %(message)s')
502
logger = logging.getLogger()
503
logger.setLevel(logging.DEBUG if os.environ.get('EMCC_DEBUG') else logging.INFO)
460
506
if not EMSCRIPTEN_TEMP_DIR:
461
507
EMSCRIPTEN_TEMP_DIR = tempfile.mkdtemp(prefix='emscripten_temp_', dir=configuration.TEMP_DIR)
463
try_delete(EMSCRIPTEN_TEMP_DIR)
464
atexit.register(clean_temp)
508
def prepare_to_clean_temp(d):
511
atexit.register(clean_temp)
512
prepare_to_clean_temp(EMSCRIPTEN_TEMP_DIR) # this global var might change later
466
514
# EM_CONFIG stuff
498
546
return os.environ.get('EMCC_LLVM_TARGET') or 'le32-unknown-nacl' # 'i386-pc-linux-gnu'
499
547
LLVM_TARGET = get_llvm_target()
549
# COMPILER_OPTS: options passed to clang when generating bitcode for us
502
551
COMPILER_OPTS # Can be set in EM_CONFIG, optionally
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__',
556
#'-fno-threadsafe-statics', # disabled due to issue 1289
510
557
'-target', LLVM_TARGET]
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.
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__']
515
568
USE_EMSDK = not os.environ.get('EMMAKEN_NO_SDK')
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'),
531
'-U__APPLE__', '-U__linux__'
585
EMSDK_OPTS += COMPILER_STANDARDIZATION_OPTS
586
if LLVM_TARGET != 'le32-unknown-nacl':
587
EMSDK_CXX_OPTS = ['-nostdinc++'] # le32 target does not need -nostdinc++
533
590
COMPILER_OPTS += EMSDK_OPTS
594
COMPILER_OPTS += COMPILER_STANDARDIZATION_OPTS
537
596
#print >> sys.stderr, 'SDK opts', ' '.join(EMSDK_OPTS)
538
597
#print >> sys.stderr, 'Compiler opts', ' '.join(COMPILER_OPTS)
639
698
# Settings. A global singleton. Not pretty, but nicer than passing |, settings| everywhere
646
reset = Settings.reset
648
# Given some emcc-type args (-O3, -s X=Y, etc.), fill Settings with the right settings
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()
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)):
662
exec 'Settings.' + args[i+1] in globals() # execute the setting
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
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]
676
def apply_opt_level(self, opt_level, noisy=False):
679
Settings.ASSERTIONS = 0
680
Settings.DISABLE_EXCEPTION_CATCHING = 1
681
Settings.EMIT_GENERATED_FUNCTIONS = 1
684
Settings.ALIASING_FUNCTION_POINTERS = 1
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.)')
694
Settings.load() # load defaults
700
class Settings2(type):
709
self.attrs = { 'QUANTUM_SIZE': 4 }
712
# Given some emcc-type args (-O3, -s X=Y, etc.), fill Settings with the right settings
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)
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)):
727
declare = re.sub(r'([\w\d]+)\s*=\s*(.+)', r'self.attrs["\1"]=\2;', args[i+1])
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
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]
742
def copy(self, values):
746
def apply_opt_level(self, opt_level, noisy=False):
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
753
self.attrs['RELOOP'] = 1
754
self.attrs['ALIASING_FUNCTION_POINTERS'] = 1
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.)')
762
def __getattr__(self, attr):
763
if attr in self.attrs:
764
return self.attrs[attr]
768
def __setattr__(self, attr, value):
769
self.attrs[attr] = value
775
if Settings2.__instance is None:
776
Settings2.__instance = Settings2.__impl()
777
return Settings2.__instance
779
def __getattr__(self, attr):
780
return getattr(self.instance(), attr)
782
def __setattr__(self, attr, value):
783
return setattr(self.instance(), attr, value)
785
class Settings(object):
786
__metaclass__ = Settings2
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()
823
# Finds the given executable 'program' in PATH. Operates like the Unix tool 'which'.
828
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
830
fpath, fname = os.path.split(program)
835
for path in os.environ["PATH"].split(os.pathsep):
836
path = path.strip('"')
837
exe_file = os.path.join(path, program)
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'
734
852
def handle_CMake_toolchain(args, env):
735
CMakeToolchain = ('''# the name of the target operating system
736
SET(CMAKE_SYSTEM_NAME Linux)
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)
746
# here is the target environment located
747
SET(CMAKE_FIND_ROOT_PATH $EMSCRIPTEN_ROOT/system/include )
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))
854
def has_substr(array, substr):
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'))
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']
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():
833
949
if configure: # Useful in debugging sometimes to comment this out (and the lines below up to and including the |link| call)
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'):
842
958
def open_make_err(i, mode='r'):
843
959
return open(os.path.join(project_dir, 'make_err' + str(i)), mode)
961
if verbose_level >= 3:
962
make_args += ['VERBOSE=1']
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:
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
860
979
except Exception, e:
862
# Due to the ugly hack above our best guess is to output the first run
863
with open_make_err(0) as 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:
985
sys.stderr.write(line)
866
986
raise Exception('could not build library ' + name + ' due to exception ' + str(e))
868
988
os.chdir(old_dir)
869
989
return generated_libs
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()
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)
1351
1479
chunkify = cache.chunkify
1482
memory_initializer_pattern = '/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, ([\dRuntime\.GLOBAL_BASEH+]+)\)'
1483
no_memory_initializer_pattern = '/\* no memory initializer \*/'
1485
memory_staticbump_pattern = 'STATICTOP = STATIC_BASE \+ (\d+);'
1487
global_initializers_pattern = '/\* global initializers \*/ __ATINIT__.push\((.+)\);'
1355
1490
def to_nice_ident(ident): # limited version of the JS function toNiceIdent
1356
return ident.replace('%', '$').replace('@', '_');
1491
return ident.replace('%', '$').replace('@', '_')
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)
1502
if Settings.DLOPEN_SUPPORT and Settings.ASSERTIONS:
1503
# guard against cross-module stack leaks
1504
ret = ret.replace(') {\n', ''') {
1506
var preStack = asm.stackSave();
1507
''').replace(';\n}', ''';
1509
assert(asm.stackSave() == preStack);
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) {
1521
%sModule["dynCall_%s"](%s);
1523
if (typeof e !== 'number' && e !== 'longjmp') throw e;
1524
asm["setThrew"](1, 0);
1526
}''' % ((' invoke_' + sig) if named else '', args, 'return ' if sig[0] != 'v' else '', sig, args)
1528
if Settings.DLOPEN_SUPPORT and Settings.ASSERTIONS:
1529
# guard against cross-module stack leaks
1530
ret = ret.replace(' try {', ''' var preStack = asm.stackSave();
1532
''').replace(' }\n}', ''' } finally {
1533
assert(asm.stackSave() == preStack);
1541
while x % by != 0: x += 1
1358
1544
# Compression of code and data for smaller downloads
1359
1545
class Compression: