aboutsummaryrefslogtreecommitdiff
path: root/tools/shared.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/shared.py')
-rw-r--r--tools/shared.py144
1 files changed, 92 insertions, 52 deletions
diff --git a/tools/shared.py b/tools/shared.py
index 3ee5db23..8031d99c 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -283,6 +283,20 @@ def check_node_version():
logging.warning('cannot check node version: %s', e)
return False
+# Finds the system temp directory without resorting to using the one configured in .emscripten
+def find_temp_directory():
+ if WINDOWS:
+ if os.getenv('TEMP') and os.path.isdir(os.getenv('TEMP')):
+ return os.getenv('TEMP')
+ elif os.getenv('TMP') and os.path.isdir(os.getenv('TMP')):
+ return os.getenv('TMP')
+ elif os.path.isdir('C:\\temp'):
+ return os.getenv('C:\\temp')
+ else:
+ return None # No luck!
+ else:
+ return '/tmp'
+
# Check that basic stuff we need (a JS engine to compile, Node.js, and Clang and LLVM)
# exists.
# The test runner always does this check (through |force|). emcc does this less frequently,
@@ -429,7 +443,6 @@ EMCC = path_from_root('emcc')
EMXX = path_from_root('em++')
EMAR = path_from_root('emar')
EMRANLIB = path_from_root('emranlib')
-EMLIBTOOL = path_from_root('emlibtool')
EMCONFIG = path_from_root('em-config')
EMLINK = path_from_root('emlink.py')
EMMAKEN = path_from_root('tools', 'emmaken.py')
@@ -451,8 +464,13 @@ class Configuration:
try:
self.TEMP_DIR = TEMP_DIR
except NameError:
- logging.debug('TEMP_DIR not defined in ~/.emscripten, using /tmp')
- self.TEMP_DIR = '/tmp'
+ self.TEMP_DIR = find_temp_directory()
+ if self.TEMP_DIR == None:
+ 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'.")
+ logging.debug('TEMP_DIR not defined in ~/.emscripten, using ' + self.TEMP_DIR)
+
+ if not os.path.isdir(self.TEMP_DIR):
+ logging.critical("The temp directory TEMP_DIR='" + self.TEMP_DIR + "' doesn't seem to exist! Please make sure that the path is correct.")
self.CANONICAL_TEMP_DIR = os.path.join(self.TEMP_DIR, 'emscripten_temp')
@@ -470,12 +488,13 @@ class Configuration:
save_debug_files=os.environ.get('EMCC_DEBUG_SAVE'))
def apply_configuration():
- global configuration, DEBUG, EMSCRIPTEN_TEMP_DIR, DEBUG_CACHE, CANONICAL_TEMP_DIR
+ global configuration, DEBUG, EMSCRIPTEN_TEMP_DIR, DEBUG_CACHE, CANONICAL_TEMP_DIR, TEMP_DIR
configuration = Configuration()
DEBUG = configuration.DEBUG
EMSCRIPTEN_TEMP_DIR = configuration.EMSCRIPTEN_TEMP_DIR
DEBUG_CACHE = configuration.DEBUG_CACHE
CANONICAL_TEMP_DIR = configuration.CANONICAL_TEMP_DIR
+ TEMP_DIR = configuration.TEMP_DIR
apply_configuration()
logging.basicConfig(format='%(levelname)-8s %(name)s: %(message)s')
@@ -527,15 +546,13 @@ def get_llvm_target():
return os.environ.get('EMCC_LLVM_TARGET') or 'le32-unknown-nacl' # 'i386-pc-linux-gnu'
LLVM_TARGET = get_llvm_target()
+# COMPILER_OPTS: options passed to clang when generating bitcode for us
try:
COMPILER_OPTS # Can be set in EM_CONFIG, optionally
except:
COMPILER_OPTS = []
-# Force a simple, standard target as much as possible: target 32-bit linux, and disable various flags that hint at other platforms
-COMPILER_OPTS = COMPILER_OPTS + ['-m32', '-U__i386__', '-U__i386', '-Ui386',
- '-U__SSE__', '-U__SSE_MATH__', '-U__SSE2__', '-U__SSE2_MATH__', '-U__MMX__',
- '-DEMSCRIPTEN', '-D__EMSCRIPTEN__', '-U__STRICT_ANSI__',
- '-D__IEEE_LITTLE_ENDIAN', '-fno-math-errno',
+COMPILER_OPTS = COMPILER_OPTS + ['-m32', '-DEMSCRIPTEN', '-D__EMSCRIPTEN__',
+ '-fno-math-errno',
#'-fno-threadsafe-statics', # disabled due to issue 1289
'-target', LLVM_TARGET]
@@ -543,6 +560,11 @@ if LLVM_TARGET == 'le32-unknown-nacl':
COMPILER_OPTS = filter(lambda opt: opt != '-m32', COMPILER_OPTS) # le32 target is 32-bit anyhow, no need for -m32
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.
+# Remove various platform specific defines, and set little endian
+COMPILER_STANDARDIZATION_OPTS = ['-U__i386__', '-U__i386', '-Ui386', '-U__STRICT_ANSI__', '-D__IEEE_LITTLE_ENDIAN',
+ '-U__SSE__', '-U__SSE_MATH__', '-U__SSE2__', '-U__SSE2_MATH__', '-U__MMX__',
+ '-U__APPLE__', '-U__linux__']
+
USE_EMSDK = not os.environ.get('EMMAKEN_NO_SDK')
if USE_EMSDK:
@@ -559,9 +581,8 @@ if USE_EMSDK:
'-Xclang', '-isystem' + path_from_root('system', 'include', 'gfx'),
'-Xclang', '-isystem' + path_from_root('system', 'include', 'net'),
'-Xclang', '-isystem' + path_from_root('system', 'include', 'SDL'),
- ] + [
- '-U__APPLE__', '-U__linux__'
]
+ EMSDK_OPTS += COMPILER_STANDARDIZATION_OPTS
if LLVM_TARGET != 'le32-unknown-nacl':
EMSDK_CXX_OPTS = ['-nostdinc++'] # le32 target does not need -nostdinc++
else:
@@ -570,6 +591,7 @@ if USE_EMSDK:
else:
EMSDK_OPTS = []
EMSDK_CXX_OPTS = []
+ COMPILER_OPTS += COMPILER_STANDARDIZATION_OPTS
#print >> sys.stderr, 'SDK opts', ' '.join(EMSDK_OPTS)
#print >> sys.stderr, 'Compiler opts', ' '.join(COMPILER_OPTS)
@@ -717,6 +739,10 @@ class Settings2(type):
return ret
@classmethod
+ def copy(self, values):
+ self.attrs = values
+
+ @classmethod
def apply_opt_level(self, opt_level, noisy=False):
if opt_level >= 1:
self.attrs['ASM_JS'] = 1
@@ -782,7 +808,6 @@ class Building:
env['LD'] = EMCC if not WINDOWS else 'python %r' % EMCC
env['LDSHARED'] = EMCC if not WINDOWS else 'python %r' % EMCC
env['RANLIB'] = EMRANLIB if not WINDOWS else 'python %r' % EMRANLIB
- #env['LIBTOOL'] = EMLIBTOOL if not WINDOWS else 'python %r' % EMLIBTOOL
env['EMMAKEN_COMPILER'] = Building.COMPILER
env['EMSCRIPTEN_TOOLS'] = path_from_root('tools')
env['CFLAGS'] = env['EMMAKEN_CFLAGS'] = ' '.join(Building.COMPILER_TEST_OPTS)
@@ -797,33 +822,12 @@ class Building:
@staticmethod
def handle_CMake_toolchain(args, env):
- CMakeToolchain = ('''# the name of the target operating system
-SET(CMAKE_SYSTEM_NAME Linux)
-
-# which C and C++ compiler to use
-SET(CMAKE_C_COMPILER %(winfix)s$EMSCRIPTEN_ROOT/emcc)
-SET(CMAKE_CXX_COMPILER %(winfix)s$EMSCRIPTEN_ROOT/em++)
-SET(CMAKE_AR %(winfix)s$EMSCRIPTEN_ROOT/emar)
-SET(CMAKE_RANLIB %(winfix)s$EMSCRIPTEN_ROOT/emranlib)
-SET(CMAKE_C_FLAGS $CFLAGS)
-SET(CMAKE_CXX_FLAGS $CXXFLAGS)
-
-# here is the target environment located
-SET(CMAKE_FIND_ROOT_PATH $EMSCRIPTEN_ROOT/system/include )
-
-# adjust the default behaviour of the FIND_XXX() commands:
-# search headers and libraries in the target environment, search
-# programs in the host environment
-set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)
-set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS else 'python ' }) \
- .replace('$EMSCRIPTEN_ROOT', path_from_root('').replace('\\', '/')) \
- .replace('$CFLAGS', env['CFLAGS']) \
- .replace('$CXXFLAGS', env['CFLAGS'])
- toolchainFile = mkstemp(suffix='.cmaketoolchain.txt', dir=configuration.TEMP_DIR)[1]
- open(toolchainFile, 'w').write(CMakeToolchain)
- args.append('-DCMAKE_TOOLCHAIN_FILE=%s' % os.path.abspath(toolchainFile))
+ # Don't append a toolchain file if the user specified one already.
+ for arg in args:
+ if '-DCMAKE_TOOLCHAIN_FILE' in arg:
+ return args
+
+ args.append('-DCMAKE_TOOLCHAIN_FILE=' + path_from_root('cmake', 'Platform', 'Emscripten.cmake'))
return args
@staticmethod
@@ -843,6 +847,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
raise
del env['EMMAKEN_JUST_CONFIGURE']
if process.returncode is not 0:
+ logging.error('Configure step failed with non-zero return code ' + str(process.returncode) + '! Command line: ' + str(args))
raise subprocess.CalledProcessError(cmd=args, returncode=process.returncode)
@staticmethod
@@ -893,12 +898,13 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
# except:
# pass
env = Building.get_building_env(native)
+ verbose_level = int(os.getenv('EM_BUILD_VERBOSE')) if os.getenv('EM_BUILD_VERBOSE') != None else 0
for k, v in env_init.iteritems():
env[k] = v
if configure: # Useful in debugging sometimes to comment this out (and the lines below up to and including the |link| call)
try:
- Building.configure(configure + configure_args, stdout=open(os.path.join(project_dir, 'configure_'), 'w'),
- stderr=open(os.path.join(project_dir, 'configure_err'), 'w'), env=env)
+ Building.configure(configure + configure_args, env=env, stdout=open(os.path.join(project_dir, 'configure_'), 'w') if verbose_level < 2 else None,
+ stderr=open(os.path.join(project_dir, 'configure_err'), 'w') if verbose_level < 1 else None)
except subprocess.CalledProcessError, e:
pass # Ignore exit code != 0
def open_make_out(i, mode='r'):
@@ -906,13 +912,16 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
def open_make_err(i, mode='r'):
return open(os.path.join(project_dir, 'make_err' + str(i)), mode)
-
+
+ if verbose_level >= 3:
+ make_args += ['VERBOSE=1']
+
for i in range(2): # FIXME: Sad workaround for some build systems that need to be run twice to succeed (e.g. poppler)
with open_make_out(i, 'w') as make_out:
with open_make_err(i, 'w') as make_err:
try:
- Building.make(make + make_args, stdout=make_out,
- stderr=make_err, env=env)
+ Building.make(make + make_args, stdout=make_out if verbose_level < 2 else None,
+ stderr=make_err if verbose_level < 1 else None, env=env)
except subprocess.CalledProcessError, e:
pass # Ignore exit code != 0
try:
@@ -924,10 +933,11 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
break
except Exception, e:
if i > 0:
- # Due to the ugly hack above our best guess is to output the first run
- with open_make_err(0) as ferr:
- for line in ferr:
- sys.stderr.write(line)
+ if verbose_level == 0:
+ # Due to the ugly hack above our best guess is to output the first run
+ with open_make_err(0) as ferr:
+ for line in ferr:
+ sys.stderr.write(line)
raise Exception('could not build library ' + name + ' due to exception ' + str(e))
if old_dir:
os.chdir(old_dir)
@@ -1176,8 +1186,6 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
def get_safe_internalize():
if not Building.can_build_standalone(): return [] # do not internalize anything
exps = expand_response(Settings.EXPORTED_FUNCTIONS)
- if '_malloc' not in exps: exps.append('_malloc') # needed internally, even if user did not add to EXPORTED_FUNCTIONS
- if '_free' not in exps: exps.append('_free')
exports = ','.join(map(lambda exp: exp[1:], exps))
# internalize carefully, llvm 3.2 will remove even main if not told not to
return ['-internalize', '-internalize-public-api-list=' + exports]
@@ -1426,7 +1434,7 @@ JCache = cache.JCache(Cache)
chunkify = cache.chunkify
class JS:
- memory_initializer_pattern = '/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, ([\dRuntime\.GLOBAL_BASE+]+)\)'
+ memory_initializer_pattern = '/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, ([\dRuntime\.GLOBAL_BASEH+]+)\)'
no_memory_initializer_pattern = '/\* no memory initializer \*/'
memory_staticbump_pattern = 'STATICTOP = STATIC_BASE \+ (\d+);'
@@ -1438,11 +1446,32 @@ class JS:
return ident.replace('%', '$').replace('@', '_')
@staticmethod
+ def make_extcall(sig, named=True):
+ args = ','.join(['a' + str(i) for i in range(1, len(sig))])
+ args = 'index' + (',' if args else '') + args
+ # C++ exceptions are numbers, and longjmp is a string 'longjmp'
+ ret = '''function%s(%s) {
+ %sModule["dynCall_%s"](%s);
+}''' % ((' extCall_' + sig) if named else '', args, 'return ' if sig[0] != 'v' else '', sig, args)
+
+ if Settings.DLOPEN_SUPPORT and Settings.ASSERTIONS:
+ # guard against cross-module stack leaks
+ ret = ret.replace(') {\n', ''') {
+ try {
+ var preStack = asm.stackSave();
+''').replace(';\n}', ''';
+ } finally {
+ assert(asm.stackSave() == preStack);
+ }
+}''')
+ return ret
+
+ @staticmethod
def make_invoke(sig, named=True):
args = ','.join(['a' + str(i) for i in range(1, len(sig))])
args = 'index' + (',' if args else '') + args
# C++ exceptions are numbers, and longjmp is a string 'longjmp'
- return '''function%s(%s) {
+ ret = '''function%s(%s) {
try {
%sModule["dynCall_%s"](%s);
} catch(e) {
@@ -1451,6 +1480,17 @@ class JS:
}
}''' % ((' invoke_' + sig) if named else '', args, 'return ' if sig[0] != 'v' else '', sig, args)
+ if Settings.DLOPEN_SUPPORT and Settings.ASSERTIONS:
+ # guard against cross-module stack leaks
+ ret = ret.replace(' try {', ''' var preStack = asm.stackSave();
+ try {
+''').replace(' }\n}', ''' } finally {
+ assert(asm.stackSave() == preStack);
+ }
+}''')
+
+ return ret
+
@staticmethod
def align(x, by):
while x % by != 0: x += 1