Skip to content

Commit e74e0af

Browse files
authored
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
1 parent a8d41c0 commit e74e0af

File tree

4 files changed

+61
-47
lines changed

4 files changed

+61
-47
lines changed

tests/test_sanity.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -390,8 +390,8 @@ def test_em_config_env_var(self):
390390

391391
self.assertContained('hello from emcc with no config file', self.run_js('a.out.js'))
392392

393-
def erase_cache(self):
394-
Cache.erase()
393+
def clear_cache(self):
394+
self.run_process([EMCC, '--clear-cache'])
395395
self.assertCacheEmpty()
396396

397397
def assertCacheEmpty(self):
@@ -406,7 +406,7 @@ def test_emcc_caching(self):
406406
BUILDING_MESSAGE = 'generating system library: %s'
407407

408408
restore_and_set_up()
409-
self.erase_cache()
409+
self.clear_cache()
410410

411411
# Building a file that *does* need something *should* trigger cache
412412
# generation, but only the first time
@@ -425,7 +425,7 @@ def test_cache_clearing_manual(self):
425425
# Manual cache clearing
426426
restore_and_set_up()
427427
self.ensure_cache()
428-
self.assertTrue(os.path.exists(Cache.dirname))
428+
self.assertExists(Cache.dirname)
429429
output = self.do([EMCC, '--clear-cache'])
430430
self.assertIn('clearing cache', output)
431431
self.assertIn(SANITY_MESSAGE, output)
@@ -438,24 +438,24 @@ def test_cache_clearing_auto(self):
438438
make_fake_clang(self.in_dir('fake', 'bin', 'clang'), EXPECTED_LLVM_VERSION)
439439
make_fake_llc(self.in_dir('fake', 'bin', 'llc'), 'got wasm32 backend! WebAssembly 32-bit')
440440
with env_modify({'EM_LLVM_ROOT': self.in_dir('fake', 'bin')}):
441-
self.assertTrue(os.path.exists(Cache.dirname))
441+
self.assertExists(Cache.dirname)
442442
output = self.do([EMCC])
443443
self.assertIn('clearing cache', output)
444444
self.assertCacheEmpty()
445445

446446
# FROZEN_CACHE prevents cache clears, and prevents building
447447
def test_FROZEN_CACHE(self):
448448
restore_and_set_up()
449-
self.erase_cache()
449+
self.clear_cache()
450450
self.ensure_cache()
451-
self.assertTrue(os.path.exists(Cache.dirname))
451+
self.assertExists(Cache.dirname)
452452
# changing config file should not clear cache
453453
add_to_config('FROZEN_CACHE = True')
454454
self.do([EMCC])
455-
self.assertTrue(os.path.exists(Cache.dirname))
455+
self.assertExists(Cache.dirname)
456456
# building libraries is disallowed
457457
output = self.do([EMBUILDER, 'build', 'libemmalloc'])
458-
self.assertIn('FROZEN_CACHE disallows building system libs', output)
458+
self.assertContained('FROZEN_CACHE is set, but cache file is missing', output)
459459

460460
# Test that if multiple processes attempt to access or build stuff to the
461461
# cache on demand, that exactly one of the processes will, and the other
@@ -484,9 +484,9 @@ def test_emcc_multiprocess_cache_access(self):
484484
num_times_libc_was_built += 1
485485

486486
# The cache directory must exist after the build
487-
self.assertTrue(os.path.exists(cache_dir_name))
487+
self.assertExists(cache_dir_name)
488488
# The cache directory must contain a built libc
489-
self.assertTrue(os.path.exists(os.path.join(cache_dir_name, libname)))
489+
self.assertExists(os.path.join(cache_dir_name, libname))
490490
# Exactly one child process should have triggered libc build!
491491
self.assertEqual(num_times_libc_was_built, 1)
492492

@@ -625,19 +625,19 @@ def test():
625625

626626
print('normal build')
627627
with env_modify({'EMCC_FORCE_STDLIBS': None}):
628-
Cache.erase()
628+
self.clear_cache()
629629
build()
630630
test()
631631

632632
print('wacky env vars, these should not mess our bootstrapping')
633633
with env_modify({'EMCC_FORCE_STDLIBS': '1'}):
634-
Cache.erase()
634+
self.clear_cache()
635635
build()
636636
test()
637637

638638
def test_vanilla(self):
639639
restore_and_set_up()
640-
Cache.erase()
640+
self.clear_cache()
641641

642642
def make_fake(report):
643643
with open(config_file, 'a') as f:
@@ -689,10 +689,10 @@ def test_embuilder_force_port(self):
689689
def test_embuilder_wasm_backend(self):
690690
restore_and_set_up()
691691
# the --lto flag makes us build wasm-bc
692-
self.do([EMCC, '--clear-cache'])
692+
self.clear_cache()
693693
self.run_process([EMBUILDER, 'build', 'libemmalloc'])
694694
self.assertExists(os.path.join(config.CACHE, 'sysroot', 'lib', 'wasm32-emscripten'))
695-
self.do([EMCC, '--clear-cache'])
695+
self.clear_cache()
696696
self.run_process([EMBUILDER, 'build', 'libemmalloc', '--lto'])
697697
self.assertExists(os.path.join(config.CACHE, 'sysroot', 'lib', 'wasm32-emscripten', 'lto'))
698698

tools/cache.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ def __init__(self, dirname):
3636
self.filelock = filelock.FileLock(self.filelock_name)
3737

