2011-11-21 09:02:46 +04:00
#!/usr/bin/env python
'''
emcc - compiler helper script
=============================
emcc is a drop-in replacement for a compiler like gcc or clang.
2011-12-14 23:12:19 +04:00
Tell your build system to use this instead of the compiler, and similarly
use emar, emld and emranlib instead of the same command without 'em'.
2011-11-21 09:02:46 +04:00
Example uses:
2011-12-17 06:14:02 +04:00
* For configure, instead of ./configure, cmake, etc., run emconfigure.py
2011-11-21 09:02:46 +04:00
with that command as an argument, for example
2011-12-17 06:14:02 +04:00
emconfigure.py ./configure [options]
2011-11-21 09:02:46 +04:00
2011-12-17 06:14:02 +04:00
emconfigure.py is a tiny script that just sets some environment vars
2011-11-21 09:02:46 +04:00
as a convenience. The command just shown is equivalent to
2011-12-14 23:12:19 +04:00
EMMAKEN_JUST_CONFIGURE=1 RANLIB=PATH/emranlib AR=PATH/emar CXX=PATH/em++ CC=PATH/emcc ./configure [options]
2011-11-21 09:02:46 +04:00
where PATH is the path to this file.
EMMAKEN_JUST_CONFIGURE tells emcc that it is being run in ./configure,
so it should relay everything to gcc/g++. You should not define that when
running make, of course.
* With CMake, the same command will work (with cmake instead of ./configure). You may also be
able to do the following in your CMakeLists.txt:
2011-12-12 08:43:08 +04:00
SET(CMAKE_C_COMPILER "PATH/emcc")
SET(CMAKE_CXX_COMPILER "PATH/em++")
2011-12-14 23:12:19 +04:00
SET(CMAKE_LINKER "PATH/emld")
SET(CMAKE_CXX_LINKER "PATH/emld")
SET(CMAKE_C_LINK_EXECUTABLE "PATH/emld")
SET(CMAKE_CXX_LINK_EXECUTABLE "PATH/emld")
SET(CMAKE_AR "PATH/emar")
SET(CMAKE_RANLIB "PATH/emranlib")
2011-11-21 09:02:46 +04:00
* For SCons the shared.py can be imported like so:
__file__ = str(Dir('#/project_path_to_emscripten/dummy/dummy'))
__rootpath__ = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
def path_from_root(*pathelems):
return os.path.join(__rootpath__, *pathelems)
exec(open(path_from_root('tools', 'shared.py'), 'r').read())
For using the Emscripten compilers/linkers/etc. you can do:
env = Environment()
...
env.Append(CCFLAGS = COMPILER_OPTS)
env.Replace(LINK = LLVM_LD)
env.Replace(LD = LLVM_LD)
TODO: Document all relevant setup changes
2011-12-13 22:09:59 +04:00
After setting that up, run your build system normally.
2011-11-21 09:02:46 +04:00
2011-12-12 08:43:08 +04:00
Note the appearance of em++ instead of emcc
2011-11-21 09:02:46 +04:00
for the C++ compiler. This is needed for cases where we get
a C++ file with a C extension, in which case CMake can be told
to run g++ on it despite the .c extension, see
https://github.com/kripken/emscripten/issues/6
(If a similar situation occurs with ./configure, you can do the same there too.)
emcc can be influenced by a few environment variables:
EMMAKEN_NO_SDK - Will tell emcc *not* to use the emscripten headers. Instead
your system headers will be used.
EMMAKEN_COMPILER - The compiler to be used, if you don't want the default clang.
'''
2011-12-15 05:42:35 +04:00
import os, sys, shutil, tempfile
2011-12-12 03:24:04 +04:00
from subprocess import Popen, PIPE, STDOUT
2011-12-11 22:23:03 +04:00
from tools import shared
2011-11-21 09:02:46 +04:00
2011-12-14 07:19:43 +04:00
DEBUG = os.environ.get('EMCC_DEBUG')
2011-12-15 23:01:56 +04:00
TEMP_DIR = os.environ.get('EMCC_TEMP_DIR')
2011-12-22 03:47:38 +04:00
LEAVE_INPUTS_RAW = os.environ.get('EMCC_LEAVE_INPUTS_RAW') # Do not compile .ll files into .bc, just compile them with emscripten directly
# Not recommended, this is mainly for the test runner, or if you have some other
# specific need.
2012-01-19 05:40:58 +04:00
# One major limitation with this mode is that dlmalloc and libc++ cannot be
# added in. Also, LLVM optimizations will not be done, nor dead code elimination
2011-11-23 09:34:03 +04:00
2011-12-12 08:43:08 +04:00
if DEBUG: print >> sys.stderr, 'emcc: ', ' '.join(sys.argv)
2012-01-22 02:01:42 +04:00
if DEBUG and LEAVE_INPUTS_RAW: print >> sys.stderr, 'emcc: leaving inputs raw'
2011-11-21 09:02:46 +04:00
2011-12-18 23:34:19 +04:00
shared.check_sanity()
2011-11-23 09:34:03 +04:00
# Handle some global flags
2011-12-11 22:23:03 +04:00
if len(sys.argv) == 1:
print 'emcc: no input files'
exit(0)
2011-11-23 09:34:03 +04:00
if sys.argv[1] == '--version':
2011-12-11 22:23:03 +04:00
print '''emcc (Emscripten GCC-like replacement) 2.0
Copyright (C) 2011 the Emscripten authors.
This is free and open source software under the MIT license.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
2011-11-23 09:34:03 +04:00
'''
exit(0)
elif sys.argv[1] == '--help':
2011-12-11 23:39:00 +04:00
this = os.path.basename('em++' if os.environ.get('EMMAKEN_CXX') else 'emcc')
print '''%s [options] file...
Most normal gcc/g++ options will work, for example:
--help Display this information
--version Display compiler version information
Options that are modified or new in %s include:
2011-12-13 01:35:51 +04:00
-O0 No optimizations (default)
2011-12-14 00:20:45 +04:00
-O1 Simple optimizations, including safe LLVM
optimizations, and no runtime assertions
2011-12-16 10:13:40 +04:00
or C++ exception catching (to re-enable
C++ exception catching, use
2012-01-08 06:36:42 +04:00
-s DISABLE_EXCEPTION_CATCHING=0 ).
Note: Optimizations are only done when
compiling to JavaScript, not to intermediate
bitcode.
2011-12-15 09:17:53 +04:00
-O2 As -O1, plus the relooper (loop recreation),
2011-12-16 22:56:36 +04:00
plus closure compiler advanced opts
2011-12-13 01:35:51 +04:00
Warning: Compiling with this takes a long time!
-O3 As -O2, plus dangerous optimizations that may
2011-12-14 00:20:45 +04:00
break the generated code! If that happens, try
-O2 and then adding dangerous optimizations one
by one.
-s OPTION=VALUE JavaScript code generation option passed
into the emscripten compiler
--typed-arrays <mode> 0: No typed arrays
1: Parallel typed arrays
2: Shared (C-like) typed arrays (default)
2011-12-15 06:01:23 +04:00
--llvm-opts <level> 0: No LLVM optimizations (default in -O0)
2011-12-14 00:20:45 +04:00
1: Safe/portable LLVM optimizations
2011-12-15 06:01:23 +04:00
(default in -O1 and above)
2011-12-14 00:20:45 +04:00
2: Full, unsafe/unportable LLVM optimizations;
this will almost certainly break the
generated code!
2011-12-15 09:17:53 +04:00
--closure <on> 0: No closure compiler (default in -O0, -O1)
1: Run closure compiler (default in -O2, -O3)
2012-01-05 02:36:02 +04:00
--js-transform <cmd> <cmd> will be called on the generated code
before it is optimized. This lets you modify
the JavaScript, for example adding some code
or removing some code, in a way that those
modifications will be optimized together with
the generated code properly. <cmd> will be
called with the filename of the generated
code as a parameter; to modify the code, you
can read the original data and then append to
it or overwrite it with the modified data.
<cmd> is interpreted as a space-separated
list of arguments, for example, <cmd> of
"python processor.py" will cause a python
script to be run.
2012-01-13 06:25:01 +04:00
--compress <on> 0: Do not compress the generated JavaScript's
whitespace (default if closure compiler
will not be run)
1: Compress the generated JavaScript's
whitespace (default if closure compiler
will be run). Note that this by itself
will not minify the code (closure does
that)
2011-12-11 23:39:00 +04:00
The target file, if specified (-o <target>), defines what will
be generated:
2011-12-11 23:54:22 +04:00
2011-12-14 22:50:22 +04:00
<name>.js JavaScript (default)
2011-12-11 23:39:00 +04:00
<name>.html HTML with embedded JavaScript
2011-12-14 22:50:22 +04:00
<name>.bc LLVM bitcode
2011-12-14 06:23:00 +04:00
<name>.o LLVM bitcode
2011-12-11 23:54:22 +04:00
The -c option (which tells gcc not to run the linker) will
2011-12-14 22:50:22 +04:00
cause LLVM bitcode to be generated, as %s only generates
2011-12-11 23:54:22 +04:00
JavaScript in the final linking stage of building.
2011-12-17 06:14:02 +04:00
The input file(s) can be either source code files that
Clang can handle (C or C++), LLVM bitcode in binary form,
or LLVM assembly files in human-readable form.
2011-12-11 23:54:22 +04:00
''' % (this, this, this)
2011-11-23 09:34:03 +04:00
exit(0)
2011-12-17 23:49:26 +04:00
# If this is a configure-type thing, do not compile to JavaScript, instead use clang
# to compile to a native binary (using our headers, so things make sense later)
2011-11-21 09:02:46 +04:00
CONFIGURE_CONFIG = os.environ.get('EMMAKEN_JUST_CONFIGURE')
CMAKE_CONFIG = 'CMakeFiles/cmTryCompileExec.dir' in ' '.join(sys.argv)# or 'CMakeCCompilerId' in ' '.join(sys.argv)
if CONFIGURE_CONFIG or CMAKE_CONFIG:
2011-12-17 23:49:26 +04:00
compiler = shared.CLANG
2011-12-18 01:14:59 +04:00
if not ('CXXCompiler' in ' '.join(sys.argv) or os.environ.get('EMMAKEN_CXX')):
2011-12-17 23:49:26 +04:00
compiler = shared.to_cc(compiler)
2011-12-15 05:01:16 +04:00
cmd = [compiler] + shared.EMSDK_OPTS + sys.argv[1:]
2011-12-18 01:14:59 +04:00
if DEBUG: print >> sys.stderr, 'emcc, just configuring: ', compiler, cmd
2011-11-21 09:02:46 +04:00
exit(os.execvp(compiler, cmd))
2011-12-12 03:24:04 +04:00
if os.environ.get('EMMAKEN_COMPILER'):
CXX = os.environ['EMMAKEN_COMPILER']
else:
CXX = shared.CLANG
2011-11-21 09:02:46 +04:00
2011-12-12 03:24:04 +04:00
CC = shared.to_cc(CXX)
2011-11-21 09:02:46 +04:00
2011-12-12 03:24:04 +04:00
# If we got here from a redirection through emmakenxx.py, then force a C++ compiler here
if os.environ.get('EMMAKEN_CXX'):
CC = CXX
2011-11-21 09:02:46 +04:00
2011-12-12 03:24:04 +04:00
CC_ADDITIONAL_ARGS = shared.COMPILER_OPTS # + ['-g']?
2011-11-21 09:02:46 +04:00
2011-12-12 03:24:04 +04:00
EMMAKEN_CFLAGS = os.environ.get('EMMAKEN_CFLAGS')
if EMMAKEN_CFLAGS: CC_ADDITIONAL_ARGS += EMMAKEN_CFLAGS.split(' ')
2011-11-21 09:02:46 +04:00
2011-12-14 06:23:00 +04:00
# ---------------- Utilities ---------------
2011-12-15 08:13:22 +04:00
SOURCE_SUFFIXES = ('.c', '.cpp', '.cxx', '.cc')
2011-12-17 06:14:02 +04:00
BITCODE_SUFFIXES = ('.bc', '.o', '.ll')
2011-12-15 06:46:48 +04:00
2011-12-14 06:23:00 +04:00
def unsuffixed(name):
return '.'.join(name.split('.')[:-1])
def unsuffixed_basename(name):
return os.path.basename(unsuffixed(name))
2011-12-17 00:36:12 +04:00
LLVM_INTERNAL_OPT_LEVEL = 2
2011-12-14 06:23:00 +04:00
# ---------------- End configs -------------
2011-11-21 09:02:46 +04:00
2011-12-12 03:24:04 +04:00
if len(sys.argv) == 1 or sys.argv[1] in ['x', 't']:
# noop ar
2011-12-12 08:43:08 +04:00
if DEBUG: print >> sys.stderr, 'emcc, just ar'
2011-12-12 03:24:04 +04:00
sys.exit(0)
2011-11-21 09:02:46 +04:00
2011-12-12 03:24:04 +04:00
use_cxx = True
header = False # pre-compiled headers. We fake that by just copying the file
2011-11-21 09:02:46 +04:00
2011-12-12 03:24:04 +04:00
for i in range(1, len(sys.argv)):
arg = sys.argv[i]
2011-12-14 23:12:19 +04:00
if not arg.startswith('-'):
2011-12-12 03:24:04 +04:00
if arg.endswith('.c'):
use_cxx = False
if arg.endswith('.h') and sys.argv[i-1] != '-include':
header = True
# Check if a target is specified
target = None
for i in range(len(sys.argv)-1):
2011-12-14 06:23:00 +04:00
if sys.argv[i].startswith('-o='):
raise Exception('Invalid syntax: do not use -o=X, use -o X')
2011-12-12 03:24:04 +04:00
if sys.argv[i] == '-o':
target = sys.argv[i+1]
sys.argv = sys.argv[:i] + sys.argv[i+2:]
break
2011-12-15 05:42:35 +04:00
if header: # header or such
if DEBUG: print >> sys.stderr, 'Just copy.'
shutil.copy(sys.argv[-1], sys.argv[-2])
exit(0)
2011-12-15 23:01:56 +04:00
if TEMP_DIR:
temp_dir = TEMP_DIR
2011-12-16 06:39:03 +04:00
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir) # clear it
os.makedirs(temp_dir)
2011-12-15 23:01:56 +04:00
else:
temp_dir = tempfile.mkdtemp()
2011-12-15 05:42:35 +04:00
def in_temp(name):
return os.path.join(temp_dir, name)
try:
2011-12-12 03:24:04 +04:00
call = CXX if use_cxx else CC
2011-12-13 01:35:51 +04:00
## Parse args
2011-12-12 03:24:04 +04:00
newargs = sys.argv[1:]
2011-12-13 01:35:51 +04:00
opt_level = 0
2011-12-15 09:17:53 +04:00
llvm_opt_level = None
closure = None
2012-01-05 02:36:02 +04:00
js_transform = None
2012-01-13 06:25:01 +04:00
compress_whitespace = None
2011-12-15 09:17:53 +04:00
def check_bad_eq(arg):
assert '=' not in arg, 'Invalid parameter (do not use "=" with "--" options)'
2011-12-13 01:35:51 +04:00
2011-12-12 03:24:04 +04:00
for i in range(len(newargs)):
if newargs[i].startswith('-O'):
2011-12-13 01:35:51 +04:00
try:
opt_level = int(newargs[i][2])
assert 0 <= opt_level <= 3
except:
raise Exception('Invalid optimization level: ' + newargs[i])
2011-12-12 03:24:04 +04:00
newargs[i] = ''
2011-12-14 02:08:44 +04:00
elif newargs[i].startswith('--llvm-opts'):
2011-12-15 09:17:53 +04:00
check_bad_eq(newargs[i])
2011-12-14 02:08:44 +04:00
llvm_opt_level = eval(newargs[i+1])
2011-12-14 04:21:03 +04:00
assert 0 <= llvm_opt_level <= 1, 'Only two levels of LLVM optimizations are supported so far, 0 (none) and 1 (safe)'
2011-12-14 02:08:44 +04:00
newargs[i] = ''
newargs[i+1] = ''
2011-12-15 09:17:53 +04:00
elif newargs[i].startswith('--closure'):
check_bad_eq(newargs[i])
closure = int(newargs[i+1])
newargs[i] = ''
newargs[i+1] = ''
2012-01-05 02:36:02 +04:00
elif newargs[i].startswith('--js-transform'):
check_bad_eq(newargs[i])
js_transform = newargs[i+1]
newargs[i] = ''
newargs[i+1] = ''
2012-01-13 06:25:01 +04:00
elif newargs[i].startswith('--compress'):
check_bad_eq(newargs[i])
compress_whitespace = int(newargs[i+1])
newargs[i] = ''
newargs[i+1] = ''
2011-12-17 23:49:26 +04:00
elif newargs[i] == '-MF': # clang cannot handle this, so we fake it
f = open(newargs[i+1], 'w')
f.write('\n')
f.close()
newargs[i] = ''
newargs[i+1] = ''
2011-12-13 06:07:07 +04:00
newargs = [ arg for arg in newargs if arg is not '' ]
2011-12-15 09:17:53 +04:00
if llvm_opt_level is None: llvm_opt_level = 1 if opt_level >= 1 else 0
if closure is None: closure = 1 if opt_level >= 2 else 0
2012-01-13 06:25:01 +04:00
if compress_whitespace is None:
compress_whitespace = closure # if closure is run, compress whitespace
2011-12-15 09:17:53 +04:00
2011-12-18 23:34:19 +04:00
if closure:
2011-12-20 03:37:47 +04:00
assert os.path.exists(shared.CLOSURE_COMPILER), 'emcc: fatal: Closure compiler (%s) does not exist' % shared.CLOSURE_COMPILER
2011-12-18 23:34:19 +04:00
2011-12-13 06:07:07 +04:00
settings_changes = []
for i in range(len(newargs)):
if newargs[i] == '-s':
2011-12-17 00:36:12 +04:00
assert '=' in newargs[i+1], 'Incorrect syntax for -s (use -s OPT=VAL): ' + newargs[i+1]
2011-12-13 06:07:07 +04:00
settings_changes.append(newargs[i+1])
newargs[i] = newargs[i+1] = ''
2011-12-13 22:09:59 +04:00
elif newargs[i].startswith('--typed-arrays'):
assert '=' not in newargs[i], 'Invalid typed arrays parameter (do not use "=")'
settings_changes.append('USE_TYPED_ARRAYS=' + newargs[i+1])
newargs[i] = ''
newargs[i+1] = ''
2011-12-13 06:07:07 +04:00
newargs = [ arg for arg in newargs if arg is not '' ]
2011-12-14 04:21:03 +04:00
input_files = []
2011-12-15 06:46:48 +04:00
has_source_inputs = False
2011-12-14 04:21:03 +04:00
for i in range(len(newargs)): # find input files XXX this a simple heuristic. we should really analyze based on a full understanding of gcc params,
# right now we just assume that what is left contains no more |-x OPT| things
arg = newargs[i]
2011-12-17 06:14:02 +04:00
if arg.endswith(SOURCE_SUFFIXES + BITCODE_SUFFIXES): # we already removed -o <target>, so all these should be inputs
2011-12-14 06:23:00 +04:00
newargs[i] = ''
2011-12-17 09:55:39 +04:00
if os.path.exists(arg):
if arg.endswith(SOURCE_SUFFIXES):
2011-12-17 23:49:26 +04:00
input_files.append(arg)
2011-12-17 09:55:39 +04:00
has_source_inputs = True
2011-12-17 23:49:26 +04:00
else:
# this should be bitcode, make sure it is valid
if arg.endswith('.ll') or shared.Building.is_bitcode(arg):
input_files.append(arg)
else:
print >> sys.stderr, 'emcc: %s: Not valid LLVM bitcode' % arg
2011-12-17 09:55:39 +04:00
else:
print >> sys.stderr, 'emcc: %s: No such file or directory' % arg
2011-12-14 06:23:00 +04:00
newargs = [ arg for arg in newargs if arg is not '' ]
2011-12-14 04:21:03 +04:00
2011-12-17 09:55:39 +04:00
assert len(input_files) > 0, 'emcc: no input files'
2011-12-14 04:21:03 +04:00
2011-12-13 06:07:07 +04:00
newargs += CC_ADDITIONAL_ARGS
2011-11-21 09:02:46 +04:00
2011-12-14 04:21:03 +04:00
specified_target = target
2011-12-14 22:50:22 +04:00
target = specified_target if specified_target is not None else 'a.out.js' # specified_target is the user-specified one, target is what we will generate
2011-12-12 03:24:04 +04:00
2011-12-14 04:21:03 +04:00
target_basename = unsuffixed_basename(target)
2011-12-12 08:43:08 +04:00
2011-12-15 06:46:48 +04:00
# -c means do not link in gcc, and for us, the parallel is to not go all the way to JS, but stop at bitcode
has_dash_c = '-c' in newargs
if has_dash_c:
assert has_source_inputs, 'Must have source code inputs to use -c'
2011-12-15 05:01:16 +04:00
target = target_basename + '.o'
2011-12-12 08:43:08 +04:00
2012-01-05 06:14:18 +04:00
if '.' in target:
final_suffix = target.split('.')[-1]
else:
final_suffix = ''
2011-12-12 03:24:04 +04:00
2011-12-14 00:20:45 +04:00
# Apply optimization level settings
2011-12-21 04:38:14 +04:00
shared.Settings.apply_opt_level(opt_level, noisy=True)
2011-12-14 00:20:45 +04:00
2011-12-17 00:36:12 +04:00
# Apply -s settings in newargs here (after optimization levels, so they can override them)
for change in settings_changes:
key, value = change.split('=')
exec('shared.Settings.' + key + ' = ' + value)
2011-12-14 06:23:00 +04:00
## Compile source code to bitcode
2011-12-13 01:35:51 +04:00
2011-12-22 03:47:38 +04:00
if DEBUG: print >> sys.stderr, 'emcc: compiling to bitcode'
2011-12-15 06:12:22 +04:00
2011-12-14 04:21:03 +04:00
# First, generate LLVM bitcode. For each input file, we get base.o with bitcode
2011-12-14 06:23:00 +04:00
for input_file in input_files:
2011-12-15 08:15:18 +04:00
if input_file.endswith(SOURCE_SUFFIXES):
2011-12-22 04:48:37 +04:00
if DEBUG: print >> sys.stderr, 'emcc: compiling source file: ', input_file
2011-12-20 04:01:01 +04:00
output_file = in_temp(unsuffixed_basename(input_file) + '.o')
args = newargs + ['-emit-llvm', '-c', input_file, '-o', output_file]
2011-12-15 05:42:35 +04:00
if DEBUG: print >> sys.stderr, "emcc running:", call, ' '.join(args)
2011-12-20 04:01:01 +04:00
Popen([call] + args).communicate() # let compiler frontend print directly, so colors are saved (PIPE kills that)
if not os.path.exists(output_file):
print >> sys.stderr, 'emcc: compiler frontend failed to generate LLVM bitcode, halting'
sys.exit(1)
2011-12-17 06:14:02 +04:00
else: # bitcode
if input_file.endswith(('.bc', '.o')):
2011-12-22 04:48:37 +04:00
if DEBUG: print >> sys.stderr, 'emcc: copying bitcode file: ', input_file
2011-12-17 06:14:02 +04:00
shutil.copyfile(input_file, in_temp(unsuffixed_basename(input_file) + '.o'))
else: #.ll
2011-12-22 03:47:38 +04:00
if not LEAVE_INPUTS_RAW:
2012-01-22 05:16:50 +04:00
# Note that by assembling the .ll file, then disassembling it later, we will
# remove annotations which is a good thing for compilation time
2011-12-22 04:48:37 +04:00
if DEBUG: print >> sys.stderr, 'emcc: assembling assembly file: ', input_file
2011-12-22 03:47:38 +04:00
shared.Building.llvm_as(input_file, in_temp(unsuffixed_basename(input_file) + '.o'))
2011-12-12 03:24:04 +04:00
# If we were just asked to generate bitcode, stop there
2011-12-15 06:12:22 +04:00
if final_suffix not in ['js', 'html']:
2012-01-08 06:36:42 +04:00
if llvm_opt_level > 0:
print >> sys.stderr, 'emcc: warning: -Ox flags ignored, since not generating JavaScript'
2011-12-15 05:42:35 +04:00
if not specified_target:
2011-12-14 04:21:03 +04:00
for input_file in input_files:
2011-12-15 05:42:35 +04:00
shutil.move(in_temp(unsuffixed_basename(input_file) + '.o'), unsuffixed_basename(input_file) + '.' + final_suffix)
else:
2011-12-15 06:46:48 +04:00
if len(input_files) == 1:
shutil.move(in_temp(unsuffixed_basename(input_files[0]) + '.o'), specified_target)
else:
2011-12-17 00:36:12 +04:00
assert not has_dash_c, 'fatal error: cannot specify -o with -c with multiple files' + str(sys.argv)
2011-12-15 06:46:48 +04:00
# We have a specified target (-o <target>), which is not JavaScript or HTML, and
2011-12-17 07:08:30 +04:00
# we have multiple files: Link them TODO: llvm link-time opts?
ld_args = map(lambda input_file: in_temp(unsuffixed_basename(input_file) + '.o'), input_files) + \
['-o', specified_target]
#[arg.split('-Wl,')[1] for arg in filter(lambda arg: arg.startswith('-Wl,'), sys.argv)]
if DEBUG: print >> sys.stderr, 'emcc: link: ' + str(ld_args)
Popen([shared.LLVM_LINK] + ld_args).communicate()
2011-12-12 03:24:04 +04:00
exit(0)
2011-12-13 01:35:51 +04:00
## Continue on to create JavaScript
2012-01-08 06:36:42 +04:00
if DEBUG: print >> sys.stderr, 'emcc: will generate JavaScript'
2011-12-15 06:12:22 +04:00
2011-12-17 00:36:12 +04:00
extra_files_to_link = []
2011-12-22 03:47:38 +04:00
if not LEAVE_INPUTS_RAW:
2012-01-18 06:41:30 +04:00
# Check if we need to include some libraries that we compile. (We implement libc ourselves in js, but
# compile a malloc implementation and stdlibc++.)
# Note that we assume a single symbol is enough to know if we have/do not have dlmalloc etc. If you
2011-12-22 03:47:38 +04:00
# include just a few symbols but want the rest, this will not work.
2012-01-18 07:05:12 +04:00
# dlmalloc
2012-01-18 06:41:30 +04:00
def create_dlmalloc():
2012-01-19 09:02:12 +04:00
if DEBUG: print >> sys.stderr, 'emcc: building dlmalloc for cache'
2012-01-19 07:39:01 +04:00
Popen([shared.EMCC, shared.path_from_root('system', 'lib', 'dlmalloc.c'), '-g', '-o', in_temp('dlmalloc.o')], stdout=PIPE, stderr=PIPE).communicate()
2012-01-19 09:02:12 +04:00
# we include the libc++ new stuff here, so that the common case of using just new/delete is quick to link
Popen([shared.EMXX, shared.path_from_root('system', 'lib', 'libcxx', 'new.cpp'), '-g', '-o', in_temp('new.o')], stdout=PIPE, stderr=PIPE).communicate()
shared.Building.link([in_temp('dlmalloc.o'), in_temp('new.o')], in_temp('dlmalloc_full.o'))
return in_temp('dlmalloc_full.o')
2012-01-18 06:41:30 +04:00
def fix_dlmalloc():
2011-12-22 03:47:38 +04:00
# dlmalloc needs some sign correction. # If we are in mode 0, switch to 2. We will add our lines
try:
if shared.Settings.CORRECT_SIGNS == 0: raise Exception('we need to change to 2')
except: # we fail if equal to 0 - so we need to switch to 2 - or if CORRECT_SIGNS is not even in Settings
shared.Settings.CORRECT_SIGNS = 2
if shared.Settings.CORRECT_SIGNS == 2:
shared.Settings.CORRECT_SIGNS_LINES = [shared.path_from_root('src', 'dlmalloc.c') + ':' + str(i+4) for i in [4816, 4191, 4246, 4199, 4205, 4235, 4227]]
# If we are in mode 1, we are correcting everything anyhow. If we are in mode 3, we will be corrected
# so all is well anyhow too.
2012-01-19 23:26:56 +04:00
# XXX We also need to add libc symbols that use malloc, for example strdup. It's very rare to use just them and not
# a normal malloc symbol (like free, after calling strdup), so we haven't hit this yet, but it is possible.
2012-01-19 09:02:12 +04:00
dlmalloc_symbols = open(shared.path_from_root('system', 'lib', 'dlmalloc.symbols')).read().split('\n')
2012-01-18 06:41:30 +04:00
2012-01-18 07:05:12 +04:00
# libcxx
def create_libcxx():
2012-01-19 09:02:12 +04:00
if DEBUG: print >> sys.stderr, 'emcc: building libcxx for cache'
2012-01-18 23:35:00 +04:00
shared.Building.build_library('libcxx', shared.EMSCRIPTEN_TEMP_DIR, shared.EMSCRIPTEN_TEMP_DIR, ['libcxx.bc'], configure=None, copy_project=True, source_dir=shared.path_from_root('system', 'lib', 'libcxx'))
return os.path.join(shared.EMSCRIPTEN_TEMP_DIR, 'libcxx', 'libcxx.bc')
2012-01-18 07:05:12 +04:00
def fix_libcxx():
2012-01-22 02:01:42 +04:00
assert shared.Settings.QUANTUM_SIZE == 4, 'We do not support libc++ with QUANTUM_SIZE == 1'
2012-01-21 05:56:59 +04:00
# libcxx might need corrections, so turn them all on. TODO: check which are actually needed
shared.Settings.CORRECT_SIGNS = shared.Settings.CORRECT_OVERFLOWS = shared.Settings.CORRECT_ROUNDINGS = 1
print >> sys.stderr, 'emcc: warning: using libcxx turns on CORRECT_* options'
2012-01-18 22:40:29 +04:00
libcxx_symbols = map(lambda line: line.strip().split(' ')[1], open(shared.path_from_root('system', 'lib', 'libcxx', 'symbols')).readlines())
libcxx_symbols = filter(lambda symbol: symbol not in dlmalloc_symbols, libcxx_symbols)
libcxx_symbols = set(libcxx_symbols)
2012-01-18 07:05:12 +04:00
2012-01-19 09:24:11 +04:00
force = False # If we have libcxx, we must force inclusion of dlmalloc, since libcxx uses new internally. Note: this is kind of hacky
for name, create, fix, library_symbols in [('libcxx', create_libcxx, fix_libcxx, libcxx_symbols),
('dlmalloc', create_dlmalloc, fix_dlmalloc, dlmalloc_symbols)]:
2012-01-22 02:12:18 +04:00
need = []
has = []
2012-01-18 06:41:30 +04:00
for input_file in input_files:
symbols = shared.Building.llvm_nm(in_temp(unsuffixed_basename(input_file) + '.o'))
2012-01-18 21:53:48 +04:00
for library_symbol in library_symbols:
if library_symbol in symbols.undefs:
2012-01-22 02:12:18 +04:00
need.append(library_symbol)
2012-01-18 21:53:48 +04:00
if library_symbol in symbols.defs:
2012-01-22 02:12:18 +04:00
has.append(library_symbol)
if DEBUG: print >> sys.stderr, 'emcc: considering including %s: we need |%s| and have |%s|' % (name, str(need), str(has))
2012-01-19 09:24:11 +04:00
if force or (need and not has):
2012-01-18 06:41:30 +04:00
# We need to build and link the library in
if DEBUG: print >> sys.stderr, 'emcc: including %s' % name
extra_files_to_link.append(shared.Cache.get(name, create))
2012-01-19 09:24:11 +04:00
force = True
2012-01-18 06:53:57 +04:00
if fix:
fix()
2011-12-17 00:36:12 +04:00
2011-12-14 04:21:03 +04:00
# First, combine the bitcode files if there are several
2011-12-17 00:36:12 +04:00
if len(input_files) + len(extra_files_to_link) > 1:
2011-12-22 04:48:37 +04:00
linker_inputs = map(lambda input_file: in_temp(unsuffixed_basename(input_file) + '.o'), input_files) + extra_files_to_link
if DEBUG: print >> sys.stderr, 'emcc: linking: ', linker_inputs
shared.Building.link(linker_inputs,
2011-12-17 00:36:12 +04:00
in_temp(target_basename + '.bc'))
2011-12-17 07:08:30 +04:00
# TODO: LLVM link-time opts? here and/or elsewhere?
2011-12-14 04:21:03 +04:00
else:
2011-12-22 03:47:38 +04:00
if not LEAVE_INPUTS_RAW:
shutil.move(in_temp(unsuffixed_basename(input_files[0]) + '.o'), in_temp(target_basename + '.bc'))
2011-12-14 04:21:03 +04:00
2012-01-08 06:36:42 +04:00
# Optimize, if asked to
if llvm_opt_level > 0 and not LEAVE_INPUTS_RAW:
if DEBUG: print >> sys.stderr, 'emcc: LLVM opts'
shared.Building.llvm_opt(in_temp(target_basename + '.bc'), LLVM_INTERNAL_OPT_LEVEL, safe=llvm_opt_level < 2)
2012-01-19 00:31:45 +04:00
else:
# If possible, remove dead functions etc., this potentially saves a lot in the size of the generated code (and the time to compile it)
2012-01-19 06:10:57 +04:00
if not LEAVE_INPUTS_RAW and not shared.Settings.BUILD_AS_SHARED_LIB and not shared.Settings.LINKABLE:
2012-01-19 00:31:45 +04:00
if DEBUG: print >> sys.stderr, 'emcc: LLVM dead globals elimination'
shared.Building.llvm_opt(in_temp(target_basename + '.bc'), ['-internalize', '-globaldce'])
2012-01-08 06:36:42 +04:00
2011-12-17 00:36:12 +04:00
# Emscripten
2011-12-23 05:02:29 +04:00
try:
if shared.Settings.RELOOP:
2012-01-08 06:36:42 +04:00
print >> sys.stderr, 'emcc: warning: The relooper optimization can be very slow.'
2011-12-23 05:02:29 +04:00
except:
pass
2011-12-15 09:17:53 +04:00
2012-01-01 23:17:49 +04:00
if DEBUG:
print >> sys.stderr, 'emcc: saving intermediate processing steps to %s' % shared.EMSCRIPTEN_TEMP_DIR
intermediate_counter = 0
2012-01-05 02:39:56 +04:00
def save_intermediate(name=None, suffix='js'):
2012-01-01 23:17:49 +04:00
global intermediate_counter
2012-01-05 02:39:56 +04:00
shutil.copyfile(final, os.path.join(shared.EMSCRIPTEN_TEMP_DIR, 'emcc-%d%s.%s' % (intermediate_counter, '' if name is None else '-' + name, suffix)))
2012-01-01 23:17:49 +04:00
intermediate_counter += 1
2011-12-22 03:47:38 +04:00
if not LEAVE_INPUTS_RAW:
2012-01-01 23:17:49 +04:00
final = in_temp(target_basename + '.bc')
2012-01-19 00:31:45 +04:00
if DEBUG: save_intermediate('bc', 'bc')
2012-01-02 08:55:55 +04:00
final = shared.Building.llvm_dis(final, final + '.ll')
2011-12-22 03:47:38 +04:00
else:
assert len(input_files) == 1
2012-01-01 23:17:49 +04:00
final = input_files[0]
2012-01-22 05:16:50 +04:00
if DEBUG: save_intermediate('ll', 'll')
2012-01-01 23:17:49 +04:00
2012-01-22 05:16:50 +04:00
if DEBUG: print >> sys.stderr, 'emcc: LLVM => JS'
2012-01-01 23:17:49 +04:00
final = shared.Building.emscripten(final, append_ext=False)
2012-01-02 08:55:55 +04:00
if DEBUG: save_intermediate('original')
2011-12-12 03:24:04 +04:00
2011-12-21 06:49:42 +04:00
# Apply a source code transformation, if requested
2012-01-05 02:36:02 +04:00
if js_transform:
2011-12-21 06:49:42 +04:00
shutil.copyfile(final, final + '.tr.js')
final += '.tr.js'
2012-01-05 02:36:02 +04:00
if DEBUG: print >> sys.stderr, 'emcc: applying transform: %s' % js_transform
Popen(js_transform.split(' ') + [os.path.abspath(final)]).communicate()
2012-01-02 08:55:55 +04:00
if DEBUG: save_intermediate('transformed')
2011-12-23 05:59:33 +04:00
2012-01-16 08:40:28 +04:00
# It is useful to run several js optimizer passes together, to save on unneeded unparsing/reparsing
js_optimizer_queue = []
def flush_js_optimizer_queue():
global final, js_optimizer_queue
if len(js_optimizer_queue) > 0:
if not DEBUG:
final = shared.Building.js_optimizer(final, js_optimizer_queue)
else:
for name in js_optimizer_queue:
print >> sys.stderr, 'emcc: applying js optimization pass:', name
final = shared.Building.js_optimizer(final, [name])
save_intermediate(name)
js_optimizer_queue = []
2011-12-15 05:42:35 +04:00
if opt_level >= 1:
2011-12-15 06:12:22 +04:00
if DEBUG: print >> sys.stderr, 'emcc: running pre-closure post-opts'
2012-01-02 01:59:09 +04:00
2012-01-16 08:40:28 +04:00
if DEBUG:
# Clean up the syntax a bit
final = shared.Building.js_optimizer(final, [])
if DEBUG: save_intermediate('pretty')
2012-01-02 08:55:55 +04:00
2012-01-02 01:59:09 +04:00
if shared.Settings.RELOOP:
2012-01-16 08:40:28 +04:00
js_optimizer_queue += ['hoistMultiples', 'loopOptimizer']
flush_js_optimizer_queue()
2011-12-12 03:24:04 +04:00
2011-12-15 05:42:35 +04:00
# eliminator
2012-01-16 08:40:28 +04:00
if DEBUG: print >> sys.stderr, 'emcc: running variable eliminator'
2011-12-15 05:42:35 +04:00
final = shared.Building.eliminator(final)
2011-12-31 02:59:57 +04:00
if DEBUG: save_intermediate('eliminator')
2011-12-15 05:42:35 +04:00
2011-12-16 06:39:03 +04:00
# js optimizer pre-pass
2012-01-16 08:40:28 +04:00
js_optimizer_queue += ['simplifyExpressionsPre', 'optimizeShiftsConservative']
###js_optimizer_queue += ['optimizeShiftsAggressive']
###final = shared.Building.eliminator(final) # aggressive shifts optimization introduces some new variables, remove ones that we can
###if DEBUG: save_intermediate('eliminator')
2011-12-16 06:39:03 +04:00
2011-12-15 09:17:53 +04:00
if closure:
2012-01-16 08:40:28 +04:00
flush_js_optimizer_queue()
2011-12-15 06:12:22 +04:00
if DEBUG: print >> sys.stderr, 'emcc: running closure'
2011-12-15 05:42:35 +04:00
final = shared.Building.closure_compiler(final)
2011-12-29 02:41:26 +04:00
if DEBUG: save_intermediate('closure')
2011-12-15 05:42:35 +04:00
if opt_level >= 1:
2011-12-16 06:39:03 +04:00
# js optimizer post-pass
2011-12-15 06:12:22 +04:00
if DEBUG: print >> sys.stderr, 'emcc: running post-closure post-opts'
2012-01-16 08:40:28 +04:00
js_optimizer_queue += ['simplifyExpressionsPost']
2011-12-15 05:42:35 +04:00
2012-01-13 06:25:01 +04:00
if compress_whitespace:
2012-01-16 08:40:28 +04:00
js_optimizer_queue += ['compress']
flush_js_optimizer_queue()
2012-01-13 06:25:01 +04:00
2011-12-15 05:42:35 +04:00
# If we were asked to also generate HTML, do that
if final_suffix == 'html':
2011-12-15 06:12:22 +04:00
if DEBUG: print >> sys.stderr, 'emcc: generating HTML'
2011-12-15 05:42:35 +04:00
shell = open(shared.path_from_root('src', 'shell.html')).read()
2012-01-06 06:11:31 +04:00
html = open(target, 'w')
2011-12-15 05:42:35 +04:00
html.write(shell.replace('{{{ SCRIPT_CODE }}}', open(final).read()))
html.close()
else:
# copy final JS to output
2012-01-06 06:11:31 +04:00
shutil.move(final, target)
2011-12-15 05:42:35 +04:00
finally:
2011-12-15 23:01:56 +04:00
if not TEMP_DIR:
2011-12-15 05:42:35 +04:00
try:
shutil.rmtree(temp_dir)
except:
pass
else:
print >> sys.stderr, 'emcc saved files are in:', temp_dir
2011-12-12 03:24:04 +04:00