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
2012-08-23 08:03:07 +04:00
use emar, emranlib etc. 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++")
2012-08-23 08:03:07 +04:00
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")
2011-12-14 23:12:19 +04:00
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'))
2012-01-31 02:10:57 +04:00
__rootpath__ = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
2011-11-21 09:02:46 +04:00
def path_from_root(*pathelems):
return os.path.join(__rootpath__, *pathelems)
2012-01-31 02:10:57 +04:00
sys.path += [path_from_root('')]
from tools.shared import *
2011-11-21 09:02:46 +04:00
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.
'''
2012-10-21 04:48:35 +04:00
import os, sys, shutil, tempfile, subprocess, shlex, time
2012-03-01 18:32:24 +04:00
from subprocess import PIPE, STDOUT
2011-12-11 22:23:03 +04:00
from tools import shared
2012-06-17 05:12:02 +04:00
from tools.shared import Compression, execute, suffix, unsuffixed, unsuffixed_basename
2012-03-01 18:32:24 +04:00
2012-01-30 08:50:29 +04:00
# Mapping of emcc opt levels to llvm opt levels. We use llvm opt level 3 in emcc opt
# levels 2 and 3 (emcc 3 is unsafe opts, so unsuitable for the only level to get
# llvm opt level 3, and speed-wise emcc level 2 is already the slowest/most optimizing
# level)
LLVM_OPT_LEVEL = {
0: 0,
1: 1,
2: 3,
3: 3,
}
2012-01-24 01:44:36 +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
2012-01-26 06:06:39 +04:00
AUTODEBUG = os.environ.get('EMCC_AUTODEBUG') # If set to 1, we will run the autodebugger (the automatic debugging tool, see tools/autodebugger).
# Note that this will disable inclusion of libraries. This is useful because including
# dlmalloc makes it hard to compare native and js builds
2012-02-28 08:09:09 +04:00
EMCC_CFLAGS = os.environ.get('EMCC_CFLAGS') # Additional compiler flags that we treat as if they were passed to us on the commandline
if DEBUG: print >> sys.stderr, '\nemcc invocation: ', ' '.join(sys.argv), (' + ' + EMCC_CFLAGS if EMCC_CFLAGS else '')
if EMCC_CFLAGS: sys.argv.append(EMCC_CFLAGS)
2011-11-23 09:34:03 +04:00
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
2012-01-24 08:29:31 +04:00
stdout = PIPE if not DEBUG else None # suppress output of child processes
stderr = PIPE if not DEBUG else None # unless we are in DEBUG mode
2012-09-14 03:59:46 +04:00
shared.check_sanity(force=DEBUG)
2011-12-18 23:34:19 +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
2012-05-24 13:15:06 +04:00
Copyright (C) 2012 the Emscripten authors.
2011-12-11 22:23:03 +04:00
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)
2012-01-24 01:44:36 +04:00
-O1 Simple optimizations, including LLVM -O1
2011-12-14 00:20:45 +04:00
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-08-26 01:10:09 +04:00
-s DISABLE_EXCEPTION_CATCHING=0 ).
2012-08-25 22:26:31 +04:00
(For details on the affects of different
opt levels, see apply_opt_level() in
2012-08-26 01:10:09 +04:00
tools/shared.py and also src/settings.js.)
2012-01-08 06:36:42 +04:00
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),
2012-01-24 01:44:36 +04:00
plus closure compiler advanced opts, plus
LLVM -O2 optimizations
2011-12-13 01:35:51 +04:00
Warning: Compiling with this takes a long time!
-O3 As -O2, plus dangerous optimizations that may
2012-10-27 04:53:46 +04:00
break the generated code! This is not
recommended at all, see the wiki for more
details (you can try -O2 and then add
dangerous optimizations one by one).
2012-03-16 23:39:12 +04:00
2011-12-14 00:20:45 +04:00
-s OPTION=VALUE JavaScript code generation option passed
2012-02-26 22:32:39 +04:00
into the emscripten compiler. For the
available options, see src/settings.js
2012-09-22 04:36:22 +04:00
Note that for options that are lists, you
need quotation marks in most shells, for
example
-s RUNTIME_LINKED_LIBS="['liblib.so']"
or
-s "RUNTIME_LINKED_LIBS=['liblib.so']"
(without the external "s in either of those,
you would get an error)
2012-03-16 23:39:12 +04:00
2011-12-14 00:20:45 +04:00
--typed-arrays <mode> 0: No typed arrays
1: Parallel typed arrays
2: Shared (C-like) typed arrays (default)
2012-03-16 23:39:12 +04:00
2012-02-01 07:09:32 +04:00
--llvm-opts <level> 0: No LLVM optimizations (default in -O0)
1: -O1 LLVM optimizations (default in -O1)
2: -O2 LLVM optimizations
3: -O3 LLVM optimizations (default in -O2+)
2012-03-16 23:39:12 +04:00
2012-03-26 01:13:53 +04:00
--llvm-lto <level> 0: No LLVM LTO (default in -O0)
1: LLVM LTO (default in -O1+)
Note: If LLVM optimizations are not run
(see --llvm-opts), setting this to 1 has no
effect.
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-03-16 23:39:12 +04:00
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-03-16 23:39:12 +04:00
2012-02-27 09:30:20 +04:00
--pre-js <file> A file whose contents are added before the
2012-03-21 00:37:59 +04:00
generated code. This is done *before*
optimization, so it will be minified
properly if closure compiler is run.
2012-03-16 23:39:12 +04:00
2012-02-27 09:30:20 +04:00
--post-js <file> A file whose contents are added after the
2012-03-21 00:37:59 +04:00
generated code This is done *before*
optimization, so it will be minified
properly if closure compiler is run.
2012-03-16 23:39:12 +04:00
2012-03-16 01:07:04 +04:00
--embed-file <file> A file to embed inside the generated
2012-02-25 03:54:47 +04:00
JavaScript. The compiled code will be able
to access the file in the current directory
2012-07-30 03:41:51 +04:00
with the same name as given here. So if
you do --embed-file dir/file.dat, then
(1) dir/file.dat must exist relative to
where you run emcc, and (2) your compiled
code will be able to find the file by
reading that same path, dir/file.dat.
2012-03-16 21:48:45 +04:00
If a directory is passed here, its entire
contents will be embedded.
2012-03-16 23:39:12 +04:00
2012-03-16 01:07:04 +04:00
--preload-file <name> A file to preload before running the
compiled code asynchronously. Otherwise
similar to --embed-file, except that this
option is only relevant when generating
2012-06-11 22:13:37 +04:00
HTML (it uses asynchronous binary XHRs),
or JS that will be used in a web page.
2012-03-16 21:48:45 +04:00
If a directory is passed here, its entire
contents will be preloaded.
2012-06-11 08:48:00 +04:00
Preloaded files are stored in filename.data,
where filename.html is the main file you
are compiling to. To run your code, you
will need both the .html and the .data.
2012-03-16 23:39:12 +04:00
--compression <codec> Compress both the compiled code and embedded/
preloaded files. <codec> should be a triple,
<native_encoder>,<js_decoder>,<js_name>
where native_encoder is a native executable
that compresses stdin to stdout (the simplest
possible interface), js_decoder is a
JavaScript file that implements a decoder,
and js_name is the name of the function to
call in the decoder file (which should
receive an array/typed array and return
an array/typed array.
Compression only works when generating HTML.
2012-03-27 03:46:24 +04:00
When compression is on, all filed specified
to be preloaded are compressed in one big
archive, which is given the same name as the
output HTML but with suffix .data.compress
2012-03-16 23:39:12 +04:00
--minify <on> 0: Do not minify the generated JavaScript's
whitespace (default if closure compiler
will not be run)
1: Minify 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)
2012-09-25 16:44:54 +04:00
--split <size> Splits the resulting javascript file into pieces
to ease debugging. This option only works if
Javascript is generated (target -o <name>.js).
Files with function declarations must be loaded
before main file upon execution.
Without "-g" option:
Creates files with function declarations up
to the given size with the suffix
"_functions.partxxx.js" and a main file with
the suffix ".js".
With "-g" option:
Recreates the directory structure of the C
source files and stores function declarations
in their respective C files with the suffix
".js". If such a file exceeds the given size,
files with the suffix ".partxxx.js" are created.
The main file resides in the base directory and
has the suffix ".js".
2012-10-13 00:40:12 +04:00
--bind Compiles the source code using the "embind"
bindings approach, which connects C/C++ and JS.
2012-02-25 08:31:11 +04:00
--ignore-dynamic-linking Normally emcc will treat dynamic linking like
static linking, by linking in the code from
the dynamic library. This fails if the same
dynamic library is linked more than once.
With this option, dynamic linking is ignored,
which allows the build system to proceed without
errors. However, you will need to manually
link to the shared libraries later on yourself.
2012-03-16 23:39:12 +04:00
2012-02-10 05:56:23 +04:00
--shell-file <path> The path name to a skeleton HTML file used
2012-01-28 01:32:46 +04:00
when generating HTML output. The shell file
used needs to have this token inside it:
{{{ SCRIPT_CODE }}}
Note that this argument is ignored if a
target other than HTML is specified using
the -o option.
2011-12-11 23:39:00 +04:00
2012-04-14 05:57:41 +04:00
--js-library <lib> A JavaScript library to use in addition to
those in Emscripten's src/library_*
2012-04-26 22:41:50 +04:00
-v Turns on verbose output. This will pass
-v to Clang, and also enable EMCC_DEBUG
to details emcc's operations
2012-06-23 07:50:21 +04:00
--remove-duplicates If set, will remove duplicate symbols when
linking. This can be useful because
llvm-link's behavior is not as permissive
as ld is.
2012-08-31 03:42:34 +04:00
--clear-cache Manually clears the cache of compiled
emscripten system libraries (libc++,
libc++abi, dlmalloc). This is normally
handled automatically, but if you update
llvm in-place (instead of having a different
directory for a new version), the caching
mechanism can get confused. Clearing the
cache can fix weird problems related to
cache incompatibilities, like clang failing
to link with library files.
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
2012-03-04 00:00:39 +04:00
<name>.o LLVM bitcode (same as .bc)
2011-12-14 06:23:00 +04:00
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.
2012-02-28 08:09:09 +04:00
emcc is affected by several environment variables. For details, view
the source of emcc (search for 'os.environ').
2011-12-11 23:54:22 +04:00
''' % (this, this, this)
2011-11-23 09:34:03 +04:00
exit(0)
2012-05-18 22:47:41 +04:00
elif len(sys.argv) == 2 and sys.argv[1] == '-v': # -v with no inputs
print 'emcc (Emscripten GCC-like replacement) 2.0'
exit(subprocess.call([shared.CLANG, '-v']))
2011-11-23 09:34:03 +04:00
2012-09-18 21:52:45 +04:00
def is_minus_s_for_emcc(newargs,i):
2012-09-20 16:32:26 +04:00
assert newargs[i] == '-s'
if i+1 < len(newargs) and '=' in newargs[i+1]: # -s OPT=VALUE is for us, -s by itself is a linker option
return True
else:
print >> sys.stderr, 'emcc: warning: treating -s as linker option and not as -s OPT=VALUE for js compilation'
return False
2012-09-18 21:52:45 +04:00
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)
2012-10-29 02:23:56 +04:00
CONFIGURE_CONFIG = (os.environ.get('EMMAKEN_JUST_CONFIGURE') or 'conftest.c' in sys.argv) and not os.environ.get('EMMAKEN_JUST_CONFIGURE_RECURSE')
2011-11-21 09:02:46 +04:00
CMAKE_CONFIG = 'CMakeFiles/cmTryCompileExec.dir' in ' '.join(sys.argv)# or 'CMakeCCompilerId' in ' '.join(sys.argv)
if CONFIGURE_CONFIG or CMAKE_CONFIG:
2012-10-30 05:52:23 +04:00
debug_configure = 0 # XXX use this to debug configure stuff. ./configure's generally hide our normal output including stderr so we write to a file
use_clang = 1 # whether we fake configure tests using clang - the local, native compiler - or not. if not we generate JS and use node with a shebang
2012-10-29 02:23:56 +04:00
# neither approach is perfect, you can try both, but may need to edit configure scripts in some cases
# XXX False is not fully tested yet
2012-10-30 05:10:35 +04:00
if debug_configure:
tempout = '/tmp/emscripten_temp/out'
if not os.path.exists(tempout):
open(tempout, 'w').write('//\n')
src = None
for i in range(len(sys.argv)):
if sys.argv[i].endswith('.c'):
try:
src = open(sys.argv[i]).read()
2012-10-30 05:52:23 +04:00
if debug_configure: open(tempout, 'a').write('============= ' + sys.argv[i] + '\n' + src + '\n=============\n\n')
2012-10-30 05:10:35 +04:00
except:
pass
if src:
if 'fopen' in src and '"w"' in src:
use_clang = True # we cannot write to files from js!
if debug_configure: open(tempout, 'a').write('Forcing clang since uses fopen to write\n')
2012-10-29 02:23:56 +04:00
compiler = os.environ.get('CONFIGURE_CC') or (shared.CLANG if use_clang else shared.EMCC) # if CONFIGURE_CC is defined, use that. let's you use local gcc etc. if you need that
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)
2012-10-29 02:23:56 +04:00
2012-09-18 21:52:45 +04:00
def filter_emscripten_options(argv):
idx = 0
2012-09-20 16:32:26 +04:00
skip_next = False
2012-09-18 21:52:45 +04:00
for el in argv:
2012-09-20 16:32:26 +04:00
if skip_next:
skip_next = False
2012-09-18 21:52:45 +04:00
idx += 1
continue
2012-09-20 16:32:26 +04:00
if el == '-s' and is_minus_s_for_emcc(argv, idx):
skip_next = True
2012-09-18 21:52:45 +04:00
else:
yield el
idx += 1
2012-10-29 02:23:56 +04:00
cmd = [compiler] + list(filter_emscripten_options(sys.argv[1:]))
if use_clang: cmd += shared.EMSDK_OPTS + ['-DEMSCRIPTEN']
2012-10-30 04:25:42 +04:00
2012-03-20 03:49:36 +04:00
if DEBUG: print >> sys.stderr, 'emcc, just configuring: ', ' '.join(cmd)
2012-10-30 05:10:35 +04:00
if debug_configure: open(tempout, 'a').write('emcc, just configuring: ' + ' '.join(cmd) + '\n\n')
2012-10-30 04:25:42 +04:00
2012-10-29 02:23:56 +04:00
if use_clang:
exit(subprocess.call(cmd))
else:
2012-10-30 05:52:23 +04:00
only_object = '-c' in cmd
2012-10-29 02:23:56 +04:00
target = None
2012-10-30 05:52:23 +04:00
for i in range(len(cmd)-1):
if cmd[i] == '-o':
if not only_object:
cmd[i+1] += '.js'
target = cmd[i+1]
2012-10-29 02:23:56 +04:00
break
2012-10-30 05:52:23 +04:00
print 't1', target
2012-10-29 02:23:56 +04:00
if not target:
2012-10-30 05:52:23 +04:00
target = 'a.out.js'
print 't2', target, only_object
os.environ['EMMAKEN_JUST_CONFIGURE_RECURSE'] = '1'
ret = subprocess.call(cmd)
os.environ['EMMAKEN_JUST_CONFIGURE_RECURSE'] = ''
if not os.path.exists(target): exit(1)
if target.endswith('.js'):
shutil.copyfile(target, target[:-3])
target = target[:-3]
2012-10-29 02:23:56 +04:00
src = open(target).read()
2012-10-30 05:52:23 +04:00
full_node = shared.NODE_JS
if os.path.sep not in full_node:
full_node = '/usr/bin/' + full_node # TODO: use whereis etc. And how about non-*NIX?
open(target, 'w').write('#!' + full_node + '\n' + src) # add shebang
2012-10-29 02:23:56 +04:00
import stat
os.chmod(target, stat.S_IMODE(os.stat(target).st_mode) | stat.S_IXUSR) # make executable
exit(ret)
2011-11-21 09:02:46 +04:00
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')
2012-03-26 21:21:25 +04:00
if EMMAKEN_CFLAGS: CC_ADDITIONAL_ARGS += shlex.split(EMMAKEN_CFLAGS)
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')
2012-06-14 00:50:15 +04:00
BITCODE_SUFFIXES = ('.bc', '.o', '.obj')
2012-02-25 08:31:11 +04:00
DYNAMICLIB_SUFFIXES = ('.dylib', '.so', '.dll')
2012-02-23 23:14:52 +04:00
STATICLIB_SUFFIXES = ('.a',)
2012-02-03 01:43:06 +04:00
ASSEMBLY_SUFFIXES = ('.ll',)
2012-02-23 23:14:52 +04:00
LIB_PREFIXES = ('', 'lib')
2011-12-15 06:46:48 +04:00
2012-10-13 00:40:12 +04:00
JS_CONTAINING_SUFFIXES = ('js', 'html')
2012-04-08 07:42:02 +04:00
seen_names = {}
2012-06-26 05:31:27 +04:00
def uniquename(name):
2012-04-08 07:42:02 +04:00
if name not in seen_names:
seen_names[name] = str(len(seen_names))
2012-06-26 05:31:27 +04:00
return unsuffixed(name) + '_' + seen_names[name] + (('.' + suffix(name)) if suffix(name) else '')
2012-04-08 07:42:02 +04:00
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
2012-03-30 08:14:53 +04:00
if '-M' in sys.argv or '-MM' in sys.argv:
2012-03-30 08:15:40 +04:00
# Just output dependencies, do not compile. Warning: clang and gcc behave differently with -MF! (clang seems to not recognize it)
2012-04-01 00:49:58 +04:00
cmd = [CC] + shared.COMPILER_OPTS + sys.argv[1:]
2012-03-30 08:14:53 +04:00
if DEBUG: print >> sys.stderr, 'emcc, just dependencies: ', ' '.join(cmd)
exit(subprocess.call(cmd))
2011-12-12 03:24:04 +04:00
# 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
2012-03-19 21:44:16 +04:00
if len(sys.argv) >= 3: # if there is a source and a target, then copy, otherwise do nothing
2012-04-08 05:32:06 +04:00
sys.argv = filter(lambda arg: not arg.startswith('-I'), sys.argv)
if DEBUG: print >> sys.stderr, 'Just copy:', sys.argv[-1], target
shutil.copy(sys.argv[-1], target)
2012-03-19 21:44:16 +04:00
else:
if DEBUG: print >> sys.stderr, 'No-op.'
2011-12-15 05:42:35 +04:00
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:
2012-11-07 08:59:08 +04:00
temp_root = shared.TEMP_DIR
2012-09-13 05:59:58 +04:00
if not os.path.exists(temp_root):
os.makedirs(temp_root)
temp_dir = tempfile.mkdtemp(dir=temp_root)
2011-12-15 23:01:56 +04:00
2011-12-15 05:42:35 +04:00
def in_temp(name):
2012-06-26 05:31:27 +04:00
return os.path.join(temp_dir, os.path.basename(name))
2011-12-15 05:42:35 +04:00
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
2012-01-24 01:44:36 +04:00
llvm_opts = None
2012-03-26 01:13:53 +04:00
llvm_lto = None
2011-12-15 09:17:53 +04:00
closure = None
2012-01-05 02:36:02 +04:00
js_transform = None
2012-06-18 00:24:22 +04:00
pre_js = ''
post_js = ''
2012-03-16 22:00:12 +04:00
minify_whitespace = None
2012-09-25 16:44:54 +04:00
split_js_file = None
2012-06-11 23:50:53 +04:00
preload_files = []
embed_files = []
2012-03-16 23:39:12 +04:00
compression = None
2012-02-25 08:31:11 +04:00
ignore_dynamic_linking = False
2012-01-28 01:32:46 +04:00
shell_path = shared.path_from_root('src', 'shell.html')
2012-04-14 05:57:41 +04:00
js_libraries = []
2012-06-23 07:50:21 +04:00
remove_duplicates = False
2012-10-13 00:40:12 +04:00
bind = False
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
2012-09-22 04:53:05 +04:00
absolute_warning_shown = False
2011-12-12 03:24:04 +04:00
for i in range(len(newargs)):
if newargs[i].startswith('-O'):
2012-10-03 23:12:22 +04:00
requested_level = newargs[i][2]
if requested_level == 's':
print >> sys.stderr, 'emcc: warning: -Os is ignored (use -O0, -O1, -O2)'
else:
try:
opt_level = int(requested_level)
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])
2012-01-24 01:44:36 +04:00
llvm_opts = eval(newargs[i+1])
2011-12-14 02:08:44 +04:00
newargs[i] = ''
newargs[i+1] = ''
2012-03-26 01:13:53 +04:00
elif newargs[i].startswith('--llvm-lto'):
check_bad_eq(newargs[i])
llvm_lto = eval(newargs[i+1])
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-02-27 09:30:20 +04:00
elif newargs[i].startswith('--pre-js'):
check_bad_eq(newargs[i])
2012-06-18 00:24:22 +04:00
pre_js += open(newargs[i+1]).read() + '\n'
2012-02-27 09:30:20 +04:00
newargs[i] = ''
newargs[i+1] = ''
elif newargs[i].startswith('--post-js'):
check_bad_eq(newargs[i])
2012-06-18 00:24:22 +04:00
post_js += open(newargs[i+1]).read() + '\n'
2012-02-27 09:30:20 +04:00
newargs[i] = ''
newargs[i+1] = ''
2012-03-16 22:00:12 +04:00
elif newargs[i].startswith('--minify'):
2012-01-13 06:25:01 +04:00
check_bad_eq(newargs[i])
2012-03-16 22:00:12 +04:00
minify_whitespace = int(newargs[i+1])
2012-01-13 06:25:01 +04:00
newargs[i] = ''
newargs[i+1] = ''
2012-09-25 16:44:54 +04:00
elif newargs[i].startswith('--split'):
check_bad_eq(newargs[i])
split_js_file = int(newargs[i+1])
newargs[i] = ''
newargs[i+1] = ''
2012-10-13 00:40:12 +04:00
elif newargs[i] == '--bind':
bind = True
newargs[i] = '-std=c++11' # Force C++11 for embind code
2012-02-25 03:54:47 +04:00
elif newargs[i].startswith('--embed-file'):
check_bad_eq(newargs[i])
2012-06-11 23:50:53 +04:00
embed_files.append(newargs[i+1])
2012-02-25 03:54:47 +04:00
newargs[i] = ''
newargs[i+1] = ''
2012-03-16 01:07:04 +04:00
elif newargs[i].startswith('--preload-file'):
check_bad_eq(newargs[i])
2012-06-11 23:50:53 +04:00
preload_files.append(newargs[i+1])
2012-03-16 01:07:04 +04:00
newargs[i] = ''
newargs[i+1] = ''
2012-03-16 23:39:12 +04:00
elif newargs[i].startswith('--compression'):
check_bad_eq(newargs[i])
parts = newargs[i+1].split(',')
assert len(parts) == 3, '--compression requires specifying native_encoder,js_decoder,js_name - see emcc --help. got: %s' % newargs[i+1]
Compression.encoder = parts[0]
Compression.decoder = parts[1]
Compression.js_name = parts[2]
assert os.path.exists(Compression.encoder), 'native encoder %s does not exist' % Compression.encoder
assert os.path.exists(Compression.decoder), 'js decoder %s does not exist' % Compression.decoder
Compression.on = True
newargs[i] = ''
newargs[i+1] = ''
2012-02-25 08:31:11 +04:00
elif newargs[i] == '--ignore-dynamic-linking':
ignore_dynamic_linking = True
newargs[i] = ''
2012-04-26 22:41:50 +04:00
elif newargs[i] == '-v':
shared.COMPILER_OPTS += ['-v']
DEBUG = 1
newargs[i] = ''
2012-01-28 01:32:46 +04:00
elif newargs[i].startswith('--shell-file'):
check_bad_eq(newargs[i])
shell_path = newargs[i+1]
newargs[i] = ''
newargs[i+1] = ''
2012-04-14 05:57:41 +04:00
elif newargs[i].startswith('--js-library'):
check_bad_eq(newargs[i])
js_libraries.append(newargs[i+1])
newargs[i] = ''
newargs[i+1] = ''
2012-06-23 07:50:21 +04:00
elif newargs[i] == '--remove-duplicates':
remove_duplicates = True
newargs[i] = ''
2012-08-31 03:42:34 +04:00
elif newargs[i] == '--clear-cache':
newargs[i] = ''
print >> sys.stderr, 'emcc: clearing cache'
shared.Cache.erase()
2012-08-19 09:05:23 +04:00
elif newargs[i].startswith(('-I/', '-L/')):
2012-09-22 04:53:05 +04:00
if not absolute_warning_shown:
print >> sys.stderr, 'emcc: warning: -I or -L of an absolute path encountered. If this is to a local system header/library, it may cause problems (local system files make sense for compiling natively on your system, but not necessarily to JavaScript)' # Of course an absolute path to a non-system-specific library or header is fine, and you can ignore this warning. The danger are system headers that are e.g. x86 specific and nonportable. The emscripten bundled headers are modified to be portable, local system ones are generally not
absolute_warning_shown = True
2011-12-13 06:07:07 +04:00
newargs = [ arg for arg in newargs if arg is not '' ]
2012-02-01 07:09:32 +04:00
if llvm_opts is None: llvm_opts = LLVM_OPT_LEVEL[opt_level]
2012-03-26 01:13:53 +04:00
if llvm_lto is None: llvm_lto = llvm_opts > 0
2011-12-15 09:17:53 +04:00
if closure is None: closure = 1 if opt_level >= 2 else 0
2012-03-16 22:00:12 +04:00
if minify_whitespace is None:
minify_whitespace = closure # if closure is run, minify 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':
2012-09-18 21:52:45 +04:00
if is_minus_s_for_emcc(newargs, i):
2012-05-31 22:59:42 +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 '' ]
2012-09-25 16:44:54 +04:00
if split_js_file:
2012-10-11 23:00:20 +04:00
settings_changes.append("PRINT_SPLIT_FILE_MARKER=1")
2012-09-25 16:44:54 +04:00
2012-02-23 23:14:52 +04:00
# Find input files
2011-12-14 04:21:03 +04:00
input_files = []
2011-12-15 06:46:48 +04:00
has_source_inputs = False
2012-04-17 23:47:52 +04:00
lib_dirs = [shared.path_from_root('system', 'local', 'lib'),
shared.path_from_root('system', 'lib')]
2012-02-23 23:14:52 +04:00
libs = []
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]
2012-03-08 02:28:13 +04:00
if i > 0:
prev = newargs[i-1]
2012-04-04 08:04:02 +04:00
if prev in ['-MT', '-install_name']: continue # ignore this gcc-style argument
2012-03-08 02:28:13 +04:00
2012-02-25 08:31:11 +04:00
if arg.endswith(SOURCE_SUFFIXES + BITCODE_SUFFIXES + DYNAMICLIB_SUFFIXES + ASSEMBLY_SUFFIXES) or shared.Building.is_ar(arg): # 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
2012-02-03 01:43:06 +04:00
if arg.endswith(ASSEMBLY_SUFFIXES) or shared.Building.is_bitcode(arg):
2011-12-17 23:49:26 +04:00
input_files.append(arg)
2012-04-04 08:05:26 +04:00
elif arg.endswith(STATICLIB_SUFFIXES + DYNAMICLIB_SUFFIXES):
# if it's not, and it's a library, just add it to libs to find later
l = unsuffixed_basename(arg)
for prefix in LIB_PREFIXES:
if not prefix: continue
if l.startswith(prefix):
l = l[len(prefix):]
break;
libs.append(l)
newargs[i] = ''
2011-12-17 23:49:26 +04:00
else:
2012-02-02 05:43:37 +04:00
print >> sys.stderr, 'emcc: %s: warning: Not valid LLVM bitcode' % arg
2011-12-17 09:55:39 +04:00
else:
2012-02-02 05:43:37 +04:00
print >> sys.stderr, 'emcc: %s: warning: No such file or directory' % arg
2012-02-23 23:14:52 +04:00
elif arg.startswith('-L'):
lib_dirs.append(arg[2:])
newargs[i] = ''
elif arg.startswith('-l'):
libs.append(arg[2:])
newargs[i] = ''
2012-04-04 08:05:26 +04:00
2012-06-25 23:50:30 +04:00
original_input_files = input_files[:]
2012-06-23 07:28:03 +04:00
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
2012-02-23 23:14:52 +04:00
# Find library files
for lib in libs:
if DEBUG: print >> sys.stderr, 'emcc: looking for library "%s"' % lib
found = False
for prefix in LIB_PREFIXES:
2012-02-25 08:31:11 +04:00
for suff in STATICLIB_SUFFIXES + DYNAMICLIB_SUFFIXES:
name = prefix + lib + suff
2012-02-23 23:14:52 +04:00
for lib_dir in lib_dirs:
path = os.path.join(lib_dir, name)
if os.path.exists(path):
if DEBUG: print >> sys.stderr, 'emcc: found library "%s" at %s' % (lib, path)
input_files.append(path)
found = True
break
if found: break
if found: break
2012-02-25 08:31:11 +04:00
if ignore_dynamic_linking:
input_files = filter(lambda input_file: not input_file.endswith(DYNAMICLIB_SUFFIXES), input_files)
2012-03-12 00:19:02 +04:00
if len(input_files) == 0:
print >> sys.stderr, 'emcc: no input files'
print >> sys.stderr, 'note that input files without a known suffix are ignored, make sure your input files end with one of: ' + str(SOURCE_SUFFIXES + BITCODE_SUFFIXES + DYNAMICLIB_SUFFIXES + STATICLIB_SUFFIXES + ASSEMBLY_SUFFIXES)
exit(0)
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
2012-03-16 23:39:12 +04:00
assert not (Compression.on and final_suffix != 'html'), 'Compression only works when generating HTML'
2012-10-13 00:40:12 +04:00
# If we are using embind and generating JS, now is the time to link in bind.cpp
if bind and final_suffix in JS_CONTAINING_SUFFIXES:
input_files.append(shared.path_from_root('system', 'lib', 'embind', 'bind.cpp'))
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
2012-02-08 04:54:24 +04:00
temp_files = []
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
2012-06-26 05:31:27 +04:00
output_file = in_temp(unsuffixed(uniquename(input_file)) + '.o')
2012-02-08 04:54:24 +04:00
temp_files.append(output_file)
2011-12-20 04:01:01 +04:00
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)
2012-03-01 18:32:24 +04:00
execute([call] + args) # let compiler frontend print directly, so colors are saved (PIPE kills that)
2011-12-20 04:01:01 +04:00
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
2012-02-03 01:43:06 +04:00
if input_file.endswith(BITCODE_SUFFIXES):
2011-12-22 04:48:37 +04:00
if DEBUG: print >> sys.stderr, 'emcc: copying bitcode file: ', input_file
2012-06-26 05:31:27 +04:00
temp_file = in_temp(unsuffixed(uniquename(input_file)) + '.o')
2012-02-08 04:54:24 +04:00
shutil.copyfile(input_file, temp_file)
temp_files.append(temp_file)
2012-02-25 08:31:11 +04:00
elif input_file.endswith(DYNAMICLIB_SUFFIXES) or shared.Building.is_ar(input_file):
2012-02-08 05:05:01 +04:00
if DEBUG: print >> sys.stderr, 'emcc: copying library file: ', input_file
2012-06-26 05:31:27 +04:00
temp_file = in_temp(uniquename(input_file))
2012-02-08 04:54:24 +04:00
shutil.copyfile(input_file, temp_file)
temp_files.append(temp_file)
2011-12-17 06:14:02 +04:00
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
2012-06-26 05:31:27 +04:00
temp_file = in_temp(unsuffixed(uniquename(input_file)) + '.o')
2012-02-09 22:37:33 +04:00
shared.Building.llvm_as(input_file, temp_file)
2012-02-08 04:54:24 +04:00
temp_files.append(temp_file)
2011-12-12 03:24:04 +04:00
2012-02-16 01:39:56 +04:00
if not LEAVE_INPUTS_RAW: assert len(temp_files) == len(input_files)
2011-12-12 03:24:04 +04:00
# If we were just asked to generate bitcode, stop there
2012-10-13 00:40:12 +04:00
if final_suffix not in JS_CONTAINING_SUFFIXES:
2012-01-24 01:44:36 +04:00
if llvm_opts > 0:
2012-01-08 06:36:42 +04:00
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:
2012-06-26 05:31:27 +04:00
shutil.move(in_temp(unsuffixed(uniquename(input_file)) + '.o'), unsuffixed_basename(input_file) + '.' + final_suffix)
2011-12-15 05:42:35 +04:00
else:
2011-12-15 06:46:48 +04:00
if len(input_files) == 1:
2012-06-26 05:31:27 +04:00
shutil.move(in_temp(unsuffixed(uniquename(input_files[0])) + '.o'), specified_target)
2011-12-15 06:46:48 +04:00
else:
2012-06-23 07:28:03 +04:00
assert len(original_input_files) == 1 or not has_dash_c, 'fatal error: cannot specify -o with -c with multiple files' + str(sys.argv) + ':' + str(original_input_files)
2011-12-15 06:46:48 +04:00
# We have a specified target (-o <target>), which is not JavaScript or HTML, and
2012-05-03 08:14:14 +04:00
# we have multiple files: Link them
if DEBUG: print >> sys.stderr, 'emcc: link: ' + str(temp_files)
2012-06-23 07:50:21 +04:00
shared.Building.link(temp_files, specified_target, remove_duplicates=remove_duplicates)
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 = []
2012-10-16 03:31:46 +04:00
if not LEAVE_INPUTS_RAW and not AUTODEBUG and \
not shared.Settings.BUILD_AS_SHARED_LIB == 2: # shared lib 2 use the library in the parent
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-04-25 04:02:43 +04:00
execute(shared.ENV_PREFIX + ['python', shared.EMCC, shared.path_from_root('system', 'lib', 'dlmalloc.c'), '-g', '-o', in_temp('dlmalloc.o')], stdout=stdout, stderr=stderr)
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
2012-04-25 04:02:43 +04:00
execute(shared.ENV_PREFIX + ['python', shared.EMXX, shared.path_from_root('system', 'lib', 'libcxx', 'new.cpp'), '-g', '-o', in_temp('new.o')], stdout=stdout, stderr=stderr)
2012-01-19 09:02:12 +04:00
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-10-14 04:10:01 +04:00
os = []
for src in ['algorithm.cpp', 'condition_variable.cpp', 'future.cpp', 'iostream.cpp', 'memory.cpp', 'random.cpp', 'stdexcept.cpp', 'system_error.cpp', 'utility.cpp', 'bind.cpp', 'debug.cpp', 'hash.cpp', 'mutex.cpp', 'string.cpp', 'thread.cpp', 'valarray.cpp', 'chrono.cpp', 'exception.cpp', 'ios.cpp', 'locale.cpp', 'regex.cpp', 'strstream.cpp', 'typeinfo.cpp']:
o = in_temp(src + '.o')
execute(shared.ENV_PREFIX + ['python', shared.EMXX, shared.path_from_root('system', 'lib', 'libcxx', src), '-o', o], stdout=stdout, stderr=stderr)
os.append(o)
shared.Building.link(os, in_temp('libcxx.bc'))
return in_temp('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
2012-06-19 07:07:56 +04:00
#print >> sys.stderr, 'emcc: info: 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-02-23 00:29:38 +04:00
# libcxxabi - just for dynamic_cast for now
def create_libcxxabi():
if DEBUG: print >> sys.stderr, 'emcc: building libcxxabi for cache'
2012-10-14 04:10:01 +04:00
os = []
for src in ['private_typeinfo.cpp']:
o = in_temp(src + '.o')
execute(shared.ENV_PREFIX + ['python', shared.EMXX, shared.path_from_root('system', 'lib', 'libcxxabi', 'src', src), '-o', o], stdout=stdout, stderr=stderr)
os.append(o)
shared.Building.link(os, in_temp('libcxxabi.bc'))
return in_temp('libcxxabi.bc')
2012-02-23 00:29:38 +04:00
def fix_libcxxabi():
assert shared.Settings.QUANTUM_SIZE == 4, 'We do not support libc++abi with QUANTUM_SIZE == 1'
2012-06-19 07:07:56 +04:00
#print >> sys.stderr, 'emcc: info: using libcxxabi, this may need CORRECT_* options'
2012-02-23 00:29:38 +04:00
#shared.Settings.CORRECT_SIGNS = shared.Settings.CORRECT_OVERFLOWS = shared.Settings.CORRECT_ROUNDINGS = 1
libcxxabi_symbols = map(lambda line: line.strip().split(' ')[1], open(shared.path_from_root('system', 'lib', 'libcxxabi', 'symbols')).readlines())
libcxxabi_symbols = filter(lambda symbol: symbol not in dlmalloc_symbols, libcxxabi_symbols)
libcxxabi_symbols = set(libcxxabi_symbols)
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
2012-02-23 00:29:38 +04:00
for name, create, fix, library_symbols in [('libcxx', create_libcxx, fix_libcxx, libcxx_symbols),
('libcxxabi', create_libcxxabi, fix_libcxxabi, libcxxabi_symbols),
('dlmalloc', create_dlmalloc, fix_dlmalloc, dlmalloc_symbols)]:
2012-01-22 02:12:18 +04:00
need = []
has = []
2012-02-16 01:39:56 +04:00
for temp_file in temp_files:
symbols = shared.Building.llvm_nm(temp_file)
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
2012-02-16 03:14:46 +04:00
# First, combine the bitcode files if there are several. We must also link if we have a singleton .a
if len(input_files) + len(extra_files_to_link) > 1 or \
2012-02-25 08:31:11 +04:00
(not LEAVE_INPUTS_RAW and not (suffix(temp_files[0]) in BITCODE_SUFFIXES or suffix(temp_files[0]) in DYNAMICLIB_SUFFIXES) and shared.Building.is_ar(temp_files[0])):
2012-02-16 03:14:46 +04:00
linker_inputs = temp_files + extra_files_to_link
2011-12-22 04:48:37 +04:00
if DEBUG: print >> sys.stderr, 'emcc: linking: ', linker_inputs
2012-06-23 07:50:21 +04:00
shared.Building.link(linker_inputs, in_temp(target_basename + '.bc'), remove_duplicates=remove_duplicates)
2012-01-30 08:50:29 +04:00
final = in_temp(target_basename + '.bc')
2011-12-14 04:21:03 +04:00
else:
2011-12-22 03:47:38 +04:00
if not LEAVE_INPUTS_RAW:
2012-02-16 01:39:56 +04:00
shutil.move(temp_files[0], in_temp(target_basename + '.bc'))
2012-01-30 08:50:29 +04:00
final = in_temp(target_basename + '.bc')
else:
final = input_files[0]
if DEBUG:
print >> sys.stderr, 'emcc: saving intermediate processing steps to %s' % shared.EMSCRIPTEN_TEMP_DIR
intermediate_counter = 0
2012-10-21 04:48:35 +04:00
intermediate_time = None
2012-01-30 08:50:29 +04:00
def save_intermediate(name=None, suffix='js'):
2012-10-21 04:48:35 +04:00
global intermediate_counter, intermediate_time
2012-01-30 08:50:29 +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)))
intermediate_counter += 1
2012-10-21 04:48:35 +04:00
now = time.time()
if intermediate_time:
print >> sys.stderr, 'emcc: step took %.2f seconds' % (now - intermediate_time)
intermediate_time = now
2012-01-30 08:50:29 +04:00
if not LEAVE_INPUTS_RAW: save_intermediate('basebc', 'bc')
2011-12-14 04:21:03 +04:00
2012-01-08 06:36:42 +04:00
# Optimize, if asked to
2012-02-01 07:09:32 +04:00
if llvm_opts > 0 and not LEAVE_INPUTS_RAW:
if DEBUG: print >> sys.stderr, 'emcc: LLVM -O%d' % llvm_opts
shared.Building.llvm_opt(in_temp(target_basename + '.bc'), llvm_opts)
2012-01-30 09:05:12 +04:00
if DEBUG: save_intermediate('opt', 'bc')
# Do LTO in a separate pass to work around LLVM bug XXX (see failure e.g. in cubescript)
2012-03-26 01:13:53 +04:00
if llvm_lto and shared.Building.can_use_unsafe_opts() and shared.Building.can_build_standalone():
2012-02-02 06:37:13 +04:00
lto_opts = []
if not shared.Building.can_inline(): lto_opts.append('-disable-inlining')
lto_opts.append('-std-link-opts')
if DEBUG: print >> sys.stderr, 'emcc: LLVM LTO:', lto_opts
shared.Building.llvm_opt(in_temp(target_basename + '.bc'), lto_opts)
2012-01-30 22:41:55 +04:00
if DEBUG: save_intermediate('lto', 'bc')
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-02-01 22:37:40 +04:00
if not LEAVE_INPUTS_RAW and shared.Building.can_build_standalone():
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-30 09:05:12 +04:00
if DEBUG: save_intermediate('dce', 'bc')
2012-01-30 08:50:29 +04:00
2012-01-25 21:55:58 +04:00
# Prepare .ll for Emscripten
2011-12-22 03:47:38 +04:00
if not LEAVE_INPUTS_RAW:
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-22 05:16:50 +04:00
if DEBUG: save_intermediate('ll', 'll')
2012-01-01 23:17:49 +04:00
2012-01-26 06:06:39 +04:00
if AUTODEBUG:
2012-02-04 03:00:45 +04:00
if DEBUG: print >> sys.stderr, 'emcc: autodebug'
2012-04-25 04:02:43 +04:00
execute(shared.ENV_PREFIX + ['python', shared.AUTODEBUGGER, final, final + '.ad.ll'])
2012-01-25 21:55:58 +04:00
final += '.ad.ll'
if DEBUG: save_intermediate('autodebug', 'll')
# Emscripten
2012-01-22 05:16:50 +04:00
if DEBUG: print >> sys.stderr, 'emcc: LLVM => JS'
2012-04-14 05:57:41 +04:00
extra_args = [] if not js_libraries else ['--libraries', ','.join(map(os.path.abspath, js_libraries))]
final = shared.Building.emscripten(final, append_ext=False, extra_args=extra_args)
2012-01-02 08:55:55 +04:00
if DEBUG: save_intermediate('original')
2011-12-12 03:24:04 +04:00
2012-03-16 01:07:04 +04:00
# Embed and preload files
2012-06-11 23:50:53 +04:00
if len(preload_files) + len(embed_files) > 0:
2012-03-16 01:07:04 +04:00
if DEBUG: print >> sys.stderr, 'emcc: setting up files'
2012-06-11 23:50:53 +04:00
file_args = []
if len(preload_files) > 0:
file_args.append('--preload')
file_args += preload_files
if len(embed_files) > 0:
file_args.append('--embed')
file_args += embed_files
if Compression.on:
file_args += ['--compress', Compression.encoder, Compression.decoder, Compression.js_name]
code = execute(shared.ENV_PREFIX + ['python', shared.FILE_PACKAGER, unsuffixed(target) + '.data'] + file_args, stdout=PIPE)[0]
2012-10-13 00:40:12 +04:00
src = open(final).read().replace('// {{PRE_RUN_ADDITIONS}}', '// {{PRE_RUN_ADDITIONS}}\n' + code)
2012-03-16 01:07:04 +04:00
final += '.files.js'
2012-02-25 03:54:47 +04:00
open(final, 'w').write(src)
2012-03-16 01:07:04 +04:00
if DEBUG: save_intermediate('files')
2012-02-25 03:54:47 +04:00
2012-02-27 09:30:20 +04:00
# Apply pre and postjs files
if pre_js or post_js:
if DEBUG: print >> sys.stderr, 'emcc: applying pre/postjses'
src = open(final).read()
final += '.pp.js'
2012-06-18 00:24:22 +04:00
open(final, 'w').write(pre_js + src + post_js)
2012-02-28 03:59:30 +04:00
if DEBUG: save_intermediate('pre-post')
2012-02-27 09:30:20 +04:00
2012-10-13 00:40:12 +04:00
# Add bindings glue if used
if bind:
if DEBUG: print >> sys.stderr, 'emcc: adding embind glue'
2012-10-13 01:26:22 +04:00
src = open(final).read().replace('// {{PRE_RUN_ADDITIONS}}', '// {{PRE_RUN_ADDITIONS}}\n' +
open(shared.path_from_root('src', 'embind', 'embind.js')).read() +
open(shared.path_from_root('src', 'embind', 'emval.js')).read()
)
2012-10-13 00:40:12 +04:00
final += '.bd.js'
open(final, 'w').write(src)
if DEBUG: save_intermediate('bind')
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-04-04 20:25:09 +04:00
posix = True if not shared.WINDOWS else False
2012-01-05 02:36:02 +04:00
if DEBUG: print >> sys.stderr, 'emcc: applying transform: %s' % js_transform
2012-04-04 20:25:09 +04:00
execute(shlex.split(js_transform, posix=posix) + [os.path.abspath(final)])
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']
2012-10-28 07:07:07 +04:00
def get_eliminate():
return 'eliminate' if not shared.Settings.ALLOW_MEMORY_GROWTH else 'eliminateMemSafe'
js_optimizer_queue += [get_eliminate()]
2011-12-15 05:42:35 +04:00
2012-03-02 04:02:37 +04:00
js_optimizer_queue += ['simplifyExpressionsPre']
if shared.Settings.RELOOP:
2012-10-28 07:07:07 +04:00
js_optimizer_queue += ['optimizeShiftsAggressive', get_eliminate()] # aggressive shifts optimization requires loops, it breaks on switches
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-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-03-16 22:00:12 +04:00
if minify_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'
2012-01-28 01:32:46 +04:00
shell = open(shell_path).read()
2012-01-06 06:11:31 +04:00
html = open(target, 'w')
2012-03-16 23:39:12 +04:00
if not Compression.on:
html.write(shell.replace('{{{ SCRIPT_CODE }}}', open(final).read()))
else:
2012-03-20 01:52:49 +04:00
# Compress the main code
js_target = unsuffixed(target) + '.js'
shutil.move(final, js_target)
Compression.compress(js_target)
# Run the decompressor in a worker, and add code to
2012-03-16 23:39:12 +04:00
# 1. download the compressed file
# 2. decompress to a typed array
# 3. convert to a string of source code
# 4. insert a script element with that source code (more effective than eval)
2012-03-20 01:52:49 +04:00
decoding = '''
var decompressWorker = new Worker('decompress.js');
var decompressCallbacks = [];
2012-03-27 04:54:44 +04:00
var decompressions = 0;
2012-03-20 01:52:49 +04:00
Module["decompress"] = function(data, callback) {
var id = decompressCallbacks.length;
decompressCallbacks.push(callback);
decompressWorker.postMessage({ data: data, id: id });
2012-03-27 04:54:44 +04:00
if (Module['setStatus']) {
decompressions++;
Module['setStatus']('Decompressing...');
}
2012-03-20 01:52:49 +04:00
};
decompressWorker.onmessage = function(event) {
decompressCallbacks[event.data.id](event.data.data);
decompressCallbacks[event.data.id] = null;
2012-03-27 04:54:44 +04:00
if (Module['setStatus']) {
decompressions--;
if (decompressions == 0) {
Module['setStatus']('');
}
}
2012-03-20 01:52:49 +04:00
};
2012-03-16 23:39:12 +04:00
var compiledCodeXHR = new XMLHttpRequest();
compiledCodeXHR.open('GET', '%s', true);
compiledCodeXHR.responseType = 'arraybuffer';
compiledCodeXHR.onload = function() {
var arrayBuffer = compiledCodeXHR.response;
if (!arrayBuffer) throw('Loading compressed code failed.');
var byteArray = new Uint8Array(arrayBuffer);
2012-03-20 01:52:49 +04:00
Module.decompress(byteArray, function(decompressed) {
var source = Array.prototype.slice.apply(decompressed).map(function(x) { return String.fromCharCode(x) }).join(''); // createObjectURL instead?
var scriptTag = document.createElement('script');
scriptTag.setAttribute('type', 'text/javascript');
scriptTag.innerHTML = source;
document.body.appendChild(scriptTag);
});
2012-03-16 23:39:12 +04:00
};
compiledCodeXHR.send(null);
2012-03-20 01:52:49 +04:00
''' % Compression.compressed_name(js_target)
2012-03-16 23:39:12 +04:00
html.write(shell.replace('{{{ SCRIPT_CODE }}}', decoding))
2012-03-20 01:52:49 +04:00
# Add decompressor with web worker glue code
decompressor = open('decompress.js', 'w')
decompressor.write(open(Compression.decoder).read())
decompressor.write('''
onmessage = function(event) {
postMessage({ data: %s(event.data.data), id: event.data.id });
};
''' % Compression.js_name)
decompressor.close()
2011-12-15 05:42:35 +04:00
html.close()
else:
2012-09-25 16:44:54 +04:00
if split_js_file:
from tools.split import split_javascript_file
split_javascript_file(final, unsuffixed(target), split_js_file)
else:
# copy final JS to output
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