comparison third_party/emsdk/test/test.py @ 186:8cf4ec5e2191 hg-web

Fixed merge conflict.
author MrJuneJune <me@mrjunejune.com>
date Fri, 23 Jan 2026 22:38:59 -0800
parents 8d17f6e6e290
children
comparison
equal deleted inserted replaced
176:fed99fc04e12 186:8cf4ec5e2191
1 #!/usr/bin/env python3
2 import json
3 import os
4 import platform
5 import shutil
6 import subprocess
7 import sys
8 import tempfile
9 import unittest
10
11 WINDOWS = sys.platform.startswith('win')
12 MACOS = sys.platform == 'darwin'
13 MACOS_ARM64 = MACOS and platform.machine() == 'arm64'
14
15 emconfig = os.path.abspath('.emscripten')
16 assert os.path.exists(emconfig)
17
18 upstream_emcc = os.path.join('upstream', 'emscripten', 'emcc')
19 emsdk = './emsdk'
20 if WINDOWS:
21 upstream_emcc += '.bat'
22 emsdk = 'emsdk.bat'
23 else:
24 emsdk = './emsdk'
25
26 # Utilities
27
28
29 def listify(x):
30 if type(x) in {list, tuple}:
31 return x
32 return [x]
33
34
35 def check_call(cmd, **kwargs):
36 if type(cmd) is not list:
37 cmd = cmd.split()
38 print('running: %s' % cmd)
39 subprocess.run(cmd, check=True, text=True, **kwargs)
40
41
42 def checked_call_with_output(cmd, expected=None, unexpected=None, stderr=None, env=None):
43 cmd = cmd.split(' ')
44 print('running: %s' % cmd)
45 try:
46 stdout = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=stderr, check=True, text=True, env=env).stdout
47 except subprocess.CalledProcessError as e:
48 print(e.stderr)
49 print(e.stdout)
50 raise e
51
52 if expected:
53 for x in listify(expected):
54 assert x in stdout, 'expected output missing: ' + stdout + '\n[[[' + x + ']]]'
55 if unexpected:
56 for x in listify(unexpected):
57 assert x not in stdout, 'unexpected output present: ' + stdout + '\n[[[' + x + ']]]'
58
59
60 def failing_call_with_output(cmd, expected, env=None):
61 proc = subprocess.run(cmd.split(' '), capture_output=True, text=True, env=env)
62 stdout = proc.stdout
63 stderr = proc.stderr
64 if WINDOWS:
65 print('warning: skipping part of failing_call_with_output() due to error codes not being propagated (see #592)')
66 else:
67 assert proc.returncode, 'call must have failed: ' + str([stdout, '\n========\n', stderr])
68 assert expected in stdout or expected in stderr, 'call did not have the expected output: %s: %s' % (expected, str([stdout, '\n========\n', stderr]))
69
70
71 def hack_emsdk(marker, replacement):
72 with open('emsdk.py') as f:
73 src = f.read()
74 assert marker in src
75 src = src.replace(marker, replacement)
76 name = '__test_emsdk'
77 with open(name, 'w') as f:
78 f.write(src)
79 return name
80
81
82 # Set up
83
84 TAGS = json.loads(open('emscripten-releases-tags.json').read())
85
86 # Tests
87
88
89 def do_lib_building(emcc):
90 cache_building_messages = ['generating system library: ']
91
92 def do_build(args, is_expected=None):
93 unexpected = None
94 expected = None
95 if is_expected is True:
96 expected = cache_building_messages
97 elif is_expected is False:
98 unexpected = cache_building_messages
99 checked_call_with_output(emcc + ' hello_world.c' + args,
100 expected=expected,
101 unexpected=unexpected,
102 stderr=subprocess.STDOUT)
103
104 # The emsdk ships all system libraries so we don't expect to see any
105 # cache population unless we explicly --clear-cache.
106 do_build('', is_expected=False)
107 check_call(emcc + ' --clear-cache')
108 do_build(' -O2', is_expected=True)
109 # Do another build at -O0. In nwers SDK versions this generates
110 # different libs, but not in older ones so don't assert here.
111 do_build('')
112 # Now verify that libs are *not* build
113 do_build(' -s WASM=0', is_expected=False)
114 do_build(' -O2 -s WASM=0', is_expected=False)
115
116
117 def run_emsdk(cmd):
118 if type(cmd) is not list:
119 cmd = cmd.split()
120 check_call([emsdk] + cmd)
121
122
123 class Emsdk(unittest.TestCase):
124 @classmethod
125 def setUpClass(cls):
126 with open('hello_world.c', 'w') as f:
127 f.write('''\
128 #include <stdio.h>
129
130 int main() {
131 printf("Hello, world!\\n");
132 return 0;
133 }
134 ''')
135
136 def setUp(self):
137 run_emsdk('install latest')
138 run_emsdk('activate latest')
139
140 def test_unknown_arch(self):
141 env = os.environ.copy()
142 env['EMSDK_ARCH'] = 'mips'
143 failing_call_with_output(emsdk + ' install latest',
144 expected='unknown machine architecture: mips',
145 env=env)
146
147 def test_wrong_bitness(self):
148 env = os.environ.copy()
149 env['EMSDK_ARCH'] = 'x86'
150 failing_call_with_output(emsdk + ' install sdk-latest-64bit',
151 expected='is only provided for 64-bit OSe',
152 env=env)
153
154 def test_already_installed(self):
155 # Test we don't re-download unnecessarily
156 checked_call_with_output(emsdk + ' install latest', expected='already installed', unexpected='Downloading:')
157
158 def test_list(self):
159 # Test we report installed tools properly. The latest version should be
160 # installed, but not some random old one.
161 checked_call_with_output(emsdk + ' list', expected=TAGS['aliases']['latest'] + ' INSTALLED', unexpected='1.39.15 INSTALLED:')
162
163 def test_config_contents(self):
164 print('test .emscripten contents')
165 with open(emconfig) as f:
166 config = f.read()
167 assert 'upstream' in config
168
169 def test_lib_building(self):
170 print('building proper system libraries')
171 do_lib_building(upstream_emcc)
172
173 def test_redownload(self):
174 print('go back to using upstream')
175 run_emsdk('activate latest')
176
177 # Test the normal tools like node don't re-download on re-install
178 print('another install must re-download')
179 checked_call_with_output(emsdk + ' uninstall node-22.16.0-64bit')
180 checked_call_with_output(emsdk + ' install node-22.16.0-64bit', expected='Downloading:', unexpected='already installed')
181 checked_call_with_output(emsdk + ' install node-22.16.0-64bit', unexpected='Downloading:', expected='already installed')
182
183 def test_tot_upstream(self):
184 print('test update-tags')
185 run_emsdk('update-tags')
186 print('test tot-upstream')
187 run_emsdk('install tot-upstream')
188 with open(emconfig) as f:
189 config = f.read()
190 run_emsdk('activate tot-upstream')
191 with open(emconfig + '.old') as f:
192 old_config = f.read()
193 self.assertEqual(config, old_config)
194 # TODO; test on latest as well
195 check_call(upstream_emcc + ' hello_world.c')
196
197 def test_closure(self):
198 # Specifically test with `--closure` so we know that node_modules is working
199 check_call(upstream_emcc + ' hello_world.c --closure=1')
200
201 def test_specific_version(self):
202 if MACOS_ARM64:
203 self.skipTest('Old sdk versions do not have ARM64 binaries')
204 print('test specific release (new, short name)')
205 run_emsdk('install 1.38.33')
206 print('another install, but no need for re-download')
207 checked_call_with_output(emsdk + ' install 1.38.33', expected='Skipped', unexpected='Downloading:')
208 run_emsdk('activate 1.38.33')
209
210 def test_specific_version_full(self):
211 if MACOS_ARM64:
212 self.skipTest('Old sdk versions do not have ARM64 binaries')
213 print('test specific release (new, full name)')
214 run_emsdk('install sdk-1.38.33-64bit')
215 run_emsdk('activate sdk-1.38.33-64bit')
216 print('test specific release (new, tag name)')
217 run_emsdk('install sdk-tag-1.38.33-64bit')
218 run_emsdk('activate sdk-tag-1.38.33-64bit')
219
220 def test_binaryen_from_source(self):
221 if MACOS:
222 self.skipTest("https://github.com/WebAssembly/binaryen/issues/4299")
223 if WINDOWS:
224 self.skipTest("https://github.com/emscripten-core/emsdk/issues/1624")
225 print('test binaryen source build')
226 run_emsdk(['install', '--build=Release', '--generator=Unix Makefiles', 'binaryen-main-64bit'])
227
228 def test_no_32bit(self):
229 print('test 32-bit error')
230 emsdk_hacked = hack_emsdk('not is_os_64bit()', 'True')
231 failing_call_with_output('%s %s install latest' % (sys.executable, emsdk_hacked),
232 'this tool is only provided for 64-bit OSes')
233 os.remove(emsdk_hacked)
234
235 def test_update_no_git(self):
236 print('test non-git update')
237
238 temp_dir = tempfile.mkdtemp()
239 for filename in os.listdir('.'):
240 if not filename.startswith('.') and not os.path.isdir(filename):
241 shutil.copy2(filename, os.path.join(temp_dir, filename))
242
243 olddir = os.getcwd()
244 try:
245 os.chdir(temp_dir)
246 run_emsdk('update')
247
248 print('second time')
249 run_emsdk('update')
250 finally:
251 os.chdir(olddir)
252
253 def test_install_arbitrary(self):
254 # Test that its possible to install arbrary emscripten-releases SDKs
255 run_emsdk('install 1b7f7bc6002a3ca73647f41fc10e1fac7f06f804')
256
257 # Check that its not re-downloaded
258 checked_call_with_output(emsdk + ' install 1b7f7bc6002a3ca73647f41fc10e1fac7f06f804', expected='Skipped', unexpected='Downloading:')
259
260 def test_install_tool(self):
261 # Test that its possible to install emscripten as tool instead of SDK
262 checked_call_with_output(emsdk + ' install releases-77b065ace39e6ab21446e13f92897f956c80476a', unexpected='Installing SDK')
263
264 def test_activate_missing(self):
265 run_emsdk('install latest')
266 failing_call_with_output(emsdk + ' activate 2.0.1', expected="error: tool is not installed and therefore cannot be activated: 'releases-13e29bd55185e3c12802bc090b4507901856b2ba-64bit'")
267
268 def test_keep_downloads(self):
269 env = os.environ.copy()
270 env['EMSDK_KEEP_DOWNLOADS'] = '1'
271 # With EMSDK_KEEP_DOWNLOADS the downloading should happen on the first
272 # install of 2.0.28, and again when we install 2.0.29, but not on the
273 # second install of 2.0.28 because the zip should already be local.
274 shutil.rmtree('downloads')
275 checked_call_with_output(emsdk + ' install 3.1.54', expected='Downloading:', env=env)
276 checked_call_with_output(emsdk + ' install 3.1.55', expected='Downloading:', env=env)
277 checked_call_with_output(emsdk + ' install 3.1.54', expected='already downloaded, skipping', unexpected='Downloading:', env=env)
278
279
280 if __name__ == '__main__':
281 unittest.main(verbosity=2)