Avoid writing even sanity.txt if FROZEN_CACHE is enabled (#13316)
These improvements to the `FROZE_CACHE` setting allow for a
pre-populated, read-only cache. In this scenario the cache
is cannot ever be locked, and that we don't even write
the sanity file.
Users can still force a sanity check with `emcc --check`
but no sanity file will ever be written.
Fixes: #13369
Sam Clegg authored 3 years ago
GitHub committed 3 years ago
389 | 389 | |
390 | 390 | self.assertContained('hello from emcc with no config file', self.run_js('a.out.js')) |
391 | 391 | |
392 | def erase_cache(self): | |
393 | Cache.erase() | |
392 | def clear_cache(self): | |
393 | self.run_process([EMCC, '--clear-cache']) | |
394 | 394 | self.assertCacheEmpty() |
395 | 395 | |
396 | 396 | def assertCacheEmpty(self): |
405 | 405 | BUILDING_MESSAGE = 'generating system library: %s' |
406 | 406 | |
407 | 407 | restore_and_set_up() |
408 | self.erase_cache() | |
408 | self.clear_cache() | |
409 | 409 | |
410 | 410 | # Building a file that *does* need something *should* trigger cache |
411 | 411 | # generation, but only the first time |
424 | 424 | # Manual cache clearing |
425 | 425 | restore_and_set_up() |
426 | 426 | self.ensure_cache() |
427 | self.assertTrue(os.path.exists(Cache.dirname)) | |
427 | self.assertExists(Cache.dirname) | |
428 | 428 | output = self.do([EMCC, '--clear-cache']) |
429 | 429 | self.assertIn('clearing cache', output) |
430 | 430 | self.assertIn(SANITY_MESSAGE, output) |
437 | 437 | make_fake_clang(self.in_dir('fake', 'bin', 'clang'), EXPECTED_LLVM_VERSION) |
438 | 438 | make_fake_llc(self.in_dir('fake', 'bin', 'llc'), 'got wasm32 backend! WebAssembly 32-bit') |
439 | 439 | with env_modify({'EM_LLVM_ROOT': self.in_dir('fake', 'bin')}): |
440 | self.assertTrue(os.path.exists(Cache.dirname)) | |
440 | self.assertExists(Cache.dirname) | |
441 | 441 | output = self.do([EMCC]) |
442 | 442 | self.assertIn('clearing cache', output) |
443 | 443 | self.assertCacheEmpty() |
445 | 445 | # FROZEN_CACHE prevents cache clears, and prevents building |
446 | 446 | def test_FROZEN_CACHE(self): |
447 | 447 | restore_and_set_up() |
448 | self.erase_cache() | |
448 | self.clear_cache() | |
449 | 449 | self.ensure_cache() |
450 | self.assertTrue(os.path.exists(Cache.dirname)) | |
450 | self.assertExists(Cache.dirname) | |
451 | 451 | # changing config file should not clear cache |
452 | 452 | add_to_config('FROZEN_CACHE = True') |
453 | 453 | self.do([EMCC]) |
454 | self.assertTrue(os.path.exists(Cache.dirname)) | |
454 | self.assertExists(Cache.dirname) | |
455 | 455 | # building libraries is disallowed |
456 | 456 | output = self.do([EMBUILDER, 'build', 'libemmalloc']) |
457 | self.assertIn('FROZEN_CACHE disallows building system libs', output) | |
457 | self.assertContained('FROZEN_CACHE is set, but cache file is missing', output) | |
458 | 458 | |
459 | 459 | # Test that if multiple processes attempt to access or build stuff to the |
460 | 460 | # cache on demand, that exactly one of the processes will, and the other |
483 | 483 | num_times_libc_was_built += 1 |
484 | 484 | |
485 | 485 | # The cache directory must exist after the build |
486 | self.assertTrue(os.path.exists(cache_dir_name)) | |
486 | self.assertExists(cache_dir_name) | |
487 | 487 | # The cache directory must contain a built libc |
488 | self.assertTrue(os.path.exists(os.path.join(cache_dir_name, libname))) | |
488 | self.assertExists(os.path.join(cache_dir_name, libname)) | |
489 | 489 | # Exactly one child process should have triggered libc build! |
490 | 490 | self.assertEqual(num_times_libc_was_built, 1) |
491 | 491 | |
624 | 624 | |
625 | 625 | print('normal build') |
626 | 626 | with env_modify({'EMCC_FORCE_STDLIBS': None}): |
627 | Cache.erase() | |
627 | self.clear_cache() | |
628 | 628 | build() |
629 | 629 | test() |
630 | 630 | |
631 | 631 | print('wacky env vars, these should not mess our bootstrapping') |
632 | 632 | with env_modify({'EMCC_FORCE_STDLIBS': '1'}): |
633 | Cache.erase() | |
633 | self.clear_cache() | |
634 | 634 | build() |
635 | 635 | test() |
636 | 636 | |
637 | 637 | def test_vanilla(self): |
638 | 638 | restore_and_set_up() |
639 | Cache.erase() | |
639 | self.clear_cache() | |
640 | 640 | |
641 | 641 | def make_fake(report): |
642 | 642 | with open(config_file, 'a') as f: |
688 | 688 | def test_embuilder_wasm_backend(self): |
689 | 689 | restore_and_set_up() |
690 | 690 | # the --lto flag makes us build wasm-bc |
691 | self.do([EMCC, '--clear-cache']) | |
691 | self.clear_cache() | |
692 | 692 | self.run_process([EMBUILDER, 'build', 'libemmalloc']) |
693 | 693 | self.assertExists(os.path.join(config.CACHE, 'sysroot', 'lib', 'wasm32-emscripten')) |
694 | self.do([EMCC, '--clear-cache']) | |
694 | self.clear_cache() | |
695 | 695 | self.run_process([EMBUILDER, 'build', 'libemmalloc', '--lto']) |
696 | 696 | self.assertExists(os.path.join(config.CACHE, 'sysroot', 'lib', 'wasm32-emscripten', 'lto')) |
697 | 697 |
35 | 35 | self.filelock = filelock.FileLock(self.filelock_name) |
36 | 36 | |
37 | 37 | def acquire_cache_lock(self): |
38 | if config.FROZEN_CACHE: | |
39 | # Raise an exception here rather than exit_with_error since in practice this | |
40 | # should never happen | |
41 | raise Exception('Attempt to lock the cache but FROZEN_CACHE is set') | |
42 | ||
38 | 43 | if not self.EM_EXCLUSIVE_CACHE_ACCESS and self.acquired_count == 0: |
39 | 44 | logger.debug('PID %s acquiring multiprocess file lock to Emscripten cache at %s' % (str(os.getpid()), self.dirname)) |
40 | 45 | try: |
113 | 118 | self.erase_file(self.get_lib_name(name)) |
114 | 119 | |
115 | 120 | def erase_file(self, shortname): |
116 | name = os.path.join(self.dirname, shortname) | |
117 | if os.path.exists(name): | |
118 | logger.info('deleting cached file: %s', name) | |
119 | tempfiles.try_delete(name) | |
121 | with self.lock(): | |
122 | name = os.path.join(self.dirname, shortname) | |
123 | if os.path.exists(name): | |
124 | logger.info('deleting cached file: %s', name) | |
125 | tempfiles.try_delete(name) | |
120 | 126 | |
121 | 127 | def get_lib(self, libname, *args, **kwargs): |
122 | 128 | name = self.get_lib_name(libname) |
132 | 138 | if os.path.exists(cachename) and not force: |
133 | 139 | return cachename |
134 | 140 | |
141 | if config.FROZEN_CACHE: | |
142 | # Raise an exception here rather than exit_with_error since in practice this | |
143 | # should never happen | |
144 | raise Exception('FROZEN_CACHE is set, but cache file is missing: %s' % shortname) | |
145 | ||
135 | 146 | with self.lock(): |
136 | 147 | if os.path.exists(cachename) and not force: |
137 | 148 | return cachename |
138 | # it doesn't exist yet, create it | |
139 | if config.FROZEN_CACHE: | |
140 | # it's ok to build small .txt marker files like "vanilla" | |
141 | if not shortname.endswith('.txt'): | |
142 | raise Exception('FROZEN_CACHE disallows building system libs: %s' % shortname) | |
143 | 149 | if what is None: |
144 | 150 | if shortname.endswith(('.bc', '.so', '.a')): |
145 | 151 | what = 'system library' |
74 | 74 | JS_ENGINES = [listify(engine) for engine in JS_ENGINES] |
75 | 75 | WASM_ENGINES = [listify(engine) for engine in WASM_ENGINES] |
76 | 76 | if not CACHE: |
77 | if root_is_writable(): | |
77 | if FROZEN_CACHE or root_is_writable(): | |
78 | 78 | CACHE = path_from_root('cache') |
79 | 79 | else: |
80 | 80 | # Use the legacy method of putting the cache in the user's home directory |
241 | 241 | |
242 | 242 | |
243 | 243 | def perform_sanity_checks(): |
244 | # some warning, mostly not fatal checks - do them even if EM_IGNORE_SANITY is on | |
245 | check_node_version() | |
246 | check_llvm_version() | |
247 | ||
248 | llvm_ok = check_llvm() | |
249 | ||
250 | if os.environ.get('EM_IGNORE_SANITY'): | |
251 | logger.info('EM_IGNORE_SANITY set, ignoring sanity checks') | |
252 | return | |
253 | ||
244 | 254 | logger.info('(Emscripten: Running sanity checks)') |
255 | ||
256 | if not llvm_ok: | |
257 | exit_with_error('failing sanity checks due to previous llvm failure') | |
245 | 258 | |
246 | 259 | with ToolchainProfiler.profile_block('sanity compiler_engine'): |
247 | 260 | try: |
266 | 279 | """ |
267 | 280 | if not force and os.environ.get('EMCC_SKIP_SANITY_CHECK') == '1': |
268 | 281 | return |
282 | ||
269 | 283 | # We set EMCC_SKIP_SANITY_CHECK so that any subprocesses that we launch will |
270 | 284 | # not re-run the tests. |
271 | 285 | os.environ['EMCC_SKIP_SANITY_CHECK'] = '1' |
286 | ||
287 | if config.FROZEN_CACHE: | |
288 | if force: | |
289 | perform_sanity_checks() | |
290 | return | |
291 | ||
292 | if os.environ.get('EM_IGNORE_SANITY'): | |
293 | perform_sanity_checks() | |
294 | return | |
295 | ||
272 | 296 | with ToolchainProfiler.profile_block('sanity'): |
273 | check_llvm_version() | |
274 | 297 | if not config.config_file: |
275 | 298 | return # config stored directly in EM_CONFIG => skip sanity checks |
276 | 299 | expected = generate_sanity() |
282 | 305 | if sanity_data != expected: |
283 | 306 | logger.debug('old sanity: %s' % sanity_data) |
284 | 307 | logger.debug('new sanity: %s' % expected) |
285 | if config.FROZEN_CACHE: | |
286 | logger.info('(Emscripten: config changed, cache may need to be cleared, but FROZEN_CACHE is set)') | |
287 | else: | |
288 | logger.info('(Emscripten: config changed, clearing cache)') | |
289 | Cache.erase() | |
290 | # the check actually failed, so definitely write out the sanity file, to | |
291 | # avoid others later seeing failures too | |
292 | force = False | |
308 | logger.info('(Emscripten: config changed, clearing cache)') | |
309 | Cache.erase() | |
310 | # the check actually failed, so definitely write out the sanity file, to | |
311 | # avoid others later seeing failures too | |
312 | force = False | |
293 | 313 | else: |
294 | 314 | if force: |
295 | 315 | logger.debug(f'sanity file up-to-date but check forced: {sanity_file}') |
298 | 318 | return # all is well |
299 | 319 | else: |
300 | 320 | logger.debug(f'sanity file not found: {sanity_file}') |
301 | ||
302 | # some warning, mostly not fatal checks - do them even if EM_IGNORE_SANITY is on | |
303 | check_node_version() | |
304 | ||
305 | llvm_ok = check_llvm() | |
306 | ||
307 | if os.environ.get('EM_IGNORE_SANITY'): | |
308 | logger.info('EM_IGNORE_SANITY set, ignoring sanity checks') | |
309 | return | |
310 | ||
311 | if not llvm_ok: | |
312 | exit_with_error('failing sanity checks due to previous llvm failure') | |
313 | 321 | |
314 | 322 | perform_sanity_checks() |
315 | 323 |