~ubuntu-branches/ubuntu/vivid/emscripten/vivid

« back to all changes in this revision

Viewing changes to tests/test_other.py

  • Committer: Package Import Robot
  • Author(s): Sylvestre Ledru
  • Date: 2013-09-20 22:44:35 UTC
  • mfrom: (4.1.1 sid)
  • Revision ID: package-import@ubuntu.com-20130920224435-apuwj4fsl3fqv1a6
Tags: 1.5.6~20130920~6010666-1
* New snapshot release
* Update the list of supported architectures to the same as libv8
  (Closes: #723129)
* emlibtool has been removed from upstream.
* Fix warning syntax-error-in-dep5-copyright
* Refresh of the patches

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import multiprocessing, os, re, shutil, subprocess, sys
 
2
import tools.shared
 
3
from tools.shared import *
 
4
from runner import RunnerCore, path_from_root
 
5
 
 
6
class other(RunnerCore):
 
7
  def test_emcc(self):
 
8
    for compiler in [EMCC, EMXX]:
 
9
      shortcompiler = os.path.basename(compiler)
 
10
      suffix = '.c' if compiler == EMCC else '.cpp'
 
11
 
 
12
      # --version
 
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.
 
19
''', output)
 
20
 
 
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', ''))
 
24
 
 
25
      # --help
 
26
      output = Popen([PYTHON, compiler, '--help'], stdout=PIPE, stderr=PIPE).communicate()
 
27
      self.assertContained('''%s [options] file...
 
28
 
 
29
Most normal gcc/g++ options will work, for example:
 
30
  --help                   Display this information
 
31
  --version                Display compiler version information
 
32
 
 
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', ''))
 
36
 
 
37
      # emcc src.cpp ==> writes a.out.js
 
38
      self.clear()
 
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'))
 
43
 
 
44
      # properly report source code errors, and stop there
 
45
      self.clear()
 
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]
 
58
 
 
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'
 
63
        self.clear()
 
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')
 
69
          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'))
 
74
 
 
75
      # handle singleton archives
 
76
      self.clear()
 
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'))
 
83
 
 
84
      # emcc src.ll ==> generates .js
 
85
      self.clear()
 
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'))
 
90
 
 
91
      # emcc [..] -o [path] ==> should work with absolute paths
 
92
      try:
 
93
        for path in [os.path.abspath(os.path.join('..', 'file1.js')), os.path.join('b_dir', 'file2.js')]:
 
94
          print path
 
95
          self.clear(in_curr=True)
 
96
          os.chdir(self.get_dir())
 
97
          if not os.path.exists('a_dir'): os.mkdir('a_dir')
 
98
          os.chdir('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()
 
101
          print output
 
102
          assert os.path.exists(path), path + ' does not exist; ' + '\n'.join(output)
 
103
          last = os.getcwd()
 
104
          os.chdir(os.path.dirname(path))
 
105
          self.assertContained('hello, world!', run_js(os.path.basename(path)))
 
106
          os.chdir(last)
 
107
      finally:
 
108
        os.chdir(self.get_dir())
 
109
      self.clear()
 
110
 
 
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
 
115
        self.clear()
 
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'
 
121
 
 
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),
 
141
      ]:
 
142
        print params, opt_level, bc_params, closure, has_malloc
 
143
        self.clear()
 
144
        keep_debug = '-g' in params
 
145
        args = [PYTHON, compiler, path_from_root('tests', 'hello_world_loop' + ('_malloc' if has_malloc else '') + '.cpp')] + params
 
146
        print '..', args
 
147
        output = Popen(args,
 
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'))
 
158
 
 
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'
 
165
        if closure:
 
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)'
 
168
        else:
 
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'
 
171
          if keep_debug:
 
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'
 
180
 
 
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'),
 
205
      ]:
 
206
        print params, text
 
207
        self.clear()
 
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
 
213
 
 
214
      # Compiling two source files into a final JS.
 
215
      for args, target in [([], 'a.out.js'), (['-o', 'combined.js'], 'combined.js')]:
 
216
        self.clear()
 
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))
 
222
 
 
223
        # Compiling two files with -c will generate separate .bc files
 
224
        self.clear()
 
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()
 
227
        if '-o' in args:
 
228
          # specifying -o and -c is an error
 
229
          assert 'fatal error' in output[1], output[1]
 
230
          continue
 
231
 
 
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)
 
235
 
 
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))
 
241
        try_delete(target)
 
242
 
 
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))
 
247
 
 
248
        # Combining bc files into another bc should also work
 
249
        try_delete(target)
 
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'))
 
258
 
 
259
      # --js-transform <transform>
 
260
      self.clear()
 
261
      trans = os.path.join(self.get_dir(), 't.py')
 
262
      trans_file = open(trans, 'w')
 
263
      trans_file.write('''
 
264
import sys
 
265
f = open(sys.argv[1], 'w')
 
266
f.write('transformed!')
 
267
f.close()
 
268
''')
 
269
      trans_file.close()
 
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'
 
272
 
 
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.
 
276
 
 
277
  def test_cmake(self):
 
278
    # Test all supported generators.
 
279
    if WINDOWS:
 
280
      generators = ['MinGW Makefiles', 'NMake Makefiles']
 
281
    else:
 
282
      generators = ['Unix Makefiles']
 
283
 
 
284
    make_commands = { 'MinGW Makefiles': ['mingw32-make'], 'NMake Makefiles': ['nmake', '/NOLOGO'], 'Unix Makefiles': ['make'] }
 
285
 
 
286
    if os.name == 'nt':
 
287
      emconfigure = path_from_root('emconfigure.bat')
 
288
    else:
 
289
      emconfigure = path_from_root('emconfigure')
 
290
 
 
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.'
 
294
        continue
 
295
 
 
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'.
 
302
          # Test both methods.
 
303
          for invoke_method in ['cmake', 'emconfigure']:
 
304
 
 
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)
 
308
            try:
 
