Mercurial
comparison third_party/emsdk/test/test.py @ 179:8d17f6e6e290
[ThirdParty] Added emsdk bazel rules that can be supported by bazel 9.0.0
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Thu, 22 Jan 2026 21:23:17 -0800 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 178:94705b5986b3 | 179:8d17f6e6e290 |
|---|---|
| 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) |