diff options
Diffstat (limited to 'tools/shared.py')
-rw-r--r-- | tools/shared.py | 144 |
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 |