309
              os.chdir(tempdirname)
 
310
 
 
311
              verbose_level = int(os.getenv('EM_BUILD_VERBOSE')) if os.getenv('EM_BUILD_VERBOSE') != None else 0
 
312
              
 
313
              # Run Cmake
 
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]
 
318
              else:
 
319
                # Test invoking via 'emconfigure cmake'
 
320
                cmd = [emconfigure, 'cmake', '-DCMAKE_BUILD_TYPE=' + configuration, '-G', generator, cmakelistsdir]
 
321
 
 
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!'
 
330
 
 
331
              # Build
 
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]
 
341
 
 
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!'
 
346
            finally:
 
347
              os.chdir(path_from_root('tests')) # Move away from the directory we are about to remove.
 
348
              shutil.rmtree(tempdirname)
 
349
 
 
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!'
 
357
 
 
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!'
 
363
 
 
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!'
 
369
 
 
370
  def test_catch_undef(self):
 
371
    open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r'''
 
372
      #include <vector>
 
373
      #include <stdio.h>
 
374
 
 
375
      class Test {
 
376
      public:
 
377
        std::vector<int> vector;
 
378
      };
 
379
 
 
380
      Test globalInstance;
 
381
 
 
382
      int main() {
 
383
        printf("hello, world!\n");
 
384
        return 0;
 
385
      }
 
386
    ''')
 
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')))
 
389
 
 
390
  def test_unaligned_memory(self):
 
391
    open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r'''
 
392
      #include <stdio.h>
 
393
 
 
394
      typedef unsigned char   Bit8u;
 
395
      typedef unsigned short  Bit16u;
 
396
      typedef unsigned int    Bit32u;
 
397
 
 
398
      int main()
 
399
      {
 
400
        Bit8u data[4] = {0x01,0x23,0x45,0x67};
 
401
 
 
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));
 
405
      }
 
406
    ''')
 
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')))
 
409
 
 
410
  def test_unaligned_memory_2(self):
 
411
    open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r'''
 
412
      #include <string>
 
413
      #include <stdio.h>
 
414
 
 
415
      int main( int argc, char ** argv )
 
416
      {
 
417
          std::string testString( "Hello, World!" );
 
418
 
 
419
          printf( "testString = %s\n", testString.c_str() );
 
420
          return 0;
 
421
      }
 
422
      ''')
 
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')))
 
425
 
 
426
  def test_asm_minify(self):
 
427
    def test(args):
 
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()
 
431
 
 
432
    src = test([])
 
433
    assert 'function _malloc' in src
 
434
 
 
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
 
439
 
 
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
 
445
 
 
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
 
451
 
 
452
  def test_dangerous_func_cast(self):
 
453
    src = r'''
 
454
      #include <stdio.h>
 
455
      typedef void (*voidfunc)();
 
456
      int my_func() {
 
457
        printf("my func\n");
 
458
        return 10;
 
459
      }
 
460
      int main(int argc, char **argv) {
 
461
        voidfunc fps[10];
 
462
        for (int i = 0; i < 10; i++) fps[i] = (i == argc) ? (void (*)())my_func : NULL;
 
463
        fps[2*(argc-1) + 1]();
 
464
        return 0;
 
465
      }
 
466
    '''
 
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()
 
473
 
 
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
 
481
 
 
482
  def test_l_link(self):
 
483
    # Linking with -lLIBNAME and -L/DIRNAME should work
 
484
 
 
485
    open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
 
486
      extern void printey();
 
487
      int main() {
 
488
        printey();
 
489
        return 0;
 
490
      }
 
491
    ''')
 
492
 
 
493
    try:
 
494
      os.makedirs(os.path.join(self.get_dir(), 'libdir'));
 
495
    except:
 
496
      pass
 
497
 
 
498
    open(os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), 'w').write('''
 
499
      #include <stdio.h>
 
500
      void printey() {
 
501
        printf("hello from lib\\n");
 
502
      }
 
503
    ''')
 
504
 
 
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'
 
510
 
 
511
  def test_static_link(self):
 
512
    def test(name, header, main, side, expected, args=[], suffix='cpp', first=True):
 
513
      print name
 
514
      #t = main ; main = side ; side = t
 
515
      original_main = main
 
516
      original_side = side
 
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)
 
533
      if first:
 
534
        shutil.copyfile('together.js', 'first.js')
 
535
        test(name + ' (reverse)', header, original_side, original_main, expected, args, suffix, False) # test reverse order
 
536
 
 
537
    # test a simple call from one module to another. only one has a string (and constant memory initialization for it)
 
538
    test('basics', '', '''
 
539
      #include <stdio.h>
 
540
      extern int sidey();
 
541
      int main() {
 
542
        printf("other says %d.", sidey());
 
543
        return 0;
 
544
      }
 
545
    ''', '''
 
546
      int sidey() { return 11; }
 
547
    ''', 'other says 11.')
 
548
 
 
549
    # finalization of float variables should pass asm.js validation
 
550
    test('floats', '', '''
 
551
      #include <stdio.h>
 
552
      extern float sidey();
 
553
      int main() {
 
554
        printf("other says %.2f.", sidey()+1);
 
555
        return 0;
 
556
      }
 
557
    ''', '''
 
558
      float sidey() { return 11.5; }
 
559
    ''', 'other says 12.50')
 
560
 
 
561
    # memory initialization in both
 
562
    test('multiple memory inits', '', r'''
 
563
      #include <stdio.h>
 
564
      extern void sidey();
 
565
      int main() {
 
566
        printf("hello from main\n");
 
567
        sidey();
 
568
        return 0;
 
569
      }
 
570
    ''', r'''
 
571
      #include <stdio.h>
 
572
      void sidey() { printf("hello from side\n"); }
 
573
    ''', 'hello from main\nhello from side\n')
 
574
 
 
575
    # function pointers
 
576
    test('fp1', 'typedef void (*voidfunc)();', r'''
 