3838
def acquire_cache_lock(self):
39+
if config.FROZEN_CACHE:
40+
# Raise an exception here rather than exit_with_error since in practice this
41+
# should never happen
42+
raise Exception('Attempt to lock the cache but FROZEN_CACHE is set')
43+
3944
if not self.EM_EXCLUSIVE_CACHE_ACCESS and self.acquired_count == 0:
4045
logger.debug('PID %s acquiring multiprocess file lock to Emscripten cache at %s' % (str(os.getpid()), self.dirname))
4146
try:
@@ -114,10 +119,11 @@ def erase_lib(self, name):
114119
self.erase_file(self.get_lib_name(name))
115120

116121
def erase_file(self, shortname):
117-
name = os.path.join(self.dirname, shortname)
118-
if os.path.exists(name):
119-
logger.info('deleting cached file: %s', name)
120-
tempfiles.try_delete(name)
122+
with self.lock():
123+
name = os.path.join(self.dirname, shortname)
124+
if os.path.exists(name):
125+
logger.info('deleting cached file: %s', name)
126+
tempfiles.try_delete(name)
121127

122128
def get_lib(self, libname, *args, **kwargs):
123129
name = self.get_lib_name(libname)
@@ -133,14 +139,14 @@ def get(self, shortname, creator, what=None, force=False):
133139
if os.path.exists(cachename) and not force:
134140
return cachename
135141

142+
if config.FROZEN_CACHE:
143+
# Raise an exception here rather than exit_with_error since in practice this
144+
# should never happen
145+
raise Exception('FROZEN_CACHE is set, but cache file is missing: %s' % shortname)
146+
136147
with self.lock():
137148
if os.path.exists(cachename) and not force:
138149
return cachename
139-
# it doesn't exist yet, create it
140-
if config.FROZEN_CACHE:
141-
# it's ok to build small .txt marker files like "vanilla"
142-
if not shortname.endswith('.txt'):
143-
raise Exception('FROZEN_CACHE disallows building system libs: %s' % shortname)
144150
if what is None:
145151
if shortname.endswith(('.bc', '.so', '.a')):
146152
what = 'system library'

tools/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def normalize_config_settings():
7575
JS_ENGINES = [listify(engine) for engine in JS_ENGINES]
7676
WASM_ENGINES = [listify(engine) for engine in WASM_ENGINES]
7777
if not CACHE:
78-
if root_is_writable():
78+
if FROZEN_CACHE or root_is_writable():
7979
CACHE = path_from_root('cache')
8080
else:
8181
# Use the legacy method of putting the cache in the user's home directory

tools/shared.py

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,21 @@ def generate_sanity():
242242

243243

244244
def perform_sanity_checks():
245+
# some warning, mostly not fatal checks - do them even if EM_IGNORE_SANITY is on
246+
check_node_version()
247+
check_llvm_version()
248+
249+
llvm_ok = check_llvm()
250+
251+
if os.environ.get('EM_IGNORE_SANITY'):
252+
logger.info('EM_IGNORE_SANITY set, ignoring sanity checks')
253+
return
254+
245255
logger.info('(Emscripten: Running sanity checks)')
246256

257+
if not llvm_ok:
258+
exit_with_error('failing sanity checks due to previous llvm failure')
259+
247260
with ToolchainProfiler.profile_block('sanity compiler_engine'):
248261
try:
249262
run_process(config.NODE_JS + ['-e', 'console.log("hello")'], stdout=PIPE)
@@ -267,11 +280,21 @@ def check_sanity(force=False):
267280
"""
268281
if not force and os.environ.get('EMCC_SKIP_SANITY_CHECK') == '1':
269282
return
283+
270284
# We set EMCC_SKIP_SANITY_CHECK so that any subprocesses that we launch will
271285
# not re-run the tests.
272286
os.environ['EMCC_SKIP_SANITY_CHECK'] = '1'
287+
288+
if config.FROZEN_CACHE:
289+
if force:
290+
perform_sanity_checks()
291+
return
292+
293+
if os.environ.get('EM_IGNORE_SANITY'):
294+
perform_sanity_checks()
295+
return
296+
273297
with ToolchainProfiler.profile_block('sanity'):
274-
check_llvm_version()
275298
if not config.config_file:
276299
return # config stored directly in EM_CONFIG => skip sanity checks
277300
expected = generate_sanity()
@@ -283,14 +306,11 @@ def check_sanity(force=False):
283306
if sanity_data != expected:
284307
logger.debug('old sanity: %s' % sanity_data)
285308
logger.debug('new sanity: %s' % expected)
286-
if config.FROZEN_CACHE:
287-
logger.info('(Emscripten: config changed, cache may need to be cleared, but FROZEN_CACHE is set)')
288-
else:
289-
logger.info('(Emscripten: config changed, clearing cache)')
290-
Cache.erase()
291-
# the check actually failed, so definitely write out the sanity file, to
292-
# avoid others later seeing failures too
293-
force = False
309+
logger.info('(Emscripten: config changed, clearing cache)')
310+
Cache.erase()
311+
# the check actually failed, so definitely write out the sanity file, to
312+
# avoid others later seeing failures too
313+
force = False
294314
else:
295315
if force:
296316
logger.debug(f'sanity file up-to-date but check forced: {sanity_file}')
@@ -300,18 +320,6 @@ def check_sanity(force=False):
300320
else:
301321
logger.debug(f'sanity file not found: {sanity_file}')
302322

303-
# some warning, mostly not fatal checks - do them even if EM_IGNORE_SANITY is on
304-
check_node_version()
305-
306-
llvm_ok = check_llvm()
307-
308-
if os.environ.get('EM_IGNORE_SANITY'):
309-
logger.info('EM_IGNORE_SANITY set, ignoring sanity checks')
310-
return
311-
312-
if not llvm_ok:
313-
exit_with_error('failing sanity checks due to previous llvm failure')
314-
315323
perform_sanity_checks()
316324

317325
if not force:

0 commit comments

Comments
 (0)