1
import multiprocessing, os, re, shutil, subprocess, sys
3
from tools.shared import *
4
from runner import RunnerCore, path_from_root
6
class other(RunnerCore):
8
for compiler in [EMCC, EMXX]:
9
shortcompiler = os.path.basename(compiler)
10
suffix = '.c' if compiler == EMCC else '.cpp'
13
output = Popen([PYTHON, compiler, '--version'], stdout=PIPE, stderr=PIPE).communicate()
14
output = output[0].replace('\r', '')
15
self.assertContained('''emcc (Emscripten GCC-like replacement)''', output)
16
self.assertContained('''Copyright (C) 2013 the Emscripten authors (see AUTHORS.txt)
17
This is free and open source software under the MIT license.
18
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
21
# -v, without input files
22
output = Popen([PYTHON, compiler, '-v'], stdout=PIPE, stderr=PIPE).communicate()
23
self.assertContained('''clang version''', output[1].replace('\r', ''), output[1].replace('\r', ''))
26
output = Popen([PYTHON, compiler, '--help'], stdout=PIPE, stderr=PIPE).communicate()
27
self.assertContained('''%s [options] file...
29
Most normal gcc/g++ options will work, for example:
30
--help Display this information
31
--version Display compiler version information
33
Options that are modified or new in %s include:
34
-O0 No optimizations (default)
35
''' % (shortcompiler, shortcompiler), output[0].replace('\r', ''), output[1].replace('\r', ''))
37
# emcc src.cpp ==> writes a.out.js
39
output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix)], stdout=PIPE, stderr=PIPE).communicate()
40
assert len(output[0]) == 0, output[0]
41
assert os.path.exists('a.out.js'), '\n'.join(output)
42
self.assertContained('hello, world!', run_js('a.out.js'))
44
# properly report source code errors, and stop there
46
assert not os.path.exists('a.out.js')
47
process = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world_error' + suffix)], stdout=PIPE, stderr=PIPE)
48
output = process.communicate()
49
assert not os.path.exists('a.out.js'), 'compilation failed, so no output file is expected'
50
assert len(output[0]) == 0, output[0]
51
assert process.returncode is not 0, 'Failed compilation must return a nonzero error code!'
52
self.assertNotContained('IOError', output[1]) # no python stack
53
self.assertNotContained('Traceback', output[1]) # no python stack
54
self.assertContained('error: invalid preprocessing directive', output[1])
55
self.assertContained(["error: use of undeclared identifier 'cheez", "error: unknown type name 'cheez'"], output[1])
56
self.assertContained('errors generated', output[1])
57
assert 'compiler frontend failed to generate LLVM bitcode, halting' in output[1].split('errors generated.')[1]
59
# emcc src.cpp -c and emcc src.cpp -o src.[o|bc] ==> should give a .bc file
60
# regression check: -o js should create "js", with bitcode content
61
for args in [['-c'], ['-o', 'src.o'], ['-o', 'src.bc'], ['-o', 'src.so'], ['-o', 'js']]:
62
target = args[1] if len(args) == 2 else 'hello_world.o'
64
Popen([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix)] + args, stdout=PIPE, stderr=PIPE).communicate()
65
syms = Building.llvm_nm(target)
66
assert len(syms.defs) == 1 and 'main' in syms.defs, 'Failed to generate valid bitcode'
67
if target == 'js': # make sure emcc can recognize the target as a bitcode file
68
shutil.move(target, target + '.bc')
70
output = Popen([PYTHON, compiler, target, '-o', target + '.js'], stdout = PIPE, stderr = PIPE).communicate()
71
assert len(output[0]) == 0, output[0]
72
assert os.path.exists(target + '.js'), 'Expected %s to exist since args are %s : %s' % (target + '.js', str(args), '\n'.join(output))
73
self.assertContained('hello, world!', run_js(target + '.js'))
75
# handle singleton archives
77
Popen([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix), '-o', 'a.bc'], stdout=PIPE, stderr=PIPE).communicate()
78
Popen([LLVM_AR, 'r', 'a.a', 'a.bc'], stdout=PIPE, stderr=PIPE).communicate()
79
assert os.path.exists('a.a')
80
output = Popen([PYTHON, compiler, 'a.a']).communicate()
81
assert os.path.exists('a.out.js'), output
82
self.assertContained('hello, world!', run_js('a.out.js'))
84
# emcc src.ll ==> generates .js
86
output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world.ll')], stdout=PIPE, stderr=PIPE).communicate()
87
assert len(output[0]) == 0, output[0]
88
assert os.path.exists('a.out.js'), '\n'.join(output)
89
self.assertContained('hello, world!', run_js('a.out.js'))
91
# emcc [..] -o [path] ==> should work with absolute paths
93
for path in [os.path.abspath(os.path.join('..', 'file1.js')), os.path.join('b_dir', 'file2.js')]:
95
self.clear(in_curr=True)
96
os.chdir(self.get_dir())
97
if not os.path.exists('a_dir'): os.mkdir('a_dir')
99
if not os.path.exists('b_dir'): os.mkdir('b_dir')
100
output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world.ll'), '-o', path], stdout=PIPE, stderr=PIPE).communicate()
102
assert os.path.exists(path), path + ' does not exist; ' + '\n'.join(output)
104
os.chdir(os.path.dirname(path))
105
self.assertContained('hello, world!', run_js(os.path.basename(path)))
108
os.chdir(self.get_dir())
111
# dlmalloc. dlmalloc is special in that it is the only part of libc that is (1) hard to write well, and
112
# very speed-sensitive. So we do not implement it in JS in library.js, instead we compile it from source
113
for source, has_malloc in [('hello_world' + suffix, False), ('hello_malloc.cpp', True)]:
114
print source, has_malloc
116
output = Popen([PYTHON, compiler, path_from_root('tests', source)], stdout=PIPE, stderr=PIPE).communicate()
117
assert os.path.exists('a.out.js'), '\n'.join(output)
118
self.assertContained('hello, world!', run_js('a.out.js'))
119
generated = open('a.out.js').read()
120
assert ('function _malloc(bytes) {' in generated) == (not has_malloc), 'If malloc is needed, it should be there, if not not'
122
# Optimization: emcc src.cpp -o something.js [-Ox]. -O0 is the same as not specifying any optimization setting
123
for params, opt_level, bc_params, closure, has_malloc in [ # bc params are used after compiling to bitcode
124
(['-o', 'something.js'], 0, None, 0, 1),
125
(['-o', 'something.js', '-O0'], 0, None, 0, 0),
126
(['-o', 'something.js', '-O1'], 1, None, 0, 0),
127
(['-o', 'something.js', '-O1', '-g'], 1, None, 0, 0), # no closure since debug
128
(['-o', 'something.js', '-O1', '--closure', '1'], 1, None, 1, 0),
129
(['-o', 'something.js', '-O1', '--closure', '1', '-s', 'ASM_JS=0'], 1, None, 1, 0),
130
(['-o', 'something.js', '-O2'], 2, None, 0, 1),
131
(['-o', 'something.js', '-O2', '-g'], 2, None, 0, 0),
132
(['-o', 'something.js', '-Os'], 2, None, 0, 1),
133
(['-o', 'something.js', '-O3', '-s', 'ASM_JS=0'], 3, None, 1, 1),
134
# and, test compiling to bitcode first
135
(['-o', 'something.bc'], 0, [], 0, 0),
136
(['-o', 'something.bc', '-O0'], 0, [], 0, 0),
137
(['-o', 'something.bc', '-O1'], 1, ['-O1'], 0, 0),
138
(['-o', 'something.bc', '-O2'], 2, ['-O2'], 0, 0),
139
(['-o', 'something.bc', '-O3'], 3, ['-O3', '-s', 'ASM_JS=0'], 1, 0),
140
(['-O1', '-o', 'something.bc'], 1, [], 0, 0),
142
print params, opt_level, bc_params, closure, has_malloc
144
keep_debug = '-g' in params
145
args = [PYTHON, compiler, path_from_root('tests', 'hello_world_loop' + ('_malloc' if has_malloc else '') + '.cpp')] + params
148
stdout=PIPE, stderr=PIPE).communicate()
149
assert len(output[0]) == 0, output[0]
150
if bc_params is not None:
151
assert os.path.exists('something.bc'), output[1]
152
bc_args = [PYTHON, compiler, 'something.bc', '-o', 'something.js'] + bc_params
153
print '....', bc_args
154
output = Popen(bc_args, stdout=PIPE, stderr=PIPE).communicate()
155
assert os.path.exists('something.js'), output[1]
156
assert ('Applying some potentially unsafe optimizations!' in output[1]) == (opt_level >= 3), 'unsafe warning should appear in opt >= 3'
157
self.assertContained('hello, world!', run_js('something.js'))
159
# Verify optimization level etc. in the generated code
160
# XXX these are quite sensitive, and will need updating when code generation changes
161
generated = open('something.js').read() # TODO: parse out the _main function itself, not support code, if the tests below need that some day
162
assert 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 should be used by default'
163
assert 'SAFE_HEAP' not in generated, 'safe heap should not be used by default'
164
assert ': while(' not in generated, 'when relooping we also js-optimize, so there should be no labelled whiles'
166
if opt_level == 0: assert '._main =' in generated, 'closure compiler should have been run'
167
elif opt_level >= 1: assert '._main=' in generated, 'closure compiler should have been run (and output should be minified)'
169
# closure has not been run, we can do some additional checks. TODO: figure out how to do these even with closure
170
assert '._main = ' not in generated, 'closure compiler should not have been run'
172
assert ('(label)' in generated or '(label | 0)' in generated) == (opt_level <= 1), 'relooping should be in opt >= 2'
173
assert ('assert(STACKTOP < STACK_MAX' in generated) == (opt_level == 0), 'assertions should be in opt == 0'
174
assert 'var $i;' in generated or 'var $i_0' in generated or 'var $storemerge3;' in generated or 'var $storemerge4;' in generated or 'var $i_04;' in generated or 'var $original = 0' in generated, 'micro opts should always be on'
175
if opt_level >= 2 and '-g' in params:
176
assert re.search('HEAP8\[\$?\w+ ?\+ ?\(+\$?\w+ ?', generated) or re.search('HEAP8\[HEAP32\[', generated), 'eliminator should create compound expressions, and fewer one-time vars' # also in -O1, but easier to test in -O2
177
assert ('_puts(' in generated) == (opt_level >= 1), 'with opt >= 1, llvm opts are run and they should optimize printf to puts'
178
if opt_level == 0 or '-g' in params: assert 'function _main() {' in generated, 'Should be unminified, including whitespace'
179
elif opt_level >= 2: assert ('function _main(){' in generated or '"use asm";var a=' in generated), 'Should be whitespace-minified'
181
# emcc -s RELOOP=1 src.cpp ==> should pass -s to emscripten.py. --typed-arrays is a convenient alias for -s USE_TYPED_ARRAYS
182
for params, test, text in [
183
(['-O2'], lambda generated: 'function intArrayToString' in generated, 'shell has unminified utilities'),
184
(['-O2', '--closure', '1'], lambda generated: 'function intArrayToString' not in generated, 'closure minifies the shell'),
185
(['-O2'], lambda generated: 'var b=0' in generated and not 'function _main' in generated, 'registerize/minify is run by default in -O2'),
186
(['-O2', '--minify', '0'], lambda generated: 'var b = 0' in generated and not 'function _main' in generated, 'minify is cancelled, but not registerize'),
187
(['-O2', '--js-opts', '0'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'js opts are cancelled'),
188
(['-O2', '-g'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'registerize/minify is cancelled by -g'),
189
(['-O2', '-g0'], lambda generated: 'var b=0' in generated and not 'function _main' in generated, 'registerize/minify is run by default in -O2 -g0'),
190
(['-O2', '-g1'], lambda generated: 'var b = 0' in generated and not 'function _main' in generated, 'compress is cancelled by -g1'),
191
(['-O2', '-g2'], lambda generated: ('var b = 0' in generated or 'var i1 = 0' in generated) and 'function _main' in generated, 'minify is cancelled by -g2'),
192
(['-O2', '-g3'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'registerize is cancelled by -g3'),
193
#(['-O2', '-g4'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'same as -g3 for now'),
194
(['-s', 'INLINING_LIMIT=0'], lambda generated: 'function _dump' in generated, 'no inlining without opts'),
195
(['-O3', '-s', 'INLINING_LIMIT=0', '--closure', '0'], lambda generated: 'function _dump' not in generated, 'lto/inlining'),
196
(['-Os', '--llvm-lto', '1', '-s', 'ASM_JS=0'], lambda generated: 'function _dump' in generated, '-Os disables inlining'),
197
(['-s', 'USE_TYPED_ARRAYS=0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'),
198
(['-s', 'USE_TYPED_ARRAYS=1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'),
199
([], lambda generated: 'Module["_dump"]' not in generated, 'dump is not exported by default'),
200
(['-s', 'EXPORTED_FUNCTIONS=["_main", "_dump"]'], lambda generated: 'Module["_dump"]' in generated, 'dump is now exported'),
201
(['--typed-arrays', '0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'),
202
(['--typed-arrays', '1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'),
203
(['--typed-arrays', '2'], lambda generated: 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 selected'),
204
(['--llvm-opts', '1'], lambda generated: '_puts(' in generated, 'llvm opts requested'),
208
output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world_loop.cpp'), '-o', 'a.out.js'] + params, stdout=PIPE, stderr=PIPE).communicate()
209
assert len(output[0]) == 0, output[0]
210
assert os.path.exists('a.out.js'), '\n'.join(output)
211
self.assertContained('hello, world!', run_js('a.out.js'))
212
assert test(open('a.out.js').read()), text
214
# Compiling two source files into a final JS.
215
for args, target in [([], 'a.out.js'), (['-o', 'combined.js'], 'combined.js')]:
217
output = Popen([PYTHON, compiler, path_from_root('tests', 'twopart_main.cpp'), path_from_root('tests', 'twopart_side.cpp')] + args,
218
stdout=PIPE, stderr=PIPE).communicate()
219
assert len(output[0]) == 0, output[0]
220
assert os.path.exists(target), '\n'.join(output)
221
self.assertContained('side got: hello from main, over', run_js(target))
223
# Compiling two files with -c will generate separate .bc files
225
output = Popen([PYTHON, compiler, path_from_root('tests', 'twopart_main.cpp'), path_from_root('tests', 'twopart_side.cpp'), '-c'] + args,
226
stdout=PIPE, stderr=PIPE).communicate()
228
# specifying -o and -c is an error
229
assert 'fatal error' in output[1], output[1]
232
assert os.path.exists('twopart_main.o'), '\n'.join(output)
233
assert os.path.exists('twopart_side.o'), '\n'.join(output)
234
assert not os.path.exists(target), 'We should only have created bitcode here: ' + '\n'.join(output)
236
# Compiling one of them alone is expected to fail
237
output = Popen([PYTHON, compiler, 'twopart_main.o', '-O1', '-g'] + args, stdout=PIPE, stderr=PIPE).communicate()
238
assert os.path.exists(target), '\n'.join(output)
239
#print '\n'.join(output)
240
self.assertContained('missing function', run_js(target, stderr=STDOUT))
243
# Combining those bc files into js should work
244
output = Popen([PYTHON, compiler, 'twopart_main.o', 'twopart_side.o'] + args, stdout=PIPE, stderr=PIPE).communicate()
245
assert os.path.exists(target), '\n'.join(output)
246
self.assertContained('side got: hello from main, over', run_js(target))
248
# Combining bc files into another bc should also work
250
assert not os.path.exists(target)
251
output = Popen([PYTHON, compiler, 'twopart_main.o', 'twopart_side.o', '-o', 'combined.bc'] + args, stdout=PIPE, stderr=PIPE).communicate()
252
syms = Building.llvm_nm('combined.bc')
253
assert len(syms.defs) == 2 and 'main' in syms.defs, 'Failed to generate valid bitcode'
254
output = Popen([PYTHON, compiler, 'combined.bc', '-o', 'combined.bc.js'], stdout = PIPE, stderr = PIPE).communicate()
255
assert len(output[0]) == 0, output[0]
256
assert os.path.exists('combined.bc.js'), 'Expected %s to exist' % ('combined.bc.js')
257
self.assertContained('side got: hello from main, over', run_js('combined.bc.js'))
259
# --js-transform <transform>
261
trans = os.path.join(self.get_dir(), 't.py')
262
trans_file = open(trans, 'w')
265
f = open(sys.argv[1], 'w')
266
f.write('transformed!')
270
output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix), '--js-transform', '%s t.py' % (PYTHON)], stdout=PIPE, stderr=PIPE).communicate()
271
assert open('a.out.js').read() == 'transformed!', 'Transformed output must be as expected'
273
# TODO: Add in files test a clear example of using disablePermissions, and link to it from the wiki
274
# TODO: test normal project linking, static and dynamic: get_library should not need to be told what to link!
275
# TODO: deprecate llvm optimizations, dlmalloc, etc. in emscripten.py.
277
def test_cmake(self):
278
# Test all supported generators.
280
generators = ['MinGW Makefiles', 'NMake Makefiles']
282
generators = ['Unix Makefiles']
284
make_commands = { 'MinGW Makefiles': ['mingw32-make'], 'NMake Makefiles': ['nmake', '/NOLOGO'], 'Unix Makefiles': ['make'] }
287
emconfigure = path_from_root('emconfigure.bat')
289
emconfigure = path_from_root('emconfigure')
291
for generator in generators:
292
if generator == 'NMake Makefiles' and not Building.which('nmake'):
293
print >> sys.stderr, 'Skipping NMake test for CMake support, since nmake was not found in PATH. Run this test in Visual Studio command prompt to easily access nmake.'
296
make = make_commands[generator]
297
cmake_cases = ['target_js', 'target_html']
298
cmake_outputs = ['hello_world.js', 'hello_world_gles.html']
299
for i in range(0, 2):
300
for configuration in ['Debug', 'Release']:
301
# CMake can be invoked in two ways, using 'emconfigure cmake', or by directly running 'cmake'.
303
for invoke_method in ['cmake', 'emconfigure']:
305
# Create a temp workspace folder
306
cmakelistsdir = path_from_root('tests', 'cmake', cmake_cases[i])
307
tempdirname = tempfile.mkdtemp(prefix='emscripten_test_' + self.__class__.__name__ + '_', dir=TEMP_DIR)
309
os.chdir(tempdirname)
311
verbose_level = int(os.getenv('EM_BUILD_VERBOSE')) if os.getenv('EM_BUILD_VERBOSE') != None else 0
314
if invoke_method == 'cmake':
315
# Test invoking cmake directly.
316
cmd = ['cmake', '-DCMAKE_TOOLCHAIN_FILE='+path_from_root('cmake', 'Platform', 'Emscripten.cmake'),
317
'-DCMAKE_BUILD_TYPE=' + configuration, '-G', generator, cmakelistsdir]
319
# Test invoking via 'emconfigure cmake'
320
cmd = [emconfigure, 'cmake', '-DCMAKE_BUILD_TYPE=' + configuration, '-G', generator, cmakelistsdir]
322
ret = Popen(cmd, stdout=None if verbose_level >= 2 else PIPE, stderr=None if verbose_level >= 1 else PIPE).communicate()
323
if len(ret) > 1 and ret[1] != None and len(ret[1].strip()) > 0:
324
logging.error(ret[1]) # If there were any errors, print them directly to console for diagnostics.
325
if len(ret) > 1 and ret[1] != None and 'error' in ret[1].lower():
326
logging.error('Failed command: ' + ' '.join(cmd))
327
logging.error('Result:\n' + ret[1])
328
raise Exception('cmake call failed!')
329
assert os.path.exists(tempdirname + '/Makefile'), 'CMake call did not produce a Makefile!'
332
cmd = make + (['VERBOSE=1'] if verbose_level >= 3 else [])
333
ret = Popen(cmd, stdout=None if verbose_level >= 2 else PIPE).communicate()
334
if len(ret) > 1 and ret[1] != None and len(ret[1].strip()) > 0:
335
logging.error(ret[1]) # If there were any errors, print them directly to console for diagnostics.
336
if len(ret) > 0 and ret[0] != None and 'error' in ret[0].lower() and not '0 error(s)' in ret[0].lower():
337
logging.error('Failed command: ' + ' '.join(cmd))
338
logging.error('Result:\n' + ret[0])
339
raise Exception('make failed!')
340
assert os.path.exists(tempdirname + '/' + cmake_outputs[i]), 'Building a cmake-generated Makefile failed to produce an output file %s!' % tempdirname + '/' + cmake_outputs[i]
342
# Run through node, if CMake produced a .js file.
343
if cmake_outputs[i].endswith('.js'):
344
ret = Popen(listify(NODE_JS) + [tempdirname + '/' + cmake_outputs[i]], stdout=PIPE).communicate()[0]
345
assert 'hello, world!' in ret, 'Running cmake-based .js application failed!'
347
os.chdir(path_from_root('tests')) # Move away from the directory we are about to remove.
348
shutil.rmtree(tempdirname)
350
def test_failure_error_code(self):
351
for compiler in [EMCC, EMXX]:
352
# Test that if one file is missing from the build, then emcc shouldn't succeed, and shouldn't try to produce an output file.
353
process = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world.c'), 'this_file_is_missing.c', '-o', 'this_output_file_should_never_exist.js'], stdout=PIPE, stderr=PIPE)
354
process.communicate()
355
assert process.returncode is not 0, 'Trying to compile a nonexisting file should return with a nonzero error code!'
356
assert os.path.exists('this_output_file_should_never_exist.js') == False, 'Emcc should not produce an output file when build fails!'
358
def test_cxx03(self):
359
for compiler in [EMCC, EMXX]:
360
process = Popen([PYTHON, compiler, path_from_root('tests', 'hello_cxx03.cpp')], stdout=PIPE, stderr=PIPE)
361
process.communicate()
362
assert process.returncode is 0, 'By default, emscripten should build using -std=c++03!'
364
def test_cxx11(self):
365
for compiler in [EMCC, EMXX]:
366
process = Popen([PYTHON, compiler, '-std=c++11', path_from_root('tests', 'hello_cxx11.cpp')], stdout=PIPE, stderr=PIPE)
367
process.communicate()
368
assert process.returncode is 0, 'User should be able to specify custom -std= on the command line!'
370
def test_catch_undef(self):
371
open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r'''
377
std::vector<int> vector;
383
printf("hello, world!\n");
387
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-fsanitize=undefined']).communicate()
388
self.assertContained('hello, world!', run_js(os.path.join(self.get_dir(), 'a.out.js')))
390
def test_unaligned_memory(self):
391
open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r'''
394
typedef unsigned char Bit8u;
395
typedef unsigned short Bit16u;
396
typedef unsigned int Bit32u;
400
Bit8u data[4] = {0x01,0x23,0x45,0x67};
402
printf("data: %x\n", *(Bit32u*)data);
403
printf("data[0,1] 16bit: %x\n", *(Bit16u*)data);
404
printf("data[1,2] 16bit: %x\n", *(Bit16u*)(data+1));
407
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-s', 'UNALIGNED_MEMORY=1']).communicate()
408
self.assertContained('data: 67452301\ndata[0,1] 16bit: 2301\ndata[1,2] 16bit: 4523', run_js(os.path.join(self.get_dir(), 'a.out.js')))
410
def test_unaligned_memory_2(self):
411
open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r'''
415
int main( int argc, char ** argv )
417
std::string testString( "Hello, World!" );
419
printf( "testString = %s\n", testString.c_str() );
423
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-s', 'UNALIGNED_MEMORY=1']).communicate()
424
self.assertContained('testString = Hello, World!', run_js(os.path.join(self.get_dir(), 'a.out.js')))
426
def test_asm_minify(self):
428
Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_loop_malloc.cpp')] + args).communicate()
429
self.assertContained('hello, world!', run_js(self.in_dir('a.out.js')))
430
return open(self.in_dir('a.out.js')).read()
433
assert 'function _malloc' in src
435
src = test(['-O2', '-s', 'ASM_JS=1'])
436
normal_size = len(src)
437
print 'normal', normal_size
438
assert 'function _malloc' not in src
440
src = test(['-O2', '-s', 'ASM_JS=1', '--minify', '0'])
441
unminified_size = len(src)
442
print 'unminified', unminified_size
443
assert unminified_size > normal_size
444
assert 'function _malloc' not in src
446
src = test(['-O2', '-s', 'ASM_JS=1', '-g'])
447
debug_size = len(src)
448
print 'debug', debug_size
449
assert debug_size > unminified_size
450
assert 'function _malloc' in src
452
def test_dangerous_func_cast(self):
455
typedef void (*voidfunc)();
460
int main(int argc, char **argv) {
462
for (int i = 0; i < 10; i++) fps[i] = (i == argc) ? (void (*)())my_func : NULL;
463
fps[2*(argc-1) + 1]();
467
open('src.c', 'w').write(src)
468
def test(args, expected, err_expected=None):
469
out, err = Popen([PYTHON, EMCC, 'src.c'] + args, stderr=PIPE).communicate()
470
if err_expected: self.assertContained(err_expected, err)
471
self.assertContained(expected, run_js(self.in_dir('a.out.js'), stderr=PIPE, full_output=True))
472
return open(self.in_dir('a.out.js')).read()
474
test([], 'my func') # no asm, so casting func works
475
test(['-O2'], 'abort', ['Casting potentially incompatible function pointer i32 ()* to void (...)*, for my_func',
476
'Incompatible function pointer casts are very dangerous with ASM_JS=1, you should investigate and correct these']) # asm, so failure
477
test(['-O2', '-s', 'ASSERTIONS=1'],
478
'Invalid function pointer called. Perhaps a miscast function pointer (check compilation warnings) or bad vtable lookup (maybe due to derefing a bad pointer, like NULL)?',
479
['Casting potentially incompatible function pointer i32 ()* to void (...)*, for my_func',
480
'Incompatible function pointer casts are very dangerous with ASM_JS=1, you should investigate and correct these']) # asm, so failure
482
def test_l_link(self):
483
# Linking with -lLIBNAME and -L/DIRNAME should work
485
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
486
extern void printey();
494
os.makedirs(os.path.join(self.get_dir(), 'libdir'));
498
open(os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), 'w').write('''
501
printf("hello from lib\\n");
505
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), '-c']).communicate()
506
shutil.move(os.path.join(self.get_dir(), 'libfile.o'), os.path.join(self.get_dir(), 'libdir', 'libfile.so'))
507
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile']).communicate()
508
self.assertContained('hello from lib', run_js(os.path.join(self.get_dir(), 'a.out.js')))
509
assert not os.path.exists('a.out') and not os.path.exists('a.exe'), 'Must not leave unneeded linker stubs'
511
def test_static_link(self):
512
def test(name, header, main, side, expected, args=[], suffix='cpp', first=True):
514
#t = main ; main = side ; side = t
517
if header: open(os.path.join(self.get_dir(), 'header.h'), 'w').write(header)
518
if type(main) == str:
519
open(os.path.join(self.get_dir(), 'main.' + suffix), 'w').write(main)
520
main = ['main.' + suffix]
521
if type(side) == str:
522
open(os.path.join(self.get_dir(), 'side.' + suffix), 'w').write(side)
523
side = ['side.' + suffix]
524
Popen([PYTHON, EMCC] + side + ['-o', 'side.js', '-s', 'SIDE_MODULE=1', '-O2'] + args).communicate()
525
# TODO: test with and without DISABLE_GL_EMULATION, check that file sizes change
526
Popen([PYTHON, EMCC] + main + ['-o', 'main.js', '-s', 'MAIN_MODULE=1', '-O2', '-s', 'DISABLE_GL_EMULATION=1'] + args).communicate()
527
Popen([PYTHON, EMLINK, 'main.js', 'side.js', 'together.js'], stdout=PIPE).communicate()
528
assert os.path.exists('together.js')
529
for engine in JS_ENGINES:
530
out = run_js('together.js', engine=engine, stderr=PIPE, full_output=True)
531
self.assertContained(expected, out)
532
if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out)
534
shutil.copyfile('together.js', 'first.js')
535
test(name + ' (reverse)', header, original_side, original_main, expected, args, suffix, False) # test reverse order
537
# test a simple call from one module to another. only one has a string (and constant memory initialization for it)
538
test('basics', '', '''
542
printf("other says %d.", sidey());
546
int sidey() { return 11; }
547
''', 'other says 11.')
549
# finalization of float variables should pass asm.js validation
550
test('floats', '', '''
552
extern float sidey();
554
printf("other says %.2f.", sidey()+1);
558
float sidey() { return 11.5; }
559
''', 'other says 12.50')
561
# memory initialization in both
562
test('multiple memory inits', '', r'''
566
printf("hello from main\n");
572
void sidey() { printf("hello from side\n"); }
573
''', 'hello from main\nhello from side\n')
576
test('fp1', 'typedef void (*voidfunc)();', r'''
579
voidfunc sidey(voidfunc f);
580
void a() { printf("hello from funcptr\n"); }
587
voidfunc sidey(voidfunc f) { return f; }
588
''', 'hello from funcptr\n')
590
# function pointers with 'return' in the name
591
test('fp2', 'typedef void (*voidfunc)();', r'''
594
int sidey(voidfunc f);
595
void areturn0() { printf("hello 0\n"); }
596
void areturn1() { printf("hello 1\n"); }
597
void areturn2() { printf("hello 2\n"); }
598
int main(int argc, char **argv) {
599
voidfunc table[3] = { areturn0, areturn1, areturn2 };
600
table[sidey(NULL)]();
605
int sidey(voidfunc f) { if (f) f(); return 1; }
609
test('global init', '', r'''
612
Class() { printf("a new Class\n"); }
620
''', 'a new Class\n')
622
# Multiple global initializers (LLVM generates overlapping names for them)
623
test('global inits', r'''
626
Class(const char *name) { printf("new %s\n", name); }
630
static Class c("main");
636
static Class c("side");
637
''', ['new main\nnew side\n', 'new side\nnew main\n'])
639
# Class code used across modules
640
test('codecall', r'''
643
Class(const char *name);
653
Class::Class(const char *name) { printf("new %s\n", name); }
656
# malloc usage in both modules
660
char *side(const char *data);
665
char *temp = side("hello through side\n");
666
char *ret = (char*)malloc(strlen(temp)+1);
674
char *side(const char *data) {
675
char *ret = (char*)malloc(strlen(data)+1);
679
''', ['hello through side\n'])
681
# libc usage in one modules. must force libc inclusion in the main module if that isn't the one using mallinfo()
683
os.environ['EMCC_FORCE_STDLIBS'] = 'libc'
684
test('malloc-1', r'''
691
printf("|%d|\n", side());
699
struct mallinfo m = mallinfo();
704
del os.environ['EMCC_FORCE_STDLIBS']
706
# iostream usage in one and std::string in both
707
test('iostream', r'''
714
std::cout << "hello from main " << side() << std::endl;
719
std::string side() { return "and hello from side"; }
720
''', ['hello from main and hello from side\n'])
722
# followup to iostream test: a second linking
723
print 'second linking of a linking output'
724
open('moar.cpp', 'w').write(r'''
727
Moar() { std::cout << "moar!" << std::endl; }
731
Popen([PYTHON, EMCC, 'moar.cpp', '-o', 'moar.js', '-s', 'SIDE_MODULE=1', '-O2']).communicate()
732
Popen([PYTHON, EMLINK, 'together.js', 'moar.js', 'triple.js'], stdout=PIPE).communicate()
733
assert os.path.exists('triple.js')
734
for engine in JS_ENGINES:
735
out = run_js('triple.js', engine=engine, stderr=PIPE, full_output=True)
736
self.assertContained('moar!\nhello from main and hello from side\n', out)
737
if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out)
739
# zlib compression library. tests function pointers in initializers and many other things
740
test('zlib', '', open(path_from_root('tests', 'zlib', 'example.c'), 'r').read(),
741
self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']),
742
open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(),
743
args=['-I' + path_from_root('tests', 'zlib')], suffix='c')
745
# bullet physics engine. tests all the things
746
test('bullet', '', open(path_from_root('tests', 'bullet', 'Demos', 'HelloWorld', 'HelloWorld.cpp'), 'r').read(),
747
self.get_library('bullet', [os.path.join('src', '.libs', 'libBulletDynamics.a'),
748
os.path.join('src', '.libs', 'libBulletCollision.a'),
749
os.path.join('src', '.libs', 'libLinearMath.a')]),
750
[open(path_from_root('tests', 'bullet', 'output.txt'), 'r').read(), # different roundings
751
open(path_from_root('tests', 'bullet', 'output2.txt'), 'r').read(),
752
open(path_from_root('tests', 'bullet', 'output3.txt'), 'r').read()],
753
args=['-I' + path_from_root('tests', 'bullet', 'src')])
756
def test_outline(self):
757
def test(name, src, libs, expected, expected_ranges, args=[], suffix='cpp'):
760
def measure_funcs(filename):
765
for line in open(filename):
767
if line.startswith('function '):
770
elif line.startswith('}') and curr:
776
for debug, outlining_limits in [
780
(['-g'], (100, 250, 500, 1000, 2000, 5000, 0))
782
for outlining_limit in outlining_limits:
783
print '\n', Building.COMPILER_TEST_OPTS, debug, outlining_limit, '\n'
784
# TODO: test without -g3, tell all sorts
785
Popen([PYTHON, EMCC, src] + libs + ['-o', 'test.js', '-O2'] + debug + ['-s', 'OUTLINING_LIMIT=%d' % outlining_limit] + args).communicate()
786
assert os.path.exists('test.js')
787
shutil.copyfile('test.js', '%d_test.js' % outlining_limit)
788
for engine in JS_ENGINES:
789
out = run_js('test.js', engine=engine, stderr=PIPE, full_output=True)
790
self.assertContained(expected, out)
791
if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out)
793
low = expected_ranges[outlining_limit][0]
794
seen = max(measure_funcs('test.js').values())
795
high = expected_ranges[outlining_limit][1]
796
print Building.COMPILER_TEST_OPTS, outlining_limit, ' ', low, '<=', seen, '<=', high
797
assert low <= seen <= high
799
for test_opts, expected_ranges in [
819
Building.COMPILER_TEST_OPTS = test_opts
820
test('zlib', path_from_root('tests', 'zlib', 'example.c'),
821
self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']),
822
open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(),
824
args=['-I' + path_from_root('tests', 'zlib')], suffix='c')
826
def test_symlink(self):
828
return self.skip('Windows FS does not need to be tested for symlinks support, since it does not have them.')
829
open(os.path.join(self.get_dir(), 'foobar.xxx'), 'w').write('int main(){ return 0; }')
830
os.symlink(os.path.join(self.get_dir(), 'foobar.xxx'), os.path.join(self.get_dir(), 'foobar.c'))
831
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foobar.c'), '-o', os.path.join(self.get_dir(), 'foobar')], stdout=PIPE, stderr=PIPE).communicate()
832
assert os.path.exists(os.path.join(self.get_dir(), 'foobar'))
833
try_delete(os.path.join(self.get_dir(), 'foobar'))
834
try_delete(os.path.join(self.get_dir(), 'foobar.xxx'))
835
try_delete(os.path.join(self.get_dir(), 'foobar.c'))
837
open(os.path.join(self.get_dir(), 'foobar.c'), 'w').write('int main(){ return 0; }')
838
os.symlink(os.path.join(self.get_dir(), 'foobar.c'), os.path.join(self.get_dir(), 'foobar.xxx'))
839
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foobar.xxx'), '-o', os.path.join(self.get_dir(), 'foobar')], stdout=PIPE, stderr=PIPE).communicate()
840
assert os.path.exists(os.path.join(self.get_dir(), 'foobar'))
841
try_delete(os.path.join(self.get_dir(), 'foobar'))
842
try_delete(os.path.join(self.get_dir(), 'foobar.xxx'))
843
try_delete(os.path.join(self.get_dir(), 'foobar.c'))
845
def test_multiply_defined_libsymbols(self):
846
lib = "int mult() { return 1; }"
847
lib_name = os.path.join(self.get_dir(), 'libA.c')
848
open(lib_name, 'w').write(lib)
850
a2_name = os.path.join(self.get_dir(), 'a2.c')
851
open(a2_name, 'w').write(a2)
853
b2_name = os.path.join(self.get_dir(), 'b2.c')
854
open(b2_name, 'w').write(b2)
859
printf("result: %d\n", mult());
863
main_name = os.path.join(self.get_dir(), 'main.c')
864
open(main_name, 'w').write(main)
866
Building.emcc(lib_name, output_filename='libA.so')
868
Building.emcc(a2_name, ['-L.', '-lA'])
869
Building.emcc(b2_name, ['-L.', '-lA'])
871
Building.emcc(main_name, ['-L.', '-lA', a2_name+'.o', b2_name+'.o'], output_filename='a.out.js')
873
self.assertContained('result: 1', run_js(os.path.join(self.get_dir(), 'a.out.js')))
875
def test_multiply_defined_libsymbols_2(self):
876
a = "int x() { return 55; }"
877
a_name = os.path.join(self.get_dir(), 'a.c')
878
open(a_name, 'w').write(a)
879
b = "int y() { return 2; }"
880
b_name = os.path.join(self.get_dir(), 'b.c')
881
open(b_name, 'w').write(b)
882
c = "int z() { return 5; }"
883
c_name = os.path.join(self.get_dir(), 'c.c')
884
open(c_name, 'w').write(c)
891
printf("result: %d\n", x() + y() + z());
895
main_name = os.path.join(self.get_dir(), 'main.c')
896
open(main_name, 'w').write(main)
898
Building.emcc(a_name) # a.c.o
899
Building.emcc(b_name) # b.c.o
900
Building.emcc(c_name) # c.c.o
901
lib_name = os.path.join(self.get_dir(), 'libLIB.a')
902
Building.emar('cr', lib_name, [a_name + '.o', b_name + '.o']) # libLIB.a with a and b
904
# a is in the lib AND in an .o, so should be ignored in the lib. We do still need b from the lib though
905
Building.emcc(main_name, ['-L.', '-lLIB', a_name+'.o', c_name + '.o'], output_filename='a.out.js')
907
self.assertContained('result: 62', run_js(os.path.join(self.get_dir(), 'a.out.js')))
909
def test_redundant_link(self):
910
lib = "int mult() { return 1; }"
911
lib_name = os.path.join(self.get_dir(), 'libA.c')
912
open(lib_name, 'w').write(lib)
917
printf("result: %d\n", mult());
921
main_name = os.path.join(self.get_dir(), 'main.c')
922
open(main_name, 'w').write(main)
924
Building.emcc(lib_name, output_filename='libA.so')
926
Building.emcc(main_name, ['libA.so']*2, output_filename='a.out.js')
928
self.assertContained('result: 1', run_js(os.path.join(self.get_dir(), 'a.out.js')))
930
def test_export_all(self):
933
void libf1() { printf("libf1\n"); }
934
void libf2() { printf("libf2\n"); }
936
lib_name = os.path.join(self.get_dir(), 'lib.c')
937
open(lib_name, 'w').write(lib)
939
open('main.js', 'w').write('''
944
Building.emcc(lib_name, ['-s', 'EXPORT_ALL=1', '--post-js', 'main.js'], output_filename='a.out.js')
946
self.assertContained('libf1\nlibf2\n', run_js(os.path.join(self.get_dir(), 'a.out.js')))
948
def test_stdin(self):
949
Building.emcc(path_from_root('tests', 'module', 'test_stdin.c'), output_filename='a.out.js')
950
open('in.txt', 'w').write('abcdef\nghijkl')
952
for engine in JS_ENGINES:
953
print >> sys.stderr, engine
954
if engine == NODE_JS: continue # FIXME
955
if engine == V8_ENGINE: continue # no stdin support in v8 shell
956
self.assertContained('abcdef\nghijkl\neof', run_js(os.path.join(self.get_dir(), 'a.out.js'), engine=engine, stdin=open('in.txt')))
958
def test_ungetc_fscanf(self):
959
open('main.cpp', 'w').write(r'''
961
int main(int argc, char const *argv[])
964
FILE* f = fopen("my_test.input", "r");
966
printf("cannot open file\n");
972
fscanf(f, "%3s", str);
977
open('my_test.input', 'w').write('abc')
978
Building.emcc('main.cpp', ['--embed-file', 'my_test.input'], output_filename='a.out.js')
979
self.assertContained('zyx', Popen(listify(JS_ENGINES[0]) + ['a.out.js'], stdout=PIPE, stderr=PIPE).communicate()[0])
981
def test_abspaths(self):
982
# Includes with absolute paths are generally dangerous, things like -I/usr/.. will get to system local headers, not our portable ones.
984
shutil.copyfile(path_from_root('tests', 'hello_world.c'), 'main.c')
986
for args, expected in [(['-I/usr/something'], True),
987
(['-L/usr/something'], True),
988
(['-I/usr/something', '-Wno-warn-absolute-paths'], False),
989
(['-L/usr/something', '-Wno-warn-absolute-paths'], False),
990
(['-Isubdir/something'], False),
991
(['-Lsubdir/something'], False),
993
err = Popen([PYTHON, EMCC, 'main.c'] + args, stderr=PIPE).communicate()[1]
994
assert ('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)' in err) == expected, err
996
def test_local_link(self):
997
# Linking a local library directly, like /usr/lib/libsomething.so, cannot work of course since it
998
# doesn't contain bitcode. However, when we see that we should look for a bitcode file for that
999
# library in the -L paths and system/lib
1000
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
1001
extern void printey();
1009
os.makedirs(os.path.join(self.get_dir(), 'subdir'));
1012
open(os.path.join(self.get_dir(), 'subdir', 'libfile.so'), 'w').write('this is not llvm bitcode!')
1014
open(os.path.join(self.get_dir(), 'libfile.cpp'), 'w').write('''
1017
printf("hello from lib\\n");
1021
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'libfile.cpp'), '-o', 'libfile.so']).communicate()
1022
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), os.path.join(self.get_dir(), 'subdir', 'libfile.so'), '-L.'], stderr=PIPE).communicate()
1023
self.assertContained('hello from lib', run_js(os.path.join(self.get_dir(), 'a.out.js')))
1025
def test_runtimelink_multi(self):
1026
return self.skip('BUILD_AS_SHARED_LIB=2 is deprecated')
1027
if Settings.ASM_JS: return self.skip('asm does not support runtime linking yet')
1029
if SPIDERMONKEY_ENGINE not in JS_ENGINES: return self.skip('cannot run without spidermonkey due to node limitations')
1031
open('testa.h', 'w').write(r'''
1042
open('testb.h', 'w').write(r'''
1053
open('testa.cpp', 'w').write(r'''
1061
open('testb.cpp', 'w').write(r'''
1069
TestA* testa = new TestA();
1072
open('main.cpp', 'w').write(r'''
1079
int main(int argc, char** argv) {
1081
TestA* testa = new TestA();
1082
TestB* testb = new TestB();
1086
Popen([PYTHON, EMCC, 'testa.cpp', '-o', 'liba.js', '-s', 'BUILD_AS_SHARED_LIB=2', '-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-I.']).communicate()
1087
Popen([PYTHON, EMCC, 'testb.cpp', '-o', 'libb.js', '-s', 'BUILD_AS_SHARED_LIB=2', '-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-I.']).communicate()
1088
Popen([PYTHON, EMCC, 'main.cpp', '-o', 'main.js', '-s', 'RUNTIME_LINKED_LIBS=["liba.js", "libb.js"]', '-s', 'NAMED_GLOBALS=1', '-I.', '-s', 'LINKABLE=1']).communicate()
1090
Popen([PYTHON, EMCC, 'main.cpp', 'testa.cpp', 'testb.cpp', '-o', 'full.js', '-I.']).communicate()
1092
self.assertContained('TestA\nTestB\nTestA\n', run_js('main.js', engine=SPIDERMONKEY_ENGINE))
1094
def test_js_libraries(self):
1095
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
1098
extern void printey();
1099
extern int calcey(int x, int y);
1103
printf("*%d*\\n", calcey(10, 22));
1107
open(os.path.join(self.get_dir(), 'mylib1.js'), 'w').write('''
1108
mergeInto(LibraryManager.library, {
1109
printey: function() {
1110
Module.print('hello from lib!');
1114
open(os.path.join(self.get_dir(), 'mylib2.js'), 'w').write('''
1115
mergeInto(LibraryManager.library, {
1116
calcey: function(x, y) {
1122
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--js-library', os.path.join(self.get_dir(), 'mylib1.js'),
1123
'--js-library', os.path.join(self.get_dir(), 'mylib2.js')]).communicate()
1124
self.assertContained('hello from lib!\n*32*\n', run_js(os.path.join(self.get_dir(), 'a.out.js')))
1126
def test_identical_basenames(self):
1127
# Issue 287: files in different dirs but with the same basename get confused as the same,
1128
# causing multiply defined symbol errors
1130
os.makedirs(os.path.join(self.get_dir(), 'foo'));
1134
os.makedirs(os.path.join(self.get_dir(), 'bar'));
1137
open(os.path.join(self.get_dir(), 'foo', 'main.cpp'), 'w').write('''
1138
extern void printey();
1144
open(os.path.join(self.get_dir(), 'bar', 'main.cpp'), 'w').write('''
1146
void printey() { printf("hello there\\n"); }
1149
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foo', 'main.cpp'), os.path.join(self.get_dir(), 'bar', 'main.cpp')]).communicate()
1150
self.assertContained('hello there', run_js(os.path.join(self.get_dir(), 'a.out.js')))
1152
# ditto with first creating .o files
1153
try_delete(os.path.join(self.get_dir(), 'a.out.js'))
1154
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foo', 'main.cpp'), '-o', os.path.join(self.get_dir(), 'foo', 'main.o')]).communicate()
1155
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'bar', 'main.cpp'), '-o', os.path.join(self.get_dir(), 'bar', 'main.o')]).communicate()
1156
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foo', 'main.o'), os.path.join(self.get_dir(), 'bar', 'main.o')]).communicate()
1157
self.assertContained('hello there', run_js(os.path.join(self.get_dir(), 'a.out.js')))
1159
def test_main_a(self):
1160
# if main() is in a .a, we need to pull in that .a
1162
main_name = os.path.join(self.get_dir(), 'main.c')
1163
open(main_name, 'w').write(r'''
1167
printf("result: %d.\n", f());
1172
other_name = os.path.join(self.get_dir(), 'other.c')
1173
open(other_name, 'w').write(r'''
1175
int f() { return 12346; }
1178
Popen([PYTHON, EMCC, main_name, '-c', '-o', main_name+'.bc']).communicate()
1179
Popen([PYTHON, EMCC, other_name, '-c', '-o', other_name+'.bc']).communicate()
1181
Popen([PYTHON, EMAR, 'cr', main_name+'.a', main_name+'.bc']).communicate()
1183
Popen([PYTHON, EMCC, other_name+'.bc', main_name+'.a']).communicate()
1185
self.assertContained('result: 12346.', run_js(os.path.join(self.get_dir(), 'a.out.js')))
1187
def test_dup_o_in_a(self):
1188
open('common.c', 'w').write(r'''
1194
Popen([PYTHON, EMCC, 'common.c', '-c', '-o', 'common.o']).communicate()
1195
Popen([PYTHON, EMAR, 'rc', 'liba.a', 'common.o']).communicate()
1197
open('common.c', 'w').write(r'''
1203
Popen([PYTHON, EMCC, 'common.c', '-c', '-o', 'common.o']).communicate()
1204
Popen([PYTHON, EMAR, 'rc', 'libb.a', 'common.o']).communicate()
1206
open('main.c', 'w').write(r'''
1214
Popen([PYTHON, EMCC, 'main.c', '-L.', '-la', '-lb']).communicate()
1216
self.assertContained('a\nb\n', run_js(os.path.join(self.get_dir(), 'a.out.js')))
1218
def test_export_in_a(self):
1219
export_name = 'this_is_an_entry_point'
1221
open('export.c', 'w').write(r'''
1224
printf("Hello, world!\n");
1227
Popen([PYTHON, EMCC, 'export.c', '-c', '-o', 'export.o']).communicate()
1228
Popen([PYTHON, EMAR, 'rc', 'libexport.a', 'export.o']).communicate()
1230
open('main.c', 'w').write(r'''
1236
definition = 'function _%s(' % export_name
1238
# Sanity check: the symbol should not be linked in if not requested.
1239
Popen([PYTHON, EMCC, 'main.c', '-L.', '-lexport']).communicate()
1240
self.assertNotContained(definition, open(os.path.join(self.get_dir(), 'a.out.js')).read())
1242
# Sanity check: exporting without a definition does not cause it to appear.
1243
# Note: exporting main prevents emcc from warning that it generated no code.
1244
Popen([PYTHON, EMCC, 'main.c', '-s', '''EXPORTED_FUNCTIONS=['_main', '_%s']''' % export_name]).communicate()
1245
self.assertNotContained(definition, open(os.path.join(self.get_dir(), 'a.out.js')).read())
1247
# Actual test: defining symbol in library and exporting it causes it to appear in the output.
1248
Popen([PYTHON, EMCC, 'main.c', '-L.', '-lexport', '-s', '''EXPORTED_FUNCTIONS=['_%s']''' % export_name]).communicate()
1249
self.assertContained(definition, open(os.path.join(self.get_dir(), 'a.out.js')).read())
1251
def test_embed_file(self):
1252
open(os.path.join(self.get_dir(), 'somefile.txt'), 'w').write('''hello from a file with lots of data and stuff in it thank you very much''')
1253
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r'''
1256
FILE *f = fopen("somefile.txt", "r");
1258
fread(buf, 1, 20, f);
1261
printf("|%s|\n", buf);
1266
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt']).communicate()
1267
self.assertContained('|hello from a file wi|', run_js(os.path.join(self.get_dir(), 'a.out.js')))
1269
# preload twice, should not err
1270
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt', '--embed-file', 'somefile.txt']).communicate()
1271
self.assertContained('|hello from a file wi|', run_js(os.path.join(self.get_dir(), 'a.out.js')))
1273
def test_embed_file_dup(self):
1274
try_delete(os.path.join(self.get_dir(), 'tst'))
1275
os.mkdir(os.path.join(self.get_dir(), 'tst'))
1276
os.mkdir(os.path.join(self.get_dir(), 'tst', 'test1'))
1277
os.mkdir(os.path.join(self.get_dir(), 'tst', 'test2'))
1279
open(os.path.join(self.get_dir(), 'tst', 'aa.txt'), 'w').write('''frist''')
1280
open(os.path.join(self.get_dir(), 'tst', 'test1', 'aa.txt'), 'w').write('''sacond''')
1281
open(os.path.join(self.get_dir(), 'tst', 'test2', 'aa.txt'), 'w').write('''thard''')
1282
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r'''
1285
void print_file(const char *name) {
1286
FILE *f = fopen(name, "r");
1288
memset(buf, 0, 100);
1289
fread(buf, 1, 20, f);
1292
printf("|%s|\n", buf);
1295
print_file("tst/aa.txt");
1296
print_file("tst/test1/aa.txt");
1297
print_file("tst/test2/aa.txt");
1302
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'tst']).communicate()
1303
self.assertContained('|frist|\n|sacond|\n|thard|\n', run_js(os.path.join(self.get_dir(), 'a.out.js')))
1305
def test_multidynamic_link(self):
1306
# Linking the same dynamic library in will error, normally, since we statically link it, causing dupe symbols
1307
# A workaround is to use --ignore-dynamic-linking, see emcc --help for details
1309
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r'''
1311
extern void printey();
1312
extern void printother();
1325
os.makedirs(os.path.join(self.get_dir(), 'libdir'));
1329
open(os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), 'w').write('''
1332
printf("hello from lib");
1336
open(os.path.join(self.get_dir(), 'libdir', 'libother.cpp'), 'w').write('''
1338
extern void printey();
1346
# This lets us link the same dynamic lib twice. We will need to link it in manually at the end.
1347
compiler = [PYTHON, EMCC, '--ignore-dynamic-linking']
1349
# Build libfile normally into an .so
1350
Popen(compiler + [os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), '-o', os.path.join(self.get_dir(), 'libdir', 'libfile.so')]).communicate()
1351
# Build libother and dynamically link it to libfile - but add --ignore-dynamic-linking
1352
Popen(compiler + [os.path.join(self.get_dir(), 'libdir', 'libother.cpp'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile', '-o', os.path.join(self.get_dir(), 'libdir', 'libother.so')]).communicate()
1353
# Build the main file, linking in both the libs
1354
Popen(compiler + [os.path.join(self.get_dir(), 'main.cpp'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile', '-lother', '-c']).communicate()
1356
# The normal build system is over. We need to do an additional step to link in the dynamic libraries, since we ignored them before
1357
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.o'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile', '-lother']).communicate()
1359
self.assertContained('*hello from lib\n|hello from lib|\n*', run_js(os.path.join(self.get_dir(), 'a.out.js')))
1361
def test_js_link(self):
1362
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
1365
printf("hello from main\\n");
1369
open(os.path.join(self.get_dir(), 'before.js'), 'w').write('''
1370
var MESSAGE = 'hello from js';
1371
if (typeof Module != 'undefined') throw 'This code should run before anything else!';
1373
open(os.path.join(self.get_dir(), 'after.js'), 'w').write('''
1374
Module.print(MESSAGE);
1377
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'before.js', '--post-js', 'after.js']).communicate()
1378
self.assertContained('hello from main\nhello from js\n', run_js(os.path.join(self.get_dir(), 'a.out.js')))
1380
def test_sdl_endianness(self):
1381
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r'''
1383
#include <SDL/SDL.h>
1386
printf("%d, %d, %d\n", SDL_BYTEORDER, SDL_LIL_ENDIAN, SDL_BIG_ENDIAN);
1390
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')]).communicate()
1391
self.assertContained('1234, 1234, 4321\n', run_js(os.path.join(self.get_dir(), 'a.out.js')))
1393
def test_link_memcpy(self):
1394
# memcpy can show up *after* optimizations, so after our opportunity to link in libc, so it must be special-cased
1395
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r'''
1398
int main(int argc, char **argv) {
1399
int num = argc + 10;
1400
char buf[num], buf2[num];
1401
for (int i = 0; i < num; i++) {
1404
for (int i = 1; i < num; i++) {
1407
for (int i = 0; i < num; i++) {
1410
for (int i = 1; i < num; i++) {
1411
buf2[i] += buf2[i-1];
1413
for (int i = 0; i < num; i++) {
1414
printf("%d:%d\n", i, buf2[i]);
1419
Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'main.cpp')]).communicate()
1420
output = run_js(os.path.join(self.get_dir(), 'a.out.js'), full_output=True, stderr=PIPE)
1421
self.assertContained('''0:0
1433
self.assertNotContained('warning: library.js memcpy should not be running, it is only for testing!', output)
1435
def test_warn_undefined(self):
1436
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r'''
1449
def clear(): try_delete('a.out.js')
1451
for args in [[], ['-O2']]:
1454
output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-s', 'WARN_ON_UNDEFINED_SYMBOLS=1'] + args, stderr=PIPE).communicate()
1455
self.assertContained('unresolved symbol: something', output[1])
1458
output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')] + args, stderr=PIPE).communicate()
1459
self.assertNotContained('unresolved symbol: something\n', output[1])
1461
for args in [[], ['-O2']]:
1464
output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-s', 'ERROR_ON_UNDEFINED_SYMBOLS=1'] + args, stderr=PIPE).communicate()
1465
self.assertContained('unresolved symbol: something', output[1])
1466
assert not os.path.exists('a.out.js')
1469
output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')] + args, stderr=PIPE).communicate()
1470
self.assertNotContained('unresolved symbol: something\n', output[1])
1471
assert os.path.exists('a.out.js')
1473
def test_toobig(self):
1474
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r'''
1477
#define BYTES 100*1024*1024
1479
int main(int argc, char **argv) {
1481
static char buf[BYTES];
1482
static char buf2[BYTES];
1483
for (int i = 0; i < BYTES; i++) {
1487
for (int i = 0; i < BYTES; i++) {
1491
printf("%d\n", buf[10] + buf2[20]);
1496
output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')], stderr=PIPE).communicate()[1]
1497
assert 'Emscripten failed' in output, output
1498
assert 'warning: very large fixed-size structural type' in output, output
1500
def test_prepost(self):
1501
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
1504
printf("hello from main\\n");
1508
open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
1510
preRun: function() { Module.print('pre-run') },
1511
postRun: function() { Module.print('post-run') }
1515
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js']).communicate()
1516
self.assertContained('pre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js')))
1518
# never run, so no preRun or postRun
1519
src = open(os.path.join(self.get_dir(), 'a.out.js')).read().replace('// {{PRE_RUN_ADDITIONS}}', 'addRunDependency()')
1520
open(os.path.join(self.get_dir(), 'a.out.js'), 'w').write(src)
1521
self.assertNotContained('pre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js')))
1523
# noInitialRun prevents run
1524
for no_initial_run, run_dep in [(0, 0), (1, 0), (0, 1)]:
1525
print no_initial_run, run_dep
1526
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')]).communicate()
1527
src = 'var Module = { noInitialRun: %d };\n' % no_initial_run + open(os.path.join(self.get_dir(), 'a.out.js')).read()
1529
src = src.replace('// {{PRE_RUN_ADDITIONS}}', '// {{PRE_RUN_ADDITIONS}}\naddRunDependency("test");') \
1530
.replace('// {{POST_RUN_ADDITIONS}}', '// {{POST_RUN_ADDITIONS}}\nremoveRunDependency("test");')
1531
open(os.path.join(self.get_dir(), 'a.out.js'), 'w').write(src)
1532
assert ('hello from main' in run_js(os.path.join(self.get_dir(), 'a.out.js'))) != no_initial_run, 'only run if no noInitialRun'
1535
# Calling main later should still work, filesystem etc. must be set up.
1536
print 'call main later'
1537
src = open(os.path.join(self.get_dir(), 'a.out.js')).read() + '\nModule.callMain();\n';
1538
open(os.path.join(self.get_dir(), 'a.out.js'), 'w').write(src)
1539
assert 'hello from main' in run_js(os.path.join(self.get_dir(), 'a.out.js')), 'main should print when called manually'
1542
open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
1544
preRun: function() { Module.print('pre-run') },
1545
postRun: function() { Module.print('post-run') },
1546
preInit: function() { Module.print('pre-init') }
1549
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js']).communicate()
1550
self.assertContained('pre-init\npre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js')))
1552
def test_prepost2(self):
1553
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
1556
printf("hello from main\\n");
1560
open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
1562
preRun: function() { Module.print('pre-run') },
1565
open(os.path.join(self.get_dir(), 'pre2.js'), 'w').write('''
1566
Module.postRun = function() { Module.print('post-run') };
1568
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '--pre-js', 'pre2.js']).communicate()
1569
self.assertContained('pre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js')))
1571
def test_prepre(self):
1572
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
1575
printf("hello from main\\n");
1579
open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
1581
preRun: [function() { Module.print('pre-run') }],
1584
open(os.path.join(self.get_dir(), 'pre2.js'), 'w').write('''
1585
Module.preRun.push(function() { Module.print('prepre') });
1587
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '--pre-js', 'pre2.js']).communicate()
1588
self.assertContained('prepre\npre-run\nhello from main\n', run_js(os.path.join(self.get_dir(), 'a.out.js')))
1590
def test_save_bc(self):
1593
Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_loop_malloc.cpp')] + ([] if not save else ['--save-bc', self.in_dir('my_bitcode.bc')])).communicate()
1594
assert 'hello, world!' in run_js(self.in_dir('a.out.js'))
1595
assert os.path.exists(self.in_dir('my_bitcode.bc')) == save
1597
try_delete('a.out.js')
1598
Building.llvm_dis(self.in_dir('my_bitcode.bc'), self.in_dir('my_ll.ll'))
1600
os.environ['EMCC_LEAVE_INPUTS_RAW'] = '1'
1601
Popen([PYTHON, EMCC, 'my_ll.ll', '-o', 'two.js']).communicate()
1602
assert 'hello, world!' in run_js(self.in_dir('two.js'))
1604
del os.environ['EMCC_LEAVE_INPUTS_RAW']
1606
def test_fix_closure(self):
1607
input = path_from_root('tests', 'test-fix-closure.js')
1608
expected = path_from_root('tests', 'test-fix-closure.out.js')
1609
Popen([PYTHON, path_from_root('tools', 'fix_closure.py'), input, 'out.js']).communicate(input)
1610
output = open('out.js').read()
1611
assert '0,zzz_Q_39fa,0' in output
1612
assert 'function(a,c)' not in output # should be uninlined, so it gets a name
1613
assert run_js(input) == run_js('out.js')
1615
def test_js_optimizer(self):
1616
for input, expected, passes in [
1617
(path_from_root('tools', 'test-js-optimizer.js'), open(path_from_root('tools', 'test-js-optimizer-output.js')).read(),
1618
['hoistMultiples', 'loopOptimizer', 'removeAssignsToUndefined', 'simplifyExpressions']),
1619
(path_from_root('tools', 'test-js-optimizer-t2c.js'), open(path_from_root('tools', 'test-js-optimizer-t2c-output.js')).read(),
1620
['simplifyExpressions', 'optimizeShiftsConservative']),
1621
(path_from_root('tools', 'test-js-optimizer-t2.js'), open(path_from_root('tools', 'test-js-optimizer-t2-output.js')).read(),
1622
['simplifyExpressions', 'optimizeShiftsAggressive']),
1623
# Make sure that optimizeShifts handles functions with shift statements.
1624
(path_from_root('tools', 'test-js-optimizer-t3.js'), open(path_from_root('tools', 'test-js-optimizer-t3-output.js')).read(),
1625
['optimizeShiftsAggressive']),
1626
(path_from_root('tools', 'test-js-optimizer-regs.js'), open(path_from_root('tools', 'test-js-optimizer-regs-output.js')).read(),
1628
(path_from_root('tools', 'eliminator', 'eliminator-test.js'), open(path_from_root('tools', 'eliminator', 'eliminator-test-output.js')).read(),
1630
(path_from_root('tools', 'eliminator', 'safe-eliminator-test.js'), open(path_from_root('tools', 'eliminator', 'safe-eliminator-test-output.js')).read(),
1631
['eliminateMemSafe']),
1632
(path_from_root('tools', 'eliminator', 'asm-eliminator-test.js'), open(path_from_root('tools', 'eliminator', 'asm-eliminator-test-output.js')).read(),
1633
['asm', 'eliminate']),
1634
(path_from_root('tools', 'test-js-optimizer-asm-regs.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-output.js')).read(),
1635
['asm', 'registerize']),
1636
(path_from_root('tools', 'test-js-optimizer-asm-regs-min.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-min-output.js')).read(),
1637
['asm', 'registerize']),
1638
(path_from_root('tools', 'test-js-optimizer-asm-pre.js'), open(path_from_root('tools', 'test-js-optimizer-asm-pre-output.js')).read(),
1639
['asm', 'simplifyExpressions']),
1640
(path_from_root('tools', 'test-js-optimizer-asm-last.js'), open(path_from_root('tools', 'test-js-optimizer-asm-last-output.js')).read(),
1642
(path_from_root('tools', 'test-js-optimizer-asm-relocate.js'), open(path_from_root('tools', 'test-js-optimizer-asm-relocate-output.js')).read(),
1643
['asm', 'relocate']),
1644
(path_from_root('tools', 'test-js-optimizer-asm-outline1.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline1-output.js')).read(),
1645
['asm', 'outline']),
1646
(path_from_root('tools', 'test-js-optimizer-asm-outline2.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline2-output.js')).read(),
1647
['asm', 'outline']),
1648
(path_from_root('tools', 'test-js-optimizer-asm-outline3.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline3-output.js')).read(),
1649
['asm', 'outline']),
1652
output = Popen(listify(NODE_JS) + [path_from_root('tools', 'js-optimizer.js'), input] + passes, stdin=PIPE, stdout=PIPE).communicate()[0]
1653
self.assertIdentical(expected, output.replace('\r\n', '\n').replace('\n\n', '\n'))
1655
def test_m_mm(self):
1656
open(os.path.join(self.get_dir(), 'foo.c'), 'w').write('''#include <emscripten.h>''')
1657
for opt in ['M', 'MM']:
1658
output, err = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foo.c'), '-' + opt], stdout=PIPE, stderr=PIPE).communicate()
1659
assert 'foo.o: ' in output, '-%s failed to produce the right output: %s' % (opt, output)
1660
assert 'error' not in err, 'Unexpected stderr: ' + err
1662
def test_chunking(self):
1663
if os.environ.get('EMCC_DEBUG'): return self.skip('cannot run in debug mode')
1664
if os.environ.get('EMCC_CORES'): return self.skip('cannot run if cores are altered')
1665
if multiprocessing.cpu_count() < 2: return self.skip('need multiple cores')
1667
os.environ['EMCC_DEBUG'] = '1'
1668
os.environ['EMCC_CORES'] = '2'
1669
for asm, linkable, chunks, js_chunks in [
1670
(0, 0, 3, 2), (0, 1, 3, 4),
1671
(1, 0, 3, 2), (1, 1, 3, 4)
1673
print asm, linkable, chunks, js_chunks
1674
output, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_libcxx.cpp'), '-O1', '-s', 'LINKABLE=%d' % linkable, '-s', 'ASM_JS=%d' % asm] + (['-O2'] if asm else []), stdout=PIPE, stderr=PIPE).communicate()
1676
for c in range(chunks, chunks+2):
1677
ok = ok or ('phase 2 working on %d chunks' % c in err)
1680
for c in range(js_chunks, js_chunks+2):
1681
ok = ok or ('splitting up js optimization into %d chunks' % c in err)
1684
del os.environ['EMCC_DEBUG']
1685
del os.environ['EMCC_CORES']
1687
def test_debuginfo(self):
1688
if os.environ.get('EMCC_DEBUG'): return self.skip('cannot run in debug mode')
1690
os.environ['EMCC_DEBUG'] = '1'
1691
# llvm debug info is kept only when we can see it, which is without the js optimize, -O0. js debug info is lost by registerize in -O2, so - g disables it
1692
for args, expect_llvm, expect_js in [
1693
(['-O0'], True, True),
1694
(['-O0', '-g'], True, True),
1695
(['-O1'], False, True),
1696
(['-O1', '-g'], False, True),
1697
(['-O2'], False, False),
1698
(['-O2', '-g'], False, True),
1700
print args, expect_llvm, expect_js
1701
output, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp')] + args, stdout=PIPE, stderr=PIPE).communicate()
1702
assert expect_llvm == ('strip-debug' not in err)
1703
assert expect_js == ('registerize' not in err)
1705
del os.environ['EMCC_DEBUG']
1707
def test_scons(self): # also incidentally tests c++11 integration in llvm 3.1
1708
try_delete(os.path.join(self.get_dir(), 'test'))
1709
shutil.copytree(path_from_root('tests', 'scons'), os.path.join(self.get_dir(), 'test'))
1710
shutil.copytree(path_from_root('tools', 'scons', 'site_scons'), os.path.join(self.get_dir(), 'test', 'site_scons'))
1711
os.chdir(os.path.join(self.get_dir(), 'test'))
1712
Popen(['scons']).communicate()
1713
output = run_js('scons_integration.js')
1714
assert 'If you see this - the world is all right!' in output
1716
def test_embind(self):
1718
([], True), # without --bind, we fail
1719
(['--bind'], False),
1720
(['--bind', '-O1'], False),
1721
(['--bind', '-O2'], False),
1722
(['--bind', '-O1', '-s', 'ASM_JS=0'], False),
1723
(['--bind', '-O2', '-s', 'ASM_JS=0'], False)
1727
try_delete(self.in_dir('a.out.js'))
1728
Popen([PYTHON, EMCC, path_from_root('tests', 'embind', 'embind_test.cpp'), '--post-js', path_from_root('tests', 'embind', 'underscore-1.4.2.js'), '--post-js', path_from_root('tests', 'embind', 'imvu_test_adapter.js'), '--post-js', path_from_root('tests', 'embind', 'embind.test.js')] + args, stderr=PIPE if fail else None).communicate()
1729
assert os.path.exists(self.in_dir('a.out.js')) == (not fail)
1731
output = run_js(self.in_dir('a.out.js'), stdout=PIPE, stderr=PIPE, full_output=True)
1732
assert "FAIL" not in output, output
1734
def test_llvm_nativizer(self):
1736
Popen(['as', '--version'], stdout=PIPE, stderr=PIPE).communicate()
1738
return self.skip('no gnu as, cannot run nativizer')
1740
# avoid impure_ptr problems etc.
1741
shutil.copyfile(path_from_root('tests', 'files.cpp'), os.path.join(self.get_dir(), 'files.cpp'))
1742
open(os.path.join(self.get_dir(), 'somefile.binary'), 'w').write('''waka waka############################''')
1743
open(os.path.join(self.get_dir(), 'test.file'), 'w').write('''ay file..............,,,,,,,,,,,,,,''')
1744
open(os.path.join(self.get_dir(), 'stdin'), 'w').write('''inter-active''')
1745
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'files.cpp'), '-c']).communicate()
1746
Popen([PYTHON, path_from_root('tools', 'nativize_llvm.py'), os.path.join(self.get_dir(), 'files.o')], stdout=PIPE, stderr=PIPE).communicate(input)
1747
output = Popen([os.path.join(self.get_dir(), 'files.o.run')], stdin=open(os.path.join(self.get_dir(), 'stdin')), stdout=PIPE, stderr=PIPE).communicate()
1748
self.assertContained('''size: 37
1749
data: 119,97,107,97,32,119,97,107,97,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35
1750
loop: 119 97 107 97 32 119 97 107 97 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35
1758
self.assertIdentical('texte\n', output[1])
1760
def test_emconfig(self):
1761
output = Popen([PYTHON, EMCONFIG, 'LLVM_ROOT'], stdout=PIPE, stderr=PIPE).communicate()[0].strip()
1763
assert output == LLVM_ROOT
1765
print >> sys.stderr, 'Assertion failed: python %s LLVM_ROOT returned "%s" instead of expected "%s"!' % (EMCONFIG, output, LLVM_ROOT)
1767
invalid = 'Usage: em-config VAR_NAME'
1768
# Don't accept variables that do not exist
1769
output = Popen([PYTHON, EMCONFIG, 'VAR_WHICH_DOES_NOT_EXIST'], stdout=PIPE, stderr=PIPE).communicate()[0].strip()
1770
assert output == invalid
1771
# Don't accept no arguments
1772
output = Popen([PYTHON, EMCONFIG], stdout=PIPE, stderr=PIPE).communicate()[0].strip()
1773
assert output == invalid
1774
# Don't accept more than one variable
1775
output = Popen([PYTHON, EMCONFIG, 'LLVM_ROOT', 'EMCC'], stdout=PIPE, stderr=PIPE).communicate()[0].strip()
1776
assert output == invalid
1777
# Don't accept arbitrary python code
1778
output = Popen([PYTHON, EMCONFIG, 'sys.argv[1]'], stdout=PIPE, stderr=PIPE).communicate()[0].strip()
1779
assert output == invalid
1781
def test_link_s(self):
1782
# -s OPT=VALUE can conflict with -s as a linker option. We warn and ignore
1783
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r'''
1793
open(os.path.join(self.get_dir(), 'supp.cpp'), 'w').write(r'''
1802
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-o', 'main.o']).communicate()
1803
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'supp.cpp'), '-o', 'supp.o']).communicate()
1805
output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.o'), '-s', os.path.join(self.get_dir(), 'supp.o'), '-s', 'SAFE_HEAP=1'], stderr=PIPE).communicate()
1806
self.assertContained('treating -s as linker option', output[1])
1807
output = run_js('a.out.js')
1808
assert 'yello' in output, 'code works'
1809
code = open('a.out.js').read()
1810
assert 'SAFE_HEAP' in code, 'valid -s option had an effect'
1812
def test_jcache_printf(self):
1813
open(self.in_dir('src.cpp'), 'w').write(r'''
1816
#include <emscripten.h>
1818
emscripten_jcache_printf("hello world\n");
1819
emscripten_jcache_printf("hello %d world\n", 5);
1820
emscripten_jcache_printf("hello %.3f world\n", 123.456789123);
1821
emscripten_jcache_printf("hello %llx world\n", 0x1234567811223344ULL);
1825
Popen([PYTHON, EMCC, self.in_dir('src.cpp')]).communicate()
1826
output = run_js('a.out.js')
1827
self.assertIdentical('hello world\nhello 5 world\nhello 123.457 world\nhello 1234567811223300 world\n', output)
1829
def test_conftest_s_flag_passing(self):
1830
open(os.path.join(self.get_dir(), 'conftest.c'), 'w').write(r'''
1835
os.environ["EMMAKEN_JUST_CONFIGURE"] = "1"
1836
cmd = [PYTHON, EMCC, '-s', 'ASSERTIONS=1', os.path.join(self.get_dir(), 'conftest.c'), '-o', 'conftest']
1837
output = Popen(cmd, stderr=PIPE).communicate()
1838
del os.environ["EMMAKEN_JUST_CONFIGURE"]
1839
self.assertNotContained('emcc: warning: treating -s as linker option', output[1])
1840
assert os.path.exists('conftest')
1842
def test_file_packager(self):
1847
open('data1.txt', 'w').write('data1')
1849
open('data2.txt', 'w').write('data2')
1850
# relative path to below the current dir is invalid
1851
out, err = Popen([PYTHON, FILE_PACKAGER, 'test.data', '--preload', '../data1.txt'], stdout=PIPE, stderr=PIPE).communicate()
1852
assert len(out) == 0
1853
assert 'below the current directory' in err
1854
# relative path that ends up under us is cool
1855
out, err = Popen([PYTHON, FILE_PACKAGER, 'test.data', '--preload', '../subdir/data2.txt'], stdout=PIPE, stderr=PIPE).communicate()
1857
assert 'below the current directory' not in err
1858
# direct path leads to the same code being generated - relative path does not make us do anything different
1859
out2, err2 = Popen([PYTHON, FILE_PACKAGER, 'test.data', '--preload', 'data2.txt'], stdout=PIPE, stderr=PIPE).communicate()
1860
assert len(out2) > 0
1861
assert 'below the current directory' not in err2
1863
return filter(lambda line: 'PACKAGE_UUID' not in line, txt.split('\n'))
1867
# sanity check that we do generate different code for different inputs
1868
out3, err3 = Popen([PYTHON, FILE_PACKAGER, 'test.data', '--preload', 'data2.txt', 'data2.txt@waka.txt'], stdout=PIPE, stderr=PIPE).communicate()
1872
def test_crunch(self):
1873
# crunch should not be run if a .crn exists that is more recent than the .dds
1874
shutil.copyfile(path_from_root('tests', 'ship.dds'), 'ship.dds')
1876
Popen([PYTHON, FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate()
1877
assert os.stat('test.data').st_size < 0.25*os.stat('ship.dds').st_size, 'Compressed should be much smaller than dds'
1878
crunch_time = os.stat('ship.crn').st_mtime
1879
dds_time = os.stat('ship.dds').st_mtime
1880
assert crunch_time > dds_time, 'Crunch is more recent'
1881
# run again, should not recrunch!
1883
Popen([PYTHON, FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate()
1884
assert crunch_time == os.stat('ship.crn').st_mtime, 'Crunch is unchanged'
1885
# update dds, so should recrunch
1887
os.utime('ship.dds', None)
1888
Popen([PYTHON, FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate()
1889
assert crunch_time < os.stat('ship.crn').st_mtime, 'Crunch was changed'
1891
def test_headless(self):
1892
if SPIDERMONKEY_ENGINE not in JS_ENGINES: return self.skip('cannot run without spidermonkey due to node limitations (Uint8ClampedArray etc.)')
1894
shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'example.png'))
1895
Popen([PYTHON, EMCC, path_from_root('tests', 'sdl_headless.c'), '-s', 'HEADLESS=1']).communicate()
1896
output = run_js('a.out.js', engine=SPIDERMONKEY_ENGINE, stderr=PIPE)
1900
you should see two lines of text in different colors and a blue rectangle
1901
SDL_Quit called (and ignored)
1903
''' in output, output
1905
def test_preprocess(self):
1908
out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-E'], stdout=PIPE).communicate()
1909
assert not os.path.exists('a.out.js')
1910
assert '''tests/hello_world.c"''' in out
1911
assert '''printf("hello, world!''' in out