577
      #include <stdio.h>
 
578
      #include "header.h"
 
579
      voidfunc sidey(voidfunc f);
 
580
      void a() { printf("hello from funcptr\n"); }
 
581
      int main() {
 
582
        sidey(a)();
 
583
        return 0;
 
584
      }
 
585
    ''', '''
 
586
      #include "header.h"
 
587
      voidfunc sidey(voidfunc f) { return f; }
 
588
    ''', 'hello from funcptr\n')
 
589
 
 
590
    # function pointers with 'return' in the name
 
591
    test('fp2', 'typedef void (*voidfunc)();', r'''
 
592
      #include <stdio.h>
 
593
      #include "header.h"
 
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)]();
 
601
        return 0;
 
602
      }
 
603
    ''', '''
 
604
      #include "header.h"
 
605
      int sidey(voidfunc f) { if (f) f(); return 1; }
 
606
    ''', 'hello 1\n')
 
607
 
 
608
    # Global initializer
 
609
    test('global init', '', r'''
 
610
      #include <stdio.h>
 
611
      struct Class {
 
612
        Class() { printf("a new Class\n"); }
 
613
      };
 
614
      static Class c;
 
615
      int main() {
 
616
        return 0;
 
617
      }
 
618
    ''', r'''
 
619
      void nothing() {}
 
620
    ''', 'a new Class\n')
 
621
 
 
622
    # Multiple global initializers (LLVM generates overlapping names for them)
 
623
    test('global inits', r'''
 
624
      #include <stdio.h>
 
625
      struct Class {
 
626
        Class(const char *name) { printf("new %s\n", name); }
 
627
      };
 
628
    ''', r'''
 
629
      #include "header.h"
 
630
      static Class c("main");
 
631
      int main() {
 
632
        return 0;
 
633
      }
 
634
    ''', r'''
 
635
      #include "header.h"
 
636
      static Class c("side");
 
637
    ''', ['new main\nnew side\n', 'new side\nnew main\n'])
 
638
 
 
639
    # Class code used across modules
 
640
    test('codecall', r'''
 
641
      #include <stdio.h>
 
642
      struct Class {
 
643
        Class(const char *name);
 
644
      };
 
645
    ''', r'''
 
646
      #include "header.h"
 
647
      int main() {
 
648
        Class c("main");
 
649
        return 0;
 
650
      }
 
651
    ''', r'''
 
652
      #include "header.h"
 
653
      Class::Class(const char *name) { printf("new %s\n", name); }
 
654
    ''', ['new main\n'])
 
655
 
 
656
    # malloc usage in both modules
 
657
    test('malloc', r'''
 
658
      #include <stdlib.h>
 
659
      #include <string.h>
 
660
      char *side(const char *data);
 
661
    ''', r'''
 
662
      #include <stdio.h>
 
663
      #include "header.h"
 
664
      int main() {
 
665
        char *temp = side("hello through side\n");
 
666
        char *ret = (char*)malloc(strlen(temp)+1);
 
667
        strcpy(ret, temp);
 
668
        temp[1] = 'x';
 
669
        puts(ret);
 
670
        return 0;
 
671
      }
 
672
    ''', r'''
 
673
      #include "header.h"
 
674
      char *side(const char *data) {
 
675
        char *ret = (char*)malloc(strlen(data)+1);
 
676
        strcpy(ret, data);
 
677
        return ret;
 
678
      }
 
679
    ''', ['hello through side\n'])
 
680
 
 
681
    # libc usage in one modules. must force libc inclusion in the main module if that isn't the one using mallinfo()
 
682
    try:
 
683
      os.environ['EMCC_FORCE_STDLIBS'] = 'libc'
 
684
      test('malloc-1', r'''
 
685
        #include <string.h>
 
686
        int side();
 
687
      ''', r'''
 
688
        #include <stdio.h>
 
689
        #include "header.h"
 
690
        int main() {
 
691
          printf("|%d|\n", side());
 
692
          return 0;
 
693
        }
 
694
      ''', r'''
 
695
        #include <stdlib.h>
 
696
        #include <malloc.h>
 
697
        #include "header.h"
 
698
        int side() {
 
699
          struct mallinfo m = mallinfo();
 
700
          return m.arena > 1;
 
701
        }
 
702
      ''', ['|1|\n'])
 
703
    finally:
 
704
      del os.environ['EMCC_FORCE_STDLIBS']
 
705
 
 
706
    # iostream usage in one and std::string in both
 
707
    test('iostream', r'''
 
708
      #include <iostream>
 
709
      #include <string>
 
710
      std::string side();
 
711
    ''', r'''
 
712
      #include "header.h"
 
713
      int main() {
 
714
        std::cout << "hello from main " << side() << std::endl;
 
715
        return 0;
 
716
      }
 
717
    ''', r'''
 
718
      #include "header.h"
 
719
      std::string side() { return "and hello from side"; }
 
720
    ''', ['hello from main and hello from side\n'])
 
721
 
 
722
    # followup to iostream test: a second linking
 
723
    print 'second linking of a linking output'
 
724
    open('moar.cpp', 'w').write(r'''
 
725
      #include <iostream>
 
726
      struct Moar {
 
727
        Moar() { std::cout << "moar!" << std::endl; }
 
728
      };
 
729
      Moar m;
 
