diff options
Diffstat (limited to 'tests/runner.py')
-rwxr-xr-x | tests/runner.py | 583 |
1 files changed, 507 insertions, 76 deletions
diff --git a/tests/runner.py b/tests/runner.py index e2bb13d4..27100cbb 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -279,8 +279,6 @@ process(sys.argv[1]) sys.argv = map(lambda arg: arg if not arg.startswith('test_') else 'default.' + arg, sys.argv) -Cache.erase() # Wipe the cache, so that we always test populating it in the tests, benchmarks, etc. - if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): # Tests @@ -430,49 +428,38 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): def test_i64(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('i64 mode 1 requires ta2') - for i64_mode in [0,1]: - if i64_mode == 0 and Settings.USE_TYPED_ARRAYS != 0: continue # Typed arrays truncate i64 - if i64_mode == 1 and Settings.QUANTUM_SIZE == 1: continue # TODO: i64 mode 1 for q1 + src = ''' + #include <stdio.h> + int main() + { + long long a = 0x2b00505c10; + long long b = a >> 29; + long long c = a >> 32; + long long d = a >> 34; + printf("*%Ld,%Ld,%Ld,%Ld*\\n", a, b, c, d); + unsigned long long ua = 0x2b00505c10; + unsigned long long ub = ua >> 29; + unsigned long long uc = ua >> 32; + unsigned long long ud = ua >> 34; + printf("*%Ld,%Ld,%Ld,%Ld*\\n", ua, ub, uc, ud); + + long long x = 0x0000def123450789ULL; // any bigger than this, and we + long long y = 0x00020ef123456089ULL; // start to run into the double precision limit! + printf("*%Ld,%Ld,%Ld,%Ld,%Ld*\\n", x, y, x | y, x & y, x ^ y, x >> 2, y << 2); - Settings.I64_MODE = i64_mode - src = ''' - #include <stdio.h> - int main() - { - long long a = 0x2b00505c10; - long long b = a >> 29; - long long c = a >> 32; - long long d = a >> 34; - printf("*%Ld,%Ld,%Ld,%Ld*\\n", a, b, c, d); - unsigned long long ua = 0x2b00505c10; - unsigned long long ub = ua >> 29; - unsigned long long uc = ua >> 32; - unsigned long long ud = ua >> 34; - printf("*%Ld,%Ld,%Ld,%Ld*\\n", ua, ub, uc, ud); - - long long x = 0x0000def123450789ULL; // any bigger than this, and we - long long y = 0x00020ef123456089ULL; // start to run into the double precision limit! - printf("*%Ld,%Ld,%Ld,%Ld,%Ld*\\n", x, y, x | y, x & y, x ^ y, x >> 2, y << 2); - - printf("*"); - long long z = 13; - int n = 0; - while (z > 1) { - printf("%.2f,", (float)z); // these must be integers! - z = z >> 1; - n++; - } - printf("*%d*\\n", n); - return 0; + printf("*"); + long long z = 13; + int n = 0; + while (z > 1) { + printf("%.2f,", (float)z); // these must be integers! + z = z >> 1; + n++; } - ''' - self.do_run(src, '*184688860176,344,43,10*\n*184688860176,344,43,10*\n*245127260211081,579378795077769,808077213656969,16428841631881,791648372025088*\n*13.00,6.00,3.00,*3*') - - if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: i64 mode 1 for q1') - - # Stuff that only works in i64_mode = 1 - - Settings.I64_MODE = 1 + printf("*%d*\\n", n); + return 0; + } + ''' + self.do_run(src, '*184688860176,344,43,10*\n*184688860176,344,43,10*\n*245127260211081,579378795077769,808077213656969,16428841631881,791648372025088*\n*13.00,6.00,3.00,*3*') src = r''' #include <time.h> @@ -653,6 +640,166 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): self.do_run(src, '*1*\n*0*\n*0*\n') + def test_i64_b(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + + src = r''' + #include <stdio.h> + #include <sys/time.h> + + typedef long long int64; + + #define PRMJ_USEC_PER_SEC 1000000L + + int main(int argc, char * argv[]) { + int64 sec = 1329409675 + argc; + int64 usec = 2329509675; + int64 mul = int64(sec) * PRMJ_USEC_PER_SEC; + int64 add = mul + int64(usec); + int add_low = add; + int add_high = add >> 32; + printf("*%lld,%lld,%u,%u*\n", mul, add, add_low, add_high); + return 0; + } + ''' + + self.do_run(src, '*1329409676000000,1329412005509675,3663280683,309527*\n') + + def test_i64_cmp(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + + src = r''' + #include <stdio.h> + + typedef long long int64; + + bool compare(int64 val) { + return val == -12; + } + + bool compare2(int64 val) { + return val < -12; + } + + int main(int argc, char * argv[]) { + printf("*%d,%d,%d,%d,%d,%d*\n", argc, compare(argc-1-12), compare(1000+argc), compare2(argc-1-10), compare2(argc-1-14), compare2(argc+1000)); + return 0; + } + ''' + + self.do_run(src, '*1,1,0,0,1,0*\n') + + def test_i64_cmp2(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + src = r''' + #include <inttypes.h> + #include <stdio.h> + + typedef int32_t INT32; + typedef int64_t INT64; + typedef uint8_t UINT8; + + void interface_clock_changed() + { + UINT8 m_divshift; + INT32 m_divisor; + + //INT64 attos = m_attoseconds_per_cycle; + INT64 attos = 279365114840; + m_divshift = 0; + while (attos >= (1UL << 31)) + { + m_divshift++; + printf("m_divshift is %i, on %Ld >?= %lu\n", m_divshift, attos, 1UL << 31); + attos >>= 1; + } + m_divisor = attos; + + printf("m_divisor is %i\n",m_divisor); + } + + int main() { + interface_clock_changed(); + return 0; + } + ''' + self.do_run(src, '''m_divshift is 1, on 279365114840 >?= 2147483648 +m_divshift is 2, on 139682557420 >?= 2147483648 +m_divshift is 3, on 69841278710 >?= 2147483648 +m_divshift is 4, on 34920639355 >?= 2147483648 +m_divshift is 5, on 17460319677 >?= 2147483648 +m_divshift is 6, on 8730159838 >?= 2147483648 +m_divshift is 7, on 4365079919 >?= 2147483648 +m_divshift is 8, on 2182539959 >?= 2147483648 +m_divisor is 1091269979 +''') + + def test_i64_double(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + src = r''' + #include <stdio.h> + + typedef long long int64; + #define JSDOUBLE_HI32_SIGNBIT 0x80000000 + + bool JSDOUBLE_IS_NEGZERO(double d) + { + union { + struct { + unsigned int lo, hi; + } s; + double d; + } x; + if (d != 0) + return false; + x.d = d; + return (x.s.hi & JSDOUBLE_HI32_SIGNBIT) != 0; + } + + bool JSINT64_IS_NEGZERO(int64 l) + { + union { + int64 i; + double d; + } x; + if (l != 0) + return false; + x.i = l; + return x.d == -0; + } + + int main(int argc, char * argv[]) { + printf("*%d,%d,%d,%d*\n", JSDOUBLE_IS_NEGZERO(0), JSDOUBLE_IS_NEGZERO(-0), JSDOUBLE_IS_NEGZERO(-1), JSDOUBLE_IS_NEGZERO(+1)); + printf("*%d,%d,%d,%d*\n", JSINT64_IS_NEGZERO(0), JSINT64_IS_NEGZERO(-0), JSINT64_IS_NEGZERO(-1), JSINT64_IS_NEGZERO(+1)); + return 0; + } + ''' + self.do_run(src, '*0,0,0,0*\n*1,1,0,0*\n') # same as gcc + + def test_i64_umul(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + src = r''' + #include <inttypes.h> + #include <stdio.h> + + typedef uint32_t UINT32; + typedef uint64_t UINT64; + + int main() { + volatile UINT32 testu32a = 2375724032U; + UINT32 bigu32 = 0xffffffffU; + volatile UINT64 testu64a = 14746250828952703000U; + + while ((UINT64)testu32a * (UINT64)bigu32 < testu64a) { + printf("testu64a is %llu\n", testu64a); + testu64a /= 2; + } + + return 0; + } + ''' + self.do_run(src, 'testu64a is 14746250828952703000\n') + def test_unaligned(self): if Settings.QUANTUM_SIZE == 1: return self.skip('No meaning to unaligned addresses in q1') @@ -1319,6 +1466,41 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): ''' self.do_run(src, 'Assertion failed: 1 == false') + def test_longjmp(self): + src = r''' + #include <stdio.h> + #include <setjmp.h> + + static jmp_buf buf; + + void second(void) { + printf("second\n"); // prints + longjmp(buf,1); // jumps back to where setjmp was called - making setjmp now return 1 + } + + void first(void) { + second(); + printf("first\n"); // does not print + } + + int main() { + int x = 0; + if ( ! setjmp(buf) ) { + x++; + first(); // when executed, setjmp returns 0 + } else { // when longjmp jumps back, setjmp returns 1 + printf("main: %d\n", x); // prints + } + + return 0; + } + ''' + # gcc -O0 and -O2 differ in what they do with the saved state of local vars - and we match that + if self.emcc_args is None or ('-O1' not in self.emcc_args and '-O2' not in self.emcc_args): + self.do_run(src, 'second\nmain: 1\n') + else: + self.do_run(src, 'second\nmain: 0\n') + def test_exceptions(self): if Settings.QUANTUM_SIZE == 1: return self.skip("we don't support libcxx in q1") @@ -1446,8 +1628,7 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): self.do_run(src, 'success') def test_typed_exceptions(self): - return self.skip('TODO: fix this for llvm 3.0') - + Settings.DISABLE_EXCEPTION_CATCHING = 0 Settings.SAFE_HEAP = 0 # Throwing null will cause an ignorable null pointer access. Settings.EXCEPTION_DEBUG = 0 # Messes up expected output. src = open(path_from_root('tests', 'exceptions', 'typed.cpp'), 'r').read() @@ -1549,6 +1730,8 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): self.do_run(src, '*11,74,32,1012*\n*11*\n*22*') def test_dynamic_cast(self): + if self.emcc_args is None: return self.skip('need libcxxabi') + src = r''' #include <stdio.h> @@ -2104,6 +2287,8 @@ def process(filename): self.do_run(src, '*nameA,nameB*') def test_llvmswitch(self): + Settings.CORRECT_SIGNS = 1 + src = ''' #include <stdio.h> #include <string.h> @@ -2115,23 +2300,22 @@ def process(filename): case 'b': case 'c': return p-1; - case 'd': + case 0xfffffff1: return p+1; } return p; } int main( int argc, const char *argv[] ) { - printf("*%d,%d,%d,%d,%d*\\n", switcher('a'), switcher('b'), switcher('c'), switcher('d'), switcher('e')); + unsigned int x = 0xfffffff1; + x >>= 0; // force it to be unsigned for purpose of checking our switch comparison in signed/unsigned + printf("*%d,%d,%d,%d,%d,%d*\\n", switcher('a'), switcher('b'), switcher('c'), switcher(x), switcher(-15), switcher('e')); return 0; } ''' - self.do_run(src, '*96,97,98,101,101*') + self.do_run(src, '*96,97,98,-14,-14,101*') def test_indirectbr(self): - if Settings.USE_TYPED_ARRAYS == 2: - Settings.I64_MODE = 1 # Unsafe optimizations use 64-bit load/store on two i32s - src = ''' #include <stdio.h> int main(void) { @@ -2439,6 +2623,27 @@ def process(filename): self.do_run(src, '*1*', force_c=True) + def test_atexit(self): + # Confirms they are called in reverse order + src = r''' + #include <stdio.h> + #include <stdlib.h> + + static void cleanA() { + printf("A"); + } + static void cleanB() { + printf("B"); + } + + int main() { + atexit(cleanA); + atexit(cleanB); + return 0; + } + ''' + self.do_run(src, 'BA') + def test_time(self): # XXX Not sure what the right output is here. Looks like the test started failing with daylight savings changes. Modified it to pass again. src = open(path_from_root('tests', 'time', 'src.c'), 'r').read() @@ -3292,15 +3497,12 @@ at function.:blag def test_parseInt(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('i64 mode 1 requires ta2') if Settings.QUANTUM_SIZE == 1: return self.skip('Q1 and I64_1 do not mix well yet') - Settings.I64_MODE = 1 # Necessary to prevent i64s being truncated into i32s, but we do still get doubling - # FIXME: The output here is wrong, due to double rounding of i64s! src = open(path_from_root('tests', 'parseInt', 'src.c'), 'r').read() expected = open(path_from_root('tests', 'parseInt', 'output.txt'), 'r').read() self.do_run(src, expected) def test_printf(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('i64 mode 1 requires ta2') - Settings.I64_MODE = 1 self.banned_js_engines = [NODE_JS, V8_ENGINE] # SpiderMonkey and V8 do different things to float64 typed arrays, un-NaNing, etc. src = open(path_from_root('tests', 'printf', 'test.c'), 'r').read() expected = [open(path_from_root('tests', 'printf', 'output.txt'), 'r').read(), @@ -3493,6 +3695,37 @@ def process(filename): self.do_run(src, 'size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\ntexte\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\n', post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h']) + def test_files_m(self): + # Test for Module.stdin etc. + + Settings.CORRECT_SIGNS = 1 + + post = ''' +def process(filename): + src = \'\'\' + var data = [10, 20, 40, 30]; + var Module = { + stdin: function() { return data.pop() || null }, + stdout: function(x) { print('got: ' + x) } + }; + \'\'\' + open(filename, 'r').read() + open(filename, 'w').write(src) +''' + src = r''' + #include <stdio.h> + #include <unistd.h> + + int main () { + char c; + fprintf(stderr, "isatty? %d,%d,%d\n", isatty(fileno(stdin)), isatty(fileno(stdout)), isatty(fileno(stderr))); + while ((c = fgetc(stdin)) != EOF) { + putc(c+5, stdout); + } + return 0; + } + ''' + self.do_run(src, 'isatty? 0,0,1\ngot: 35\ngot: 45\ngot: 25\ngot: 15\n', post_build=post) + def test_folders(self): add_pre_run = ''' def process(filename): @@ -3867,7 +4100,9 @@ def process(filename): printf( "%i %i %i", one, two, three ); } ''' - self.do_run(src, "1 2 3") + for linkable in [0, 1]: + Settings.LINKABLE = linkable # regression check for issue #273 + self.do_run(src, "1 2 3") def test_readdir(self): add_pre_run = ''' @@ -4203,7 +4438,7 @@ def process(filename): ''' # FIXME: should not have so many newlines in output here - self.do_run(src, 'hello world\n\n77.\n') + self.do_run(src, 'hello world\n77.\n') def test_stdvec(self): src = ''' @@ -4813,6 +5048,74 @@ Block 0: ''', post_build=post1) ### Integration tests + def test_ccall(self): + if self.emcc_args is not None and '-O2' in self.emcc_args: + self.emcc_args += ['--closure', '1'] # Use closure here, to test we export things right + + src = r''' + #include <stdio.h> + + // Optimizations might wipe out our functions without this + #define KEEPALIVE __attribute__((used)) + + extern "C" { + int KEEPALIVE get_int() { return 5; } + float KEEPALIVE get_float() { return 3.14; } + char * KEEPALIVE get_string() { return "hello world"; } + void KEEPALIVE print_int(int x) { printf("%d\n", x); } + void KEEPALIVE print_float(float x) { printf("%.2f\n", x); } + void KEEPALIVE print_string(char *x) { printf("%s\n", x); } + int KEEPALIVE multi(int x, float y, int z, char *str) { puts(str); return (x+y)*z; } + int * KEEPALIVE pointer(int *in) { printf("%d\n", *in); static int ret = 21; return &ret; } + } + + int main(int argc, char **argv) { + // keep them alive + if (argc == 10) return get_int(); + if (argc == 11) return get_float(); + if (argc == 12) return get_string()[0]; + if (argc == 13) print_int(argv[0][0]); + if (argc == 14) print_float(argv[0][0]); + if (argc == 15) print_string(argv[0]); + if (argc == 16) pointer((int*)argv[0]); + if (argc % 17 == 12) return multi(argc, float(argc)/2, argc+1, argv[0]); + return 0; + } + ''' + + post = ''' +def process(filename): + src = \'\'\' + var Module = { + 'postRun': function() { + print('*'); + var ret; + ret = ccall('get_int', 'number'); print([typeof ret, ret]); + ret = ccall('get_float', 'number'); print([typeof ret, ret.toFixed(2)]); + ret = ccall('get_string', 'string'); print([typeof ret, ret]); + ret = ccall('print_int', null, ['number'], [12]); print(typeof ret); + ret = ccall('print_float', null, ['number'], [14.56]); print(typeof ret); + ret = ccall('print_string', null, ['string'], ["cheez"]); print(typeof ret); + ret = ccall('multi', 'number', ['number', 'number', 'number', 'string'], [2, 1.4, 3, 'more']); print([typeof ret, ret]); + var p = ccall('malloc', 'pointer', ['number'], [4]); + setValue(p, 650, 'i32'); + ret = ccall('pointer', 'pointer', ['pointer'], [p]); print([typeof ret, getValue(ret, 'i32')]); + print('*'); + // part 2: cwrap + var multi = cwrap('multi', 'number', ['number', 'number', 'number', 'string']); + print(multi(2, 1.4, 3, 'atr')); + print(multi(8, 5.4, 4, 'bret')); + print('*'); + } + }; + \'\'\' + open(filename, 'r').read() + open(filename, 'w').write(src) +''' + + Settings.EXPORTED_FUNCTIONS = ['_get_int', '_get_float', '_get_string', '_print_int', '_print_float', '_print_string', '_multi', '_pointer', '_malloc'] + + self.do_run(src, '*\nnumber,5\nnumber,3.14\nstring,hello world\n12\nundefined\n14.56\nundefined\ncheez\nundefined\nmore\nnumber,10\n650\nnumber,21\n*\natr\n10\nbret\n53\n*\n', post_build=post) + def test_scriptaclass(self): header_filename = os.path.join(self.get_dir(), 'header.h') header = ''' @@ -5394,7 +5697,7 @@ def process(filename): } ''' - if Settings.I64_MODE == 0: # the errors here are very specific to non-i64 mode 1 + if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1 Settings.CORRECT_ROUNDINGS = 0 self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-6**5*') # JS floor operations, always to the negative. This is an undetected error here! self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # We get these right, since they are 32-bit and we can shortcut using the |0 trick @@ -5407,7 +5710,7 @@ def process(filename): self.do_run(src.replace('TYPE', 'unsigned int'), '*2147483645**2**-5**5*') # Correct Settings.CORRECT_SIGNS = 0 - if Settings.I64_MODE == 0: # the errors here are very specific to non-i64 mode 1 + if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1 Settings.CORRECT_ROUNDINGS = 2 Settings.CORRECT_ROUNDINGS_LINES = ["src.cpp:13"] # Fix just the last mistake self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-5**5*') @@ -5415,7 +5718,7 @@ def process(filename): self.do_run(src.replace('TYPE', 'unsigned int'), '*-3**2**-5**5*') # No such luck here # And reverse the check with = 2 - if Settings.I64_MODE == 0: # the errors here are very specific to non-i64 mode 1 + if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1 Settings.CORRECT_ROUNDINGS = 3 Settings.CORRECT_ROUNDINGS_LINES = ["src.cpp:999"] self.do_run(src.replace('TYPE', 'long long'), '*-2**2**-5**5*') @@ -5478,16 +5781,21 @@ def process(filename): def test_exit_status(self): Settings.CATCH_EXIT_CODE = 1 - src = ''' + src = r''' #include <stdio.h> #include <stdlib.h> + static void cleanup() { + printf("cleanup\n"); + } + int main() { - printf("hello, world!\\n"); + atexit(cleanup); // this atexit should still be called + printf("hello, world!\n"); exit(118); // Unusual exit status to make sure it's working! } ''' - self.do_run(src, 'hello, world!\nExit Status: 118') + self.do_run(src, 'hello, world!\ncleanup\nExit Status: 118') # Generate tests for everything @@ -5539,11 +5847,6 @@ class %s(T): Settings.CATCH_EXIT_CODE = 0 Settings.EMULATE_UNALIGNED_ACCESSES = int(Settings.USE_TYPED_ARRAYS == 2 and Building.LLVM_OPTS == 2) Settings.DOUBLE_MODE = 1 if Settings.USE_TYPED_ARRAYS and Building.LLVM_OPTS == 0 else 0 - if Settings.USE_TYPED_ARRAYS == 2: - Settings.I64_MODE = 1 - Settings.SAFE_HEAP = 1 # only checks for alignment problems, which is very important with unsafe opts - else: - Settings.I64_MODE = 0 Building.pick_llvm_opts(3) @@ -5577,9 +5880,6 @@ TT = %s del T # T is just a shape for the specific subclasses, we don't test it itself class other(RunnerCore): - def test_reminder(self): - assert 0, 'find appearances of i64 in src/, most are now unneeded' - def test_emcc(self): emcc_debug = os.environ.get('EMCC_DEBUG') @@ -5636,7 +5936,7 @@ Options that are modified or new in %s include: # emcc src.cpp -c and emcc src.cpp -o src.[o|bc] ==> should give a .bc file # regression check: -o js should create "js", with bitcode content - for args in [['-c'], ['-o', 'src.o'], ['-o', 'src.bc'], ['-o', 'js']]: + for args in [['-c'], ['-o', 'src.o'], ['-o', 'src.bc'], ['-o', 'src.so'], ['-o', 'js']]: target = args[1] if len(args) == 2 else 'hello_world.o' clear() Popen([compiler, path_from_root('tests', 'hello_world' + suffix)] + args, stdout=PIPE, stderr=PIPE).communicate() @@ -5650,6 +5950,15 @@ Options that are modified or new in %s include: assert os.path.exists(target + '.js'), 'Expected %s to exist since args are %s : %s' % (target + '.js', str(args), '\n'.join(output)) self.assertContained('hello, world!', run_js(target + '.js')) + # handle singleton archives + clear() + Popen([compiler, path_from_root('tests', 'hello_world' + suffix), '-o', 'a.bc'], stdout=PIPE, stderr=PIPE).communicate() + Popen([LLVM_AR, 'r', 'a.a', 'a.bc'], stdout=PIPE, stderr=PIPE).communicate() + assert os.path.exists('a.a') + output = Popen([compiler, 'a.a']).communicate() + assert os.path.exists('a.out.js'), output + self.assertContained('hello, world!', run_js('a.out.js')) + # emcc src.ll ==> generates .js clear() output = Popen([compiler, path_from_root('tests', 'hello_world.ll')], stdout=PIPE, stderr=PIPE).communicate() @@ -5742,12 +6051,12 @@ Options that are modified or new in %s include: for params, test, text in [ (['-s', 'INLINING_LIMIT=0'], lambda generated: 'function _dump' in generated, 'no inlining without opts'), (['-O1', '-s', 'INLINING_LIMIT=0'], lambda generated: 'function _dump' not in generated, 'inlining'), - (['-s', 'USE_TYPED_ARRAYS=0', '-s', 'I64_MODE=0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'), - (['-s', 'USE_TYPED_ARRAYS=1', '-s', 'I64_MODE=0'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'), + (['-s', 'USE_TYPED_ARRAYS=0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'), + (['-s', 'USE_TYPED_ARRAYS=1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'), ([], lambda generated: 'Module["_dump"]' not in generated, 'dump is not exported by default'), (['-s', 'EXPORTED_FUNCTIONS=["_main", "_dump"]'], lambda generated: 'Module["_dump"]' in generated, 'dump is now exported'), - (['--typed-arrays', '0', '-s', 'I64_MODE=0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'), - (['--typed-arrays', '1', '-s', 'I64_MODE=0'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'), + (['--typed-arrays', '0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'), + (['--typed-arrays', '1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'), (['--typed-arrays', '2'], lambda generated: 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 selected'), (['--llvm-opts', '1'], lambda generated: '_puts(' in generated, 'llvm opts requested'), ]: @@ -5884,13 +6193,134 @@ f.close() clear() output = Popen([EMCC, path_from_root('tests', 'hello_world_gles.c'), '-o', 'something.html', '-DHAVE_BUILTIN_SINCOS', - '-s', 'USE_TYPED_ARRAYS=0', '-s', 'I64_MODE=0', + '-s', 'USE_TYPED_ARRAYS=0', '--shell-file', path_from_root('tests', 'hello_world_gles_shell.html')], stdout=PIPE, stderr=PIPE).communicate() assert len(output[0]) == 0, output[0] assert os.path.exists('something.html'), output run_browser('something.html', 'You should not see animating gears.', '/report_gl_result?false') + def test_emcc_l_link(self): + # Linking with -lLIBNAME and -L/DIRNAME should work + + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' + extern void printey(); + int main() { + printey(); + return 0; + } + ''') + + try: + os.makedirs(os.path.join(self.get_dir(), 'libdir')); + except: + pass + + open(os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), 'w').write(''' + #include <stdio.h> + void printey() { + printf("hello from lib\\n"); + } + ''') + + Popen([EMCC, os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), '-c'], stdout=PIPE, stderr=STDOUT).communicate() + shutil.move(os.path.join(self.get_dir(), 'libfile.o'), os.path.join(self.get_dir(), 'libdir', 'libfile.so')) + Popen([EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile'], stdout=PIPE, stderr=STDOUT).communicate() + self.assertContained('hello from lib', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_emcc_embed_file(self): + 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''') + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' + #include <stdio.h> + int main() { + FILE *f = fopen("somefile.txt", "r"); + char buf[100]; + fread(buf, 1, 20, f); + buf[20] = 0; + fclose(f); + printf("|%s|\n", buf); + return 0; + } + ''') + + Popen([EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt']).communicate() + self.assertContained('|hello from a file wi|', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_emcc_multidynamic_link(self): + # Linking the same dynamic library in will error, normally, since we statically link it, causing dupe symbols + # A workaround is to use --ignore-dynamic-linking, see emcc --help for details + + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' + #include <stdio.h> + extern void printey(); + extern void printother(); + int main() { + printf("*"); + printey(); + printf("\n"); + printother(); + printf("\n"); + printf("*"); + return 0; + } + ''') + + try: + os.makedirs(os.path.join(self.get_dir(), 'libdir')); + except: + pass + + open(os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), 'w').write(''' + #include <stdio.h> + void printey() { + printf("hello from lib"); + } + ''') + + open(os.path.join(self.get_dir(), 'libdir', 'libother.cpp'), 'w').write(''' + #include <stdio.h> + extern void printey(); + void printother() { + printf("|"); + printey(); + printf("|"); + } + ''') + + # This lets us link the same dynamic lib twice. We will need to link it in manually at the end. + compiler = [EMCC, '--ignore-dynamic-linking'] + + # Build libfile normally into an .so + Popen(compiler + [os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), '-o', os.path.join(self.get_dir(), 'libdir', 'libfile.so')]).communicate() + # Build libother and dynamically link it to libfile - but add --ignore-dynamic-linking + 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() + # Build the main file, linking in both the libs + Popen(compiler + [os.path.join(self.get_dir(), 'main.cpp'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile', '-lother', '-c']).communicate() + + # The normal build system is over. We need to do an additional step to link in the dynamic libraries, since we ignored them before + Popen([EMCC, os.path.join(self.get_dir(), 'main.o'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile', '-lother']).communicate() + + self.assertContained('*hello from lib\n|hello from lib|\n*', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_emcc_js_link(self): + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' + #include <stdio.h> + int main() { + printf("hello from main\\n"); + return 0; + } + ''') + open(os.path.join(self.get_dir(), 'before.js'), 'w').write(''' + var MESSAGE = 'hello from js'; + if (typeof Module != 'undefined') throw 'This code should run before anything else!'; + ''') + open(os.path.join(self.get_dir(), 'after.js'), 'w').write(''' + print(MESSAGE); + ''') + + Popen([EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'before.js', '--post-js', 'after.js']).communicate() + self.assertContained('hello from main\nhello from js\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + def test_eliminator(self): input = open(path_from_root('tools', 'eliminator', 'eliminator-test.js')).read() expected = open(path_from_root('tools', 'eliminator', 'eliminator-test-output.js')).read() @@ -5936,6 +6366,7 @@ elif 'benchmark' in str(sys.argv): pass finally: os.chdir(d) + fingerprint.append('llvm: ' + LLVM_ROOT) print 'Running Emscripten benchmarks... [ %s ]' % ' | '.join(fingerprint) sys.argv = filter(lambda x: x != 'benchmark', sys.argv) |