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.
|
|
|
|
|
|
|
|
Tell your build system to use this instead of the compiler, linker, ar and
|
|
|
|
ranlib. All the normal build commands will be sent to this script, which
|
|
|
|
will proxy them to the appropriate LLVM build commands, in order to
|
|
|
|
generate proper code for Emscripten to later process.
|
|
|
|
|
|
|
|
For example, compilation will be translated into calls to clang
|
|
|
|
with -emit-llvm, and linking will be translated into calls to llvm-link,
|
|
|
|
and so forth.
|
|
|
|
|
|
|
|
emcc is only meant to *COMPILE* source code into LLVM bitcode. It does
|
|
|
|
not do optimizations (in fact, it will disable -Ox flags and warn you
|
|
|
|
about that). The reason is that doing such optimizations early can lead
|
|
|
|
to bitcode that Emscripten cannot process properly, or can process but
|
|
|
|
not fully optimize. You can (and probably should) still run LLVM
|
|
|
|
optimizations though, by telling emscripten.py to do so (or run LLVM
|
|
|
|
opt yourself, but be careful with the parameters you pass).
|
|
|
|
|
|
|
|
Example uses:
|
|
|
|
|
|
|
|
* For configure, instead of ./configure, cmake, etc., run emconfiguren.py
|
|
|
|
with that command as an argument, for example
|
|
|
|
|
|
|
|
emconfiguren.py ./configure [options]
|
|
|
|
|
|
|
|
emconfiguren.py is a tiny script that just sets some environment vars
|
|
|
|
as a convenience. The command just shown is equivalent to
|
|
|
|
|
2011-12-12 08:43:08 +04:00
|
|
|
EMMAKEN_JUST_CONFIGURE=1 RANLIB=PATH/emcc AR=PATH/emcc 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++")
|
|
|
|
SET(CMAKE_LINKER "PATH/emcc")
|
|
|
|
SET(CMAKE_CXX_LINKER "PATH/emcc")
|
|
|
|
SET(CMAKE_C_LINK_EXECUTABLE "PATH/emcc")
|
|
|
|
SET(CMAKE_CXX_LINK_EXECUTABLE "PATH/emcc")
|
|
|
|
SET(CMAKE_AR "PATH/emcc")
|
|
|
|
SET(CMAKE_RANLIB "PATH/emcc")
|
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
|
|
|
|
|
|
|
|
After setting that up, run your build system normally. It should generate
|
|
|
|
LLVM instead of the normal output, and end up with .ll files that you can
|
|
|
|
give to Emscripten. Note that this tool doesn't run Emscripten itself. Note
|
|
|
|
also that you may need to do some manual fiddling later, for example to
|
|
|
|
link files that weren't linked, and them llvm-dis them.
|
|
|
|
|
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-12 03:24:04 +04:00
|
|
|
import os, sys, shutil
|
|
|
|
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-12 03:24:04 +04:00
|
|
|
DEBUG = 1
|
2011-11-23 09:34:03 +04:00
|
|
|
|
2011-11-21 09:02:46 +04:00
|
|
|
################### XXX
|
2011-12-11 22:23:03 +04:00
|
|
|
print >> sys.stderr, '\n***This is a WORK IN PROGRESS***'
|
|
|
|
print >> sys.stderr, '***[%s]***\n' % str(sys.argv)
|
2011-11-21 09:02:46 +04:00
|
|
|
################### XXX
|
|
|
|
|
2011-12-12 08:43:08 +04:00
|
|
|
if DEBUG: print >> sys.stderr, 'emcc: ', ' '.join(sys.argv)
|
2011-11-21 09:02:46 +04:00
|
|
|
|
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:
|
|
|
|
-O0 [..] default
|
|
|
|
-OX TODO
|
|
|
|
-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 typed arrays (default)
|
|
|
|
--llvm-opts <mode> 0: none (default)
|
|
|
|
1: safe/portable
|
|
|
|
2: unsafe/unportable
|
|
|
|
|
|
|
|
The target file, if specified (-o <target>), defines what will
|
|
|
|
be generated:
|
2011-12-11 23:54:22 +04:00
|
|
|
|
2011-12-11 23:39:00 +04:00
|
|
|
<name>.js JavaScript (default)
|
|
|
|
<name>.o LLVM bitcode
|
|
|
|
<name>.bc LLVM bitcode
|
|
|
|
<name>.html HTML with embedded JavaScript
|
|
|
|
|
2011-12-11 23:54:22 +04:00
|
|
|
The -c option (which tells gcc not to run the linker) will
|
|
|
|
also cause LLVM bitcode to be generated, as %s only generates
|
|
|
|
JavaScript in the final linking stage of building.
|
|
|
|
|
|
|
|
''' % (this, this, this)
|
2011-11-23 09:34:03 +04:00
|
|
|
exit(0)
|
|
|
|
|
2011-11-21 09:02:46 +04:00
|
|
|
# If this is a configure-type thing, just do that
|
|
|
|
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:
|
|
|
|
compiler = 'g++' if 'CXXCompiler' in ' '.join(sys.argv) or os.environ.get('EMMAKEN_CXX') else 'gcc'
|
|
|
|
cmd = [compiler] + EMSDK_OPTS + sys.argv[1:]
|
2011-12-12 08:43:08 +04:00
|
|
|
if DEBUG: print >> sys.stderr, 'emcc, just configuring: ', 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']?
|
|
|
|
ALLOWED_LINK_ARGS = ['-f', '-help', '-o', '-print-after', '-print-after-all', '-print-before',
|
|
|
|
'-print-before-all', '-time-passes', '-v', '-verify-dom-info', '-version' ]
|
|
|
|
TWO_PART_DISALLOWED_LINK_ARGS = ['-L'] # Ignore thingsl like |-L .|
|
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-12 03:24:04 +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
|
|
|
|
use_linker = 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
|
|
|
opts = []
|
|
|
|
files = []
|
|
|
|
for i in range(1, len(sys.argv)):
|
|
|
|
arg = sys.argv[i]
|
|
|
|
if arg.startswith('-'):
|
|
|
|
opts.append(arg)
|
|
|
|
else:
|
|
|
|
files.append(arg)
|
|
|
|
if arg.endswith('.c'):
|
|
|
|
use_cxx = False
|
|
|
|
if arg.endswith(('.c', '.cc', '.cpp', '.dT')):
|
|
|
|
use_linker = False
|
|
|
|
if arg.endswith('.h') and sys.argv[i-1] != '-include':
|
|
|
|
header = True
|
|
|
|
use_linker = False
|
|
|
|
|
|
|
|
if '--version' in opts:
|
|
|
|
use_linker = False
|
|
|
|
|
|
|
|
use_compiler = not use_linker and not header
|
|
|
|
|
|
|
|
if set(sys.argv[1]).issubset(set('-cruqs')): # ar
|
|
|
|
sys.argv = sys.argv[:1] + sys.argv[3:] + ['-o='+sys.argv[2]]
|
|
|
|
assert use_linker, 'Linker should be used in this case'
|
|
|
|
|
|
|
|
# Check if a target is specified
|
|
|
|
target = None
|
|
|
|
for i in range(len(sys.argv)-1):
|
|
|
|
if sys.argv[i] == '-o':
|
|
|
|
target = sys.argv[i+1]
|
|
|
|
sys.argv = sys.argv[:i] + sys.argv[i+2:]
|
|
|
|
break
|
|
|
|
|
|
|
|
if use_linker:
|
|
|
|
call = LLVM_LD
|
|
|
|
newargs = ['-disable-opt']
|
|
|
|
i = 0
|
|
|
|
while i < len(sys.argv)-1:
|
|
|
|
i += 1
|
2011-11-21 09:02:46 +04:00
|
|
|
arg = sys.argv[i]
|
|
|
|
if arg.startswith('-'):
|
2011-12-12 03:24:04 +04:00
|
|
|
prefix = arg.split('=')[0]
|
|
|
|
if prefix in ALLOWED_LINK_ARGS:
|
2011-11-21 09:02:46 +04:00
|
|
|
newargs.append(arg)
|
2011-12-12 03:24:04 +04:00
|
|
|
if arg in TWO_PART_DISALLOWED_LINK_ARGS:
|
|
|
|
i += 1
|
|
|
|
elif arg.endswith('.so'):
|
|
|
|
continue # .so's do not exist yet, in many cases
|
|
|
|
else:
|
|
|
|
# not option, so just append
|
|
|
|
newargs.append(arg)
|
|
|
|
if target:
|
|
|
|
newargs.append('-o=' + target)
|
|
|
|
|
|
|
|
if DEBUG: print >> sys.stderr, "Running:", call, ' '.join(newargs)
|
|
|
|
Popen([call] + newargs).communicate()
|
|
|
|
exit(0)
|
|
|
|
|
|
|
|
elif use_compiler:
|
|
|
|
call = CXX if use_cxx else CC
|
|
|
|
newargs = sys.argv[1:]
|
|
|
|
for i in range(len(newargs)):
|
|
|
|
if newargs[i].startswith('-O'):
|
2011-12-12 08:43:08 +04:00
|
|
|
if DEBUG: print >> sys.stderr, 'emcc: WARNING: Optimization flags (-Ox) are ignored in emcc. Tell emscripten.py to do that, or run LLVM opt.'
|
2011-12-12 03:24:04 +04:00
|
|
|
newargs[i] = ''
|
|
|
|
newargs = [ arg for arg in newargs if arg is not '' ] + CC_ADDITIONAL_ARGS
|
2011-11-21 09:02:46 +04:00
|
|
|
|
2011-12-12 03:24:04 +04:00
|
|
|
if target is None:
|
|
|
|
# No explicit -o specified, so do the most natural thing, compile to .js
|
|
|
|
target = 'a.out.js'
|
|
|
|
|
|
|
|
target_basename = '.'.join(target.split('.')[:-1])
|
2011-12-12 08:43:08 +04:00
|
|
|
|
|
|
|
if '-c' in newargs: # -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
|
|
|
|
target = target_basename + '.bc'
|
|
|
|
|
2011-12-12 03:24:04 +04:00
|
|
|
final_suffix = target.split('.')[-1]
|
|
|
|
|
|
|
|
# First, generate LLVM bitcode TODO: handle |emcc a.cpp b.cpp -c| which generate *two* bitcode files
|
|
|
|
newargs = newargs + ['-emit-llvm', '-c', '-o', target_basename + '.bc']
|
2011-11-21 09:02:46 +04:00
|
|
|
|
2011-11-23 09:34:03 +04:00
|
|
|
if DEBUG: print >> sys.stderr, "Running:", call, ' '.join(newargs)
|
2011-12-12 03:24:04 +04:00
|
|
|
Popen([call] + newargs).communicate()
|
|
|
|
|
|
|
|
# If we were just asked to generate bitcode, stop there
|
|
|
|
if final_suffix in ['o', 'bc']:
|
|
|
|
if final_suffix == 'o':
|
|
|
|
shutil.move(target_basename + '.bc', target_basename + '.o')
|
|
|
|
exit(0)
|
|
|
|
|
|
|
|
# Continue on to create JavaScript
|
|
|
|
temp_files = shared.TempFiles()
|
|
|
|
temp_files.note(target_basename + '.bc')
|
|
|
|
try:
|
|
|
|
shared.Building.emscripten(target_basename + '.bc', append_ext=False)
|
|
|
|
shutil.move(target_basename + '.bc.o.js', target_basename + '.js')
|
|
|
|
|
2011-12-12 23:24:16 +04:00
|
|
|
# If we were asked to also generate HTML, do that
|
|
|
|
if final_suffix == 'html':
|
|
|
|
shell = open(shared.path_from_root('src', 'shell.html')).read()
|
|
|
|
html = open(target_basename + '.html', 'w')
|
|
|
|
html.write(shell.replace('{{{ SCRIPT_CODE }}}', open(target_basename + '.js').read()))
|
|
|
|
html.close()
|
|
|
|
temp_files.note(target_basename + '.js')
|
|
|
|
|
2011-12-12 03:24:04 +04:00
|
|
|
finally:
|
|
|
|
temp_files.clean()
|
|
|
|
|
|
|
|
exit(0)
|
|
|
|
|
|
|
|
else: # header or such
|
|
|
|
if DEBUG: print >> sys.stderr, 'Just copy.'
|
|
|
|
shutil.copy(sys.argv[-1], sys.argv[-2])
|
|
|
|
exit(0)
|
|
|
|
|
2011-11-21 09:02:46 +04:00
|
|
|
|
|
|
|
|