730
    ''')
 
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)
 
738
 
 
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')
 
744
 
 
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')])
 
754
 
 
755
 
 
756
  def test_outline(self):
 
757
    def test(name, src, libs, expected, expected_ranges, args=[], suffix='cpp'):
 
758
      print name
 
759
 
 
760
      def measure_funcs(filename):
 
761
        i = 0
 
762
        start = -1
 
763
        curr = None
 
764
        ret = {}
 
765
        for line in open(filename):
 
766
          i += 1
 
767
          if line.startswith('function '):
 
768
            start = i
 
769
            curr = line
 
770
          elif line.startswith('}') and curr:
 
771
            size = i - start
 
772
            ret[curr] = size
 
773
            curr = None
 
774
        return ret
 
775
 
 
776
      for debug, outlining_limits in [
 
777
        ([], (1000,)),
 
778
        (['-g1'], (1000,)),
 
779
        (['-g2'], (1000,)),
 
780
        (['-g'], (100, 250, 500, 1000, 2000, 5000, 0))
 
781
      ]:
 
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)
 
792
          if debug == ['-g']:
 
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
 
798
 
 
799
    for test_opts, expected_ranges in [
 
800
      ([], {
 
801
         100: (190, 275),
 
802
         250: (200, 500),
 
803
         500: (250, 500),
 
804
        1000: (230, 1000),
 
805
        2000: (380, 2000),
 
806
        5000: (800, 5000),
 
807
           0: (1500, 5000)
 
808
      }),
 
809
      (['-O2'], {
 
810
         100: (0, 1500),
 
811
         250: (0, 1500),
 
812
         500: (0, 1500),
 
813
        1000: (0, 1500),
 
814
        2000: (0, 2000),
 
815
        5000: (0, 5000),
 
816
           0: (0, 5000)
 
817
      }),
 
818
    ]:
 
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(),
 
823
                   expected_ranges,
 
824
                   args=['-I' + path_from_root('tests', 'zlib')], suffix='c')
 
825
 
 
826
  def test_symlink(self):
 
827
    if os.name == 'nt':
 
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'))
 
836
 
 
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'))
 
844
    
 
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)
 
849
    a2 = "void x() {}"
 
850
    a2_name = os.path.join(self.get_dir(), 'a2.c')
 
851
    open(a2_name, 'w').write(a2)
 
852
    b2 = "void y() {}"
 
853
    b2_name = os.path.join(self.get_dir(), 'b2.c')
 
854
    open(b2_name, 'w').write(b2)
 
855
    main = r'''
 
856
      #include <stdio.h>
 
857
      int mult();
 
858
      int main() {
 
859
        printf("result: %d\n", mult());
 
860
        return 0;
 
861
      }
 
862
    '''
 
863
    main_name = os.path.join(self.get_dir(), 'main.c')
 
864
    open(main_name, 'w').write(main)
 
865
 
 
866
    Building.emcc(lib_name, output_filename='libA.so')
 
867
 
 
868
    Building.emcc(a2_name, ['-L.', '-lA'])
 
869
    Building.emcc(b2_name, ['-L.', '-lA'])
 
870
 
 
871
    Building.emcc(main_name, ['-L.', '-lA', a2_name+'.o', b2_name+'.o'], output_filename='a.out.js')
 
872
 
 
873
    self.assertContained('result: 1', run_js(os.path.join(self.get_dir(), 'a.out.js')))
 
874
 
 
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)
 
885
    main = r'''
 
886
      #include <stdio.h>
 
887
      int x();
 
888
      int y();
 
889
      int z();
 
890
      int main() {
 
891
        printf("result: %d\n", x() + y() + z());
 
892
        return 0;
 
893
      }
 
894
    '''
 
895
    main_name = os.path.join(self.get_dir(), 'main.c')
 
896
    open(main_name, 'w').write(main)
 
897
 
 
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
 
903
 
 
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')
 
906
 
 
907
    self.assertContained('result: 62', run_js(os.path.join(self.get_dir(), 'a.out.js')))
 
908
 
 
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)
 
913
    main = r'''
 
914
      #include <stdio.h>
 
915
      int mult();
 
916
      int main() {
 
917
        printf("result: %d\n", mult());
 
918
        return 0;
 
919
      }
 
920
    '''
 
921
    main_name = os.path.join(self.get_dir(), 'main.c')
 
922
    open(main_name, 'w').write(main)
 
923
 
 
924
    Building.emcc(lib_name, output_filename='libA.so')
 
925
 
 
926
    Building.emcc(main_name, ['libA.so']*2, output_filename='a.out.js')
 
927
 
 
928
    self.assertContained('result: 1', run_js(os.path.join(self.get_dir(), 'a.out.js')))
 
929
 
 
930
  def test_export_all(self):
 
931
    lib = r'''
 
932
      #include <stdio.h>
 
933
      void libf1() { printf("libf1\n"); }
 
934
      void libf2() { printf("libf2\n"); }
 
935
    '''
 
936
    lib_name = os.path.join(self.get_dir(), 'lib.c')
 
937
    open(lib_name, 'w').write(lib)
 
938
 
 
939
    open('main.js', 'w').write('''
 
940
      _libf1();
 
941
      _libf2();
 
942
    ''')
 
943
 
 
944
    Building.emcc(lib_name, ['-s', 'EXPORT_ALL=1', '--post-js', 'main.js'], output_filename='a.out.js')
 
945
 
 
946
    self.assertContained('libf1\nlibf2\n', run_js(os.path.join(self.get_dir(), 'a.out.js')))
 
947
 
 
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')
 
951
 
 
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')))
 
957
 
 
958
  def test_ungetc_fscanf(self):
 
959
    open('main.cpp', 'w').write(r'''
 
960
      #include <stdio.h>
 
961
      int main(int argc, char const *argv[])
 
962
      {
 
963
          char str[4] = {0};
 
964
          FILE* f = fopen("my_test.input", "r");
 
965
          if (f == NULL) {
 
966
              printf("cannot open file\n");
 
967
              return -1;
 
968
          }
 
969
          ungetc('x', f);
 
970
          ungetc('y', f);
 
971
          ungetc('z', f);
 
972
          fscanf(f, "%3s", str);
 
973
          printf("%s\n", str);
 
974
          return 0;
 
975
      }
 
976
    ''')
 
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])
 
980
 
 
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.
 
983
 
 
984
    shutil.copyfile(path_from_root('tests', 'hello_world.c'), 'main.c')
 
985
 
 
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),
 
992
                           ([], 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
 
995
 
 
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();
 
1002
      int main() {
 
1003
        printey();
 
1004
        return 0;
 
1005
      }
 
1006
    ''')
 
1007
 
 
1008
    try:
 
1009
      os.makedirs(os.path.join(self.get_dir(), 'subdir'));
 
1010
    except:
 
1011
      pass
 
1012
    open(os.path.join(self.get_dir(), 'subdir', 'libfile.so'), 'w').write('this is not llvm bitcode!')
 
1013
 
 
1014
    open(os.path.join(self.get_dir(), 'libfile.cpp'), 'w').write('''
 
1015
      #include <stdio.h>
 
1016
      void printey() {
 
1017
        printf("hello from lib\\n");
 
1018
      }
 
1019
    ''')
 
1020
 
 
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')))
 
1024
 
 
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')
 
1028
 
 
1029
    if SPIDERMONKEY_ENGINE not in JS_ENGINES: return self.skip('cannot run without spidermonkey due to node limitations')
 
1030
 
 
1031
    open('testa.h', 'w').write(r'''
 
1032
      #ifndef _TESTA_H_
 
1033
      #define _TESTA_H_
 
1034
 
 
1035
      class TestA {
 
1036
        public:
 
1037
          TestA();
 
1038
      };
 
1039
 
 
1040
      #endif
 
1041
    ''')
 
1042
    open('testb.h', 'w').write(r'''
 
1043
      #ifndef _TESTB_H_
 
1044
      #define _TESTB_H_
 
1045
 
 
1046
      class TestB {
 
1047
        public:
 
1048
          TestB();
 
1049
      };
 
1050
 
 
1051
      #endif
 
1052
    ''')
 
1053
    open('testa.cpp', 'w').write(r'''
 
1054
      #include <stdio.h>
 
1055
      #include <testa.h>
 
1056
 
 
1057
      TestA::TestA() {
 
1058
        printf("TestA\n");
 
1059
      }
 
1060
    ''')
 
1061
    open('testb.cpp', 'w').write(r'''
 
1062
      #include <stdio.h>
 
1063
      #include <testb.h>
 
1064
      #include <testa.h>
 
1065
      /*
 
1066
      */
 
1067
      TestB::TestB() {
 
1068
        printf("TestB\n");
 
1069
        TestA* testa = new TestA();
 
1070
      }
 
1071
    ''')
 
1072
    open('main.cpp', 'w').write(r'''
 
1073
      #include <stdio.h>
 
1074
      #include <testa.h>
 
1075
      #include <testb.h>
 
1076
 
 
1077
      /*
 
1078
      */
 
1079
      int main(int argc, char** argv) {
 
1080
        printf("Main\n");
 
1081
        TestA* testa = new TestA();
 
1082
        TestB* testb = new TestB();
 
1083
      }
 
1084
    ''')
 
1085
 
 
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()
 
1089
 
 
1090
    Popen([PYTHON, EMCC, 'main.cpp', 'testa.cpp', 'testb.cpp', '-o', 'full.js', '-I.']).communicate()
 
1091
 
 
1092
    self.assertContained('TestA\nTestB\nTestA\n', run_js('main.js', engine=SPIDERMONKEY_ENGINE))
 
1093
 
 
1094
  def test_js_libraries(self):
 
1095
    open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
 
1096
      #include <stdio.h>
 
1097
      extern "C" {
 
1098
        extern void printey();
 
1099
        extern int calcey(int x, int y);
 
1100
      }
 
1101
      int main() {
 
1102
        printey();
 
1103
        printf("*%d*\\n", calcey(10, 22));
 
1104
        return 0;
 
1105
      }
 
1106
    ''')
 
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!');
 
1111
        }
 
1112
      });
 
1113
    ''')
 
1114
    open(os.path.join(self.get_dir(), 'mylib2.js'), 'w').write('''
 
1115
      mergeInto(LibraryManager.library, {
 
1116
        calcey: function(x, y) {
 
1117
          return x + y;
 
1118
        }
 
1119
      });
 
1120
    ''')
 
1121
 
 
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')))
 
1125
 
 
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
 
1129
    try:
 
1130
      os.makedirs(os.path.join(self.get_dir(), 'foo'));
 
1131
    except:
 
1132
      pass
 
1133
    try:
 
1134
      os.makedirs(os.path.join(self.get_dir(), 'bar'));
 
1135
    except:
 
1136
      pass
 
1137
    open(os.path.join(self.get_dir(), 'foo', 'main.cpp'), 'w').write('''
 
1138
      extern void printey();
 
1139
      int main() {
 
1140
        printey();
 
1141
        return 0;
 
1142
      }
 
1143
    ''')
 
1144
    open(os.path.join(self.get_dir(), 'bar', 'main.cpp'), 'w').write('''
 
1145
      #include<stdio.h>
 
1146
      void printey() { printf("hello there\\n"); }
 
1147
    ''')
 
1148
 
 
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')))
 
1151
 
 
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')))
 
1158
 
 
1159
  def test_main_a(self):
 
1160
    # if main() is in a .a, we need to pull in that .a
 
1161
 
 
1162
    main_name = os.path.join(self.get_dir(), 'main.c')
 
1163
    open(main_name, 'w').write(r'''
 
1164
      #include <stdio.h>
 
1165
      extern int f();
 
1166
      int main() {
 
1167
        printf("result: %d.\n", f());
 
1168
        return 0;
 
1169
      }
 
1170
    ''')
 
1171
 
 
1172
    other_name = os.path.join(self.get_dir(), 'other.c')
 
1173
    open(other_name, 'w').write(r'''
 
1174
      #include <stdio.h>
 
1175
      int f() { return 12346; }
 
1176
    ''')
 
1177
 
 
1178
    Popen([PYTHON, EMCC, main_name, '-c', '-o', main_name+'.bc']).communicate()
 
1179
    Popen([PYTHON, EMCC, other_name, '-c', '-o', other_name+'.bc']).communicate()
 
1180
 
 
1181
    Popen([PYTHON, EMAR, 'cr', main_name+'.a', main_name+'.bc']).communicate()
 
1182
 
 
1183
    Popen([PYTHON, EMCC, other_name+'.bc', main_name+'.a']).communicate()
 
1184
 
 
1185
    self.assertContained('result: 12346.', run_js(os.path.join(self.get_dir(), 'a.out.js')))
 
1186
 
 
1187
  def test_dup_o_in_a(self):
 
1188
    open('common.c', 'w').write(r'''
 
1189
      #include <stdio.h>
 
1190
      void a(void) {
 
1191
        printf("a\n");
 
1192
      }
 
1193
    ''')
 
1194
    Popen([PYTHON, EMCC, 'common.c', '-c', '-o', 'common.o']).communicate()
 
1195
    Popen([PYTHON, EMAR, 'rc', 'liba.a', 'common.o']).communicate()
 
1196
 
 
1197
    open('common.c', 'w').write(r'''
 
1198
      #include <stdio.h>
 
1199
      void b(void) {
 
1200
        printf("b\n");
 
1201
      }
 
1202
    ''')
 
1203
    Popen([PYTHON, EMCC, 'common.c', '-c', '-o', 'common.o']).communicate()
 
1204
    Popen([PYTHON, EMAR, 'rc', 'libb.a', 'common.o']).communicate()
 
1205
 
 
1206
    open('main.c', 'w').write(r'''
 
1207
      void a(void);
 
1208
      void b(void);
 
1209
      int main() {
 
1210
        a();
 
1211
        b();
 
1212
      }
 
1213
    ''')
 
1214
    Popen([PYTHON, EMCC, 'main.c', '-L.', '-la', '-lb']).communicate()
 
1215
 
 
1216
    self.assertContained('a\nb\n', run_js(os.path.join(self.get_dir(), 'a.out.js')))
 
1217
 
 
1218
  def test_export_in_a(self):
 
1219
    export_name = 'this_is_an_entry_point'
 
1220
 
 
1221
    open('export.c', 'w').write(r'''
 
1222
      #include <stdio.h>
 
1223
      void %s(void) {
 
1224
        printf("Hello, world!\n");
 
1225
      }
 
1226
    ''' % export_name)
 
1227
    Popen([PYTHON, EMCC, 'export.c', '-c', '-o', 'export.o']).communicate()
 
1228
    Popen([PYTHON, EMAR, 'rc', 'libexport.a', 'export.o']).communicate()
 
1229
 
 
1230
    open('main.c', 'w').write(r'''
 
1231
      int main() {
 
1232
        return 0;
 
1233
      }
 
1234
    ''')
 
1235
 
 
1236
    definition = 'function _%s(' % export_name
 
1237
 
 
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())
 
1241
 
 
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())
 
1246
 
 
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())
 
1250
 
 
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'''
 
1254
      #include <stdio.h>
 
1255
      int main() {
 
1256
        FILE *f = fopen("somefile.txt", "r");
 
1257
        char buf[100];
 
1258
        fread(buf, 1, 20, f);
 
1259
        buf[20] = 0;
 
1260
        fclose(f);
 
1261
        printf("|%s|\n", buf);
 
1262
        return 0;
 
1263
      }
 
1264
    ''')
 
1265
 
 
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')))
 
1268
 
 
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')))
 
1272
 
 
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'))
 
1278
 
 
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'''
 
1283
      #include <stdio.h>
 
1284
      #include <string.h>
 
1285
      void print_file(const char *name) {
 
1286
        FILE *f = fopen(name, "r");
 
1287
        char buf[100];
 
1288
        memset(buf, 0, 100);
 
1289
        fread(buf, 1, 20, f);
 
1290
        buf[20] = 0;
 
1291
        fclose(f);
 
1292
        printf("|%s|\n", buf);
 
1293
      }
 
1294
      int main() {
 
1295
        print_file("tst/aa.txt");
 
1296
        print_file("tst/test1/aa.txt");
 
1297
        print_file("tst/test2/aa.txt");
 
1298
        return 0;
 
1299
      }
 
1300
    ''')
 
1301
 
 
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')))
 
1304
 
 
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
 
1308
 
 
1309
    open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r'''
 
1310
      #include <stdio.h>
 
1311
      extern void printey();
 
1312
      extern void printother();
 
1313
      int main() {
 
1314
        printf("*");
 
1315
        printey();
 
1316
        printf("\n");
 
1317
        printother();
 
1318
        printf("\n");
 
1319
        printf("*");
 
1320
        return 0;
 
1321
      }
 
1322
    ''')
 
1323
 
 
1324
    try:
 
1325
      os.makedirs(os.path.join(self.get_dir(), 'libdir'));
 
1326
    except:
 
1327
      pass
 
1328
 
 
1329
    open(os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), 'w').write('''
 
1330
      #include <stdio.h>
 
1331
      void printey() {
 
1332
        printf("hello from lib");
 
1333
      }
 
1334
    ''')
 
1335
 
 
1336
    open(os.path.join(self.get_dir(), 'libdir', 'libother.cpp'), 'w').write('''
 
1337
      #include <stdio.h>
 
1338
      extern void printey();
 
1339
      void printother() {
 
1340
        printf("|");
 
1341
        printey();
 
1342
        printf("|");
 
1343
      }
 
1344
    ''')
 
1345
 
 
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']
 
1348
 
 
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()
 
1355
 
 
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()
 
1358
 
 
1359
    self.assertContained('*hello from lib\n|hello from lib|\n*', run_js(os.path.join(self.get_dir(), 'a.out.js')))
 
1360
 
 
1361
  def test_js_link(self):
 
1362
    open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
 
1363
      #include <stdio.h>
 
1364
      int main() {
 
1365
        printf("hello from main\\n");
 
1366
        return 0;
 
1367
      }
 
1368
    ''')
 
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!';
 
1372
    ''')
 
1373
    open(os.path.join(self.get_dir(), 'after.js'), 'w').write('''
 
1374
      Module.print(MESSAGE);
 
1375
    ''')
 
1376
 
 
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')))
 
1379
 
 
1380
  def test_sdl_endianness(self):
 
1381
    open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r'''
 
1382
      #include <stdio.h>
 
1383
      #include <SDL/SDL.h>
 
1384
 
 
1385
      int main() {
 
1386
        printf("%d, %d, %d\n", SDL_BYTEORDER, SDL_LIL_ENDIAN, SDL_BIG_ENDIAN);
 
1387
        return 0;
 
1388
      }
 
1389
    ''')
 
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')))
 
1392
 
 
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'''
 
1396
      #include <stdio.h>
 
1397
 
 
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++) {
 
1402
          buf[i] = i*i+i/3;
 
1403
        }
 
1404
        for (int i = 1; i < num; i++) {
 
1405
          buf[i] += buf[i-1];
 
1406
        }
 
1407
        for (int i = 0; i < num; i++) {
 
1408
          buf2[i] = buf[i];
 
1409
        }
 
1410
        for (int i = 1; i < num; i++) {
 
1411
          buf2[i] += buf2[i-1];
 
1412
        }
 
1413
        for (int i = 0; i < num; i++) {
 
1414
          printf("%d:%d\n", i, buf2[i]);
 
1415
        }
 
1416
        return 0;
 
1417
      }
 
1418
    ''')
 
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
 
1422
1:1
 
1423
2:6
 
1424
3:21
 
1425
4:53
 
1426
5:111
 
1427
6:-49
 
1428
7:98
 
1429
8:55
 
1430
9:96
 
1431
10:-16
 
1432
''', output)
 
1433
    self.assertNotContained('warning: library.js memcpy should not be running, it is only for testing!', output)
 
1434
 
 
1435
  def test_warn_undefined(self):
 
1436
    open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r'''
 
1437
      #include <stdio.h>
 
1438
 
 
1439
      extern "C" {
 
1440
        void something();
 
1441
      }
 
1442
 
 
1443
      int main() {
 
1444
        something();
 
1445
        return 0;
 
1446
      }
 
1447
    ''')
 
1448
 
 
1449
    def clear(): try_delete('a.out.js')
 
1450
 
 
1451
    for args in [[], ['-O2']]:
 
1452
      clear()
 
1453
      print 'warn', args
 
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])
 
1456
 
 
1457
      clear()
 
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])
 
1460
 
 
1461
    for args in [[], ['-O2']]:
 
1462
      clear()
 
1463
      print 'error', args
 
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')
 
1467
 
 
1468
      clear()
 
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')
 
1472
 
 
1473
  def test_toobig(self):
 
1474
    open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r'''
 
1475
      #include <stdio.h>
 
1476
 
 
1477
      #define BYTES 100*1024*1024
 
1478
 
 
1479
      int main(int argc, char **argv) {
 
1480
        if (argc == 100) {
 
1481
          static char buf[BYTES];
 
1482
          static char buf2[BYTES];
 
1483
          for (int i = 0; i < BYTES; i++) {
 
1484
            buf[i] = i*i;
 
1485
            buf2[i] = i/3;
 
1486
          }
 
1487
          for (int i = 0; i < BYTES; i++) {
 
1488
            buf[i] = buf2[i/2];
 
1489
            buf2[i] = buf[i/3];
 
1490
          }
 
1491
          printf("%d\n", buf[10] + buf2[20]);
 
1492
        }
 
1493
        return 0;
 
1494
      }
 
1495
    ''')
 
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
 
1499
 
 
1500
  def test_prepost(self):
 
1501
    open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
 
1502
      #include <stdio.h>
 
1503
      int main() {
 
1504
        printf("hello from main\\n");
 
1505
        return 0;
 
1506
      }
 
1507
    ''')
 
1508
    open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
 
1509
      var Module = {
 
1510
        preRun: function() { Module.print('pre-run') },
 
1511
        postRun: function() { Module.print('post-run') }
 
1512
      };
 
1513
    ''')
 
1514
 
 
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')))
 
1517
 
 
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')))
 
1522
 
 
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()
 
1528
      if run_dep:
 
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'
 
1533
 
 
1534
      if no_initial_run:
 
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'
 
1540
 
 
1541
    # Use postInit
 
1542
    open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
 
1543
      var Module = {
 
1544
        preRun: function() { Module.print('pre-run') },
 
1545
        postRun: function() { Module.print('post-run') },
 
1546
        preInit: function() { Module.print('pre-init') }
 
1547
      };
 
1548
    ''')
 
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')))
 
1551
 
 
1552
  def test_prepost2(self):
 
1553
    open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
 
1554
      #include <stdio.h>
 
1555
      int main() {
 
1556
        printf("hello from main\\n");
 
1557
        return 0;
 
1558
      }
 
1559
    ''')
 
1560
    open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
 
1561
      var Module = {
 
1562
        preRun: function() { Module.print('pre-run') },
 
1563
      };
 
1564
    ''')
 
1565
    open(os.path.join(self.get_dir(), 'pre2.js'), 'w').write('''
 
1566
      Module.postRun = function() { Module.print('post-run') };
 
1567
    ''')
 
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')))
 
1570
 
 
1571
  def test_prepre(self):
 
1572
    open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
 
1573
      #include <stdio.h>
 
1574
      int main() {
 
1575
        printf("hello from main\\n");
 
1576
        return 0;
 
1577
      }
 
1578
    ''')
 
1579
    open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
 
1580
      var Module = {
 
1581
        preRun: [function() { Module.print('pre-run') }],
 
1582
      };
 
1583
    ''')
 
1584
    open(os.path.join(self.get_dir(), 'pre2.js'), 'w').write('''
 
1585
      Module.preRun.push(function() { Module.print('prepre') });
 
1586
    ''')
 
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')))
 
1589
 
 
1590
  def test_save_bc(self):
 
1591
    for save in [0, 1]:
 
1592
      self.clear()
 
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
 
1596
      if save:
 
1597
        try_delete('a.out.js')
 
1598
        Building.llvm_dis(self.in_dir('my_bitcode.bc'), self.in_dir('my_ll.ll'))
 
1599
        try:
 
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'))
 
1603
        finally:
 
1604
          del os.environ['EMCC_LEAVE_INPUTS_RAW']
 
1605
 
 
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')
 
1614
 
 
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(),
 
1627
       ['registerize']),
 
1628
      (path_from_root('tools', 'eliminator', 'eliminator-test.js'), open(path_from_root('tools', 'eliminator', 'eliminator-test-output.js')).read(),
 
1629
       ['eliminate']),
 
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(),
 
1641
       ['asm', 'last']),
 
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']),
 
1650
    ]:
 
1651
      print input
 
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'))
 
1654
 
 
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
 
1661
 
 
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')
 
1666
    try:
 
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)
 
1672
        ]:
 
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()
 
1675
        ok = False
 
1676
        for c in range(chunks, chunks+2):
 
1677
          ok = ok or ('phase 2 working on %d chunks' % c in err)
 
1678
        assert ok, err
 
1679
        ok = False
 
1680
        for c in range(js_chunks, js_chunks+2):
 
1681
          ok = ok or ('splitting up js optimization into %d chunks' % c in err)
 
1682
        assert ok, err
 
1683
    finally:
 
1684
      del os.environ['EMCC_DEBUG']
 
1685
      del os.environ['EMCC_CORES']
 
1686
 
 
1687
  def test_debuginfo(self):
 
1688
    if os.environ.get('EMCC_DEBUG'): return self.skip('cannot run in debug mode')
 
1689
    try:
 
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),
 
1699
        ]:
 
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)
 
1704
    finally:
 
1705
      del os.environ['EMCC_DEBUG']
 
1706
 
 
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
 
1715
 
 
1716
  def test_embind(self):
 
1717
    for args, fail in [
 
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)
 
1724
    ]:
 
1725
      print args, fail
 
1726
      self.clear()
 
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)
 
1730
      if 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
 
1733
 
 
1734
  def test_llvm_nativizer(self):
 
1735
    try:
 
1736
      Popen(['as', '--version'], stdout=PIPE, stderr=PIPE).communicate()
 
1737
    except:
 
1738
      return self.skip('no gnu as, cannot run nativizer')
 
1739
 
 
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 
 
1751
input:inter-active
 
1752
texto
 
1753
$
 
1754
5 : 10,30,20,11,88
 
1755
other=ay file...
 
1756
seeked= file.
 
1757
''', output[0])
 
1758
    self.assertIdentical('texte\n', output[1])
 
1759
 
 
1760
  def test_emconfig(self):
 
1761
    output = Popen([PYTHON, EMCONFIG, 'LLVM_ROOT'], stdout=PIPE, stderr=PIPE).communicate()[0].strip()
 
1762
    try:
 
1763
      assert output == LLVM_ROOT
 
1764
    except:
 
1765
      print >> sys.stderr, 'Assertion failed: python %s LLVM_ROOT returned "%s" instead of expected "%s"!' % (EMCONFIG, output, LLVM_ROOT)
 
1766
      raise
 
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
 
1780
 
 
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'''
 
1784
      extern "C" {
 
1785
        void something();
 
1786
      }
 
1787
 
 
1788
      int main() {
 
1789
        something();
 
1790
        return 0;
 
1791
      }
 
1792
    ''')
 
1793
    open(os.path.join(self.get_dir(), 'supp.cpp'), 'w').write(r'''
 
1794
      #include <stdio.h>
 
1795
 
 
1796
      extern "C" {
 
1797
        void something() {
 
1798
          printf("yello\n");
 
1799
        }
 
1800
      }
 
1801
    ''')
 
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()
 
1804
 
 
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'
 
1811
 
 
1812
  def test_jcache_printf(self):
 
1813
    open(self.in_dir('src.cpp'), 'w').write(r'''
 
1814
      #include <stdio.h>
 
1815
      #include <stdint.h>
 
1816
      #include <emscripten.h>
 
1817
      int main() {
 
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);
 
1822
        return 0;
 
1823
      }
 
1824
    ''')
 
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)
 
1828
 
 
1829
  def test_conftest_s_flag_passing(self):
 
1830
    open(os.path.join(self.get_dir(), 'conftest.c'), 'w').write(r'''
 
1831
      int main() {
 
1832
        return 0;
 
1833
      }
 
1834
    ''')
 
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')
 
1841
 
 
1842
  def test_file_packager(self):
 
1843
    try:
 
1844
      os.mkdir('subdir')
 
1845
    except:
 
1846
      pass
 
1847
    open('data1.txt', 'w').write('data1')
 
1848
    os.chdir('subdir')
 
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()
 
1856
    assert len(out) > 0
 
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
 
1862
    def clean(txt):
 
1863
      return filter(lambda line: 'PACKAGE_UUID' not in line, txt.split('\n'))
 
1864
    out = clean(out)
 
1865
    out2 = clean(out2)
 
1866
    assert out == out2
 
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()
 
1869
    out3 = clean(out3)
 
1870
    assert out != out3
 
1871
 
 
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')
 
1875
    time.sleep(0.1)
 
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!
 
1882
    time.sleep(0.1)
 
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
 
1886
    time.sleep(0.1)
 
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'
 
1890
 
 
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.)')
 
1893
 
 
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)
 
1897
    assert '''Init: 0
 
1898
Font: 0x1
 
1899
Sum: 0
 
1900
you should see two lines of text in different colors and a blue rectangle
 
1901
SDL_Quit called (and ignored)
 
1902
done.
 
1903
''' in output, output
 
1904
 
 
1905
  def test_preprocess(self):
 
1906
    self.clear()
 
1907
 
 
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
 
1912