Skip to content

Commit b4ceebf

Browse files
committed
Build a complete sysroot in the cache directory
Rather than adding various include paths, copy any needed headers into the sysroot along with any libraries. This means that emscripten can work a lot more like the traditional cross compiler (e.g. clang -target=xxx --sysroot=yyy), and we can start to think of the emscripten driver as a seperate thing to the sysroot. Fixes: #9353
1 parent 7fafb14 commit b4ceebf

File tree

14 files changed

+152
-108
lines changed

14 files changed

+152
-108
lines changed

ChangeLog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ See docs/process.md for more on how version tagging works.
2020

2121
Current Trunk
2222
-------------
23+
- Emscripten now builds a complete sysroot insde the EM_CACHE directory.
24+
This includes the system headers which get copied into place there rather
25+
than adding a sequence of extra include directories.
2326

2427
2.0.11: 12/17/2020
2528
------------------

embuilder.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,9 @@ def main():
129129

130130
if args.lto:
131131
shared.Settings.LTO = "full"
132-
# Reconfigure the cache dir to reflect the change
133-
shared.reconfigure_cache()
134132

135133
if args.pic:
136134
shared.Settings.RELOCATABLE = 1
137-
# Reconfigure the cache dir to reflect the change
138-
shared.reconfigure_cache()
139135

140136
if args.force:
141137
force = True
@@ -177,6 +173,10 @@ def main():
177173
if force:
178174
library.erase()
179175
library.get_path()
176+
elif what == 'sysroot':
177+
if force:
178+
shared.Cache.erase_file('sysroot_install.stamp')
179+
system_libs.ensure_sysroot()
180180
elif what == 'struct_info':
181181
if force:
182182
shared.Cache.erase_file('generated_struct_info.json')

emcc.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,16 +1329,6 @@ def filter_out_duplicate_dynamic_libs(inputs):
13291329
'_emscripten_stack_get_end',
13301330
'_emscripten_stack_set_limits']
13311331

1332-
# Reconfigure the cache now that settings have been applied. Some settings
1333-
# such as LTO and SIDE_MODULE/MAIN_MODULE effect which cache directory we use.
1334-
shared.reconfigure_cache()
1335-
1336-
if not compile_only and not options.post_link:
1337-
ldflags = shared.emsdk_ldflags(newargs)
1338-
for f in ldflags:
1339-
newargs.append(f)
1340-
add_link_flag(len(newargs), f)
1341-
13421332
# SSEx is implemented on top of SIMD128 instruction set, but do not pass SSE flags to LLVM
13431333
# so it won't think about generating native x86 SSE code.
13441334
newargs = [x for x in newargs if x not in shared.SIMD_INTEL_FEATURE_TOWER and x not in shared.SIMD_NEON_FLAGS]
@@ -1943,6 +1933,7 @@ def is_link_flag(flag):
19431933

19441934
compile_args = [a for a in newargs if a and not is_link_flag(a)]
19451935
cflags = calc_cflags(options)
1936+
system_libs.ensure_sysroot()
19461937
system_libs.add_ports_cflags(cflags, shared.Settings)
19471938

19481939
def use_cxx(src):
@@ -1969,9 +1960,8 @@ def get_compiler(cxx):
19691960
return CC
19701961

19711962
def get_clang_command(src_file):
1972-
cxx = use_cxx(src_file)
1973-
per_file_cflags = shared.get_cflags(args, cxx)
1974-
return get_compiler(cxx) + cflags + per_file_cflags + compile_args + [src_file]
1963+
per_file_cflags = shared.get_cflags(args)
1964+
return get_compiler(use_cxx(src_file)) + cflags + per_file_cflags + compile_args + [src_file]
19751965

19761966
def get_clang_command_asm(src_file):
19771967
asflags = shared.get_asmflags()
@@ -2067,6 +2057,10 @@ def compile_source_file(i, input_file):
20672057
if specified_target and specified_target.startswith('-'):
20682058
exit_with_error('invalid output filename: `%s`' % specified_target)
20692059

2060+
ldflags = shared.emsdk_ldflags(newargs)
2061+
for f in ldflags:
2062+
add_link_flag(sys.maxsize, f)
2063+
20702064
using_lld = not (link_to_object and shared.Settings.LTO)
20712065
link_flags = filter_link_flags(link_flags, using_lld)
20722066

src/struct_info.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1525,7 +1525,7 @@
15251525
]
15261526
},
15271527
{
1528-
"file": "../lib/libc/musl/src/internal/pthread_impl.h",
1528+
"file": "system/lib/libc/musl/src/internal/pthread_impl.h",
15291529
"structs": {
15301530
"pthread": [
15311531
"threadStatus",
@@ -1548,7 +1548,7 @@
15481548
"defines": ["__ATTRP_C11_THREAD"]
15491549
},
15501550
{
1551-
"file": "../lib/libc/musl/src/internal/libc.h",
1551+
"file": "system/lib/libc/musl/src/internal/libc.h",
15521552
"structs": {
15531553
"libc": [
15541554
"global_locale"

system/lib/fetch/asmfs.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#include <emscripten/emscripten.h>
1616
#include <emscripten/fetch.h>
1717
#include <emscripten/threading.h>
18-
#include <libc/fcntl.h>
18+
#include <fcntl.h>
1919
#include <math.h>
2020
#include <string.h>
2121
#include <sys/stat.h>

tests/test_other.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,10 +471,10 @@ def test_emcc_asm_v_wasm(self):
471471
def test_emcc_cflags(self):
472472
output = self.run_process([EMCC, '--cflags'], stdout=PIPE)
473473
flags = output.stdout.strip()
474-
self.assertContained(shared.shlex_join(shared.emsdk_cflags([], False)), flags)
474+
self.assertContained(shared.shlex_join(shared.emsdk_cflags([])), flags)
475475
output = self.run_process([EMXX, '--cflags'], stdout=PIPE)
476476
flags = output.stdout.strip()
477-
self.assertContained(shared.shlex_join(shared.emsdk_cflags([], True)), flags)
477+
self.assertContained(shared.shlex_join(shared.emsdk_cflags([])), flags)
478478
# check they work
479479
cmd = [CLANG_CXX, path_from_root('tests', 'hello_world.cpp')] + shlex.split(flags.replace('\\', '\\\\')) + ['-c', '-emit-llvm', '-o', 'a.bc']
480480
self.run_process(cmd)

tests/test_sanity.py

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from tools.shared import EXPECTED_LLVM_VERSION, Cache
2222
from tools import shared, system_libs, utils
2323

24-
SANITY_FILE = shared.Cache.get_path('sanity.txt', root=True)
24+
SANITY_FILE = shared.Cache.get_path('sanity.txt')
2525
commands = [[EMCC], [PYTHON, path_from_root('tests', 'runner.py'), 'blahblah']]
2626

2727

@@ -415,31 +415,29 @@ def ensure_cache(self):
415415
self.do([EMCC, '-O2', path_from_root('tests', 'hello_world.c')])
416416

417417
def test_emcc_caching(self):
418-
BUILDING_MESSAGE = 'generating system library: X'
418+
BUILDING_MESSAGE = 'generating system library: %s'
419419

420420
restore_and_set_up()
421421
self.erase_cache()
422422

423423
# Building a file that *does* need something *should* trigger cache
424424
# generation, but only the first time
425-
libname = 'libc++'
425+
libname = Cache.get_lib_name('libc++.a')
426426
for i in range(3):
427427
print(i)
428428
self.clear()
429429
output = self.do([EMCC, '-O' + str(i), path_from_root('tests', 'hello_libcxx.cpp'), '-s', 'DISABLE_EXCEPTION_CATCHING=0'])
430430
print('\n\n\n', output)
431-
self.assertContainedIf(BUILDING_MESSAGE.replace('X', libname), output, i == 0)
431+
self.assertContainedIf(BUILDING_MESSAGE % libname, output, i == 0)
432432
self.assertContained('hello, world!', self.run_js('a.out.js'))
433433
self.assertExists(Cache.dirname)
434-
full_libname = libname + '.bc' if libname != 'libc++' else libname + '.a'
435-
self.assertExists(os.path.join(Cache.dirname, full_libname))
434+
self.assertExists(os.path.join(Cache.dirname, libname))
436435

437436
def test_cache_clearing_manual(self):
438437
# Manual cache clearing
439438
restore_and_set_up()
440439
self.ensure_cache()
441440
self.assertTrue(os.path.exists(Cache.dirname))
442-
self.assertTrue(os.path.exists(Cache.root_dirname))
443441
output = self.do([EMCC, '--clear-cache'])
444442
self.assertIn('clearing cache', output)
445443
self.assertIn(SANITY_MESSAGE, output)
@@ -463,12 +461,10 @@ def test_FROZEN_CACHE(self):
463461
self.erase_cache()
464462
self.ensure_cache()
465463
self.assertTrue(os.path.exists(Cache.dirname))
466-
self.assertTrue(os.path.exists(Cache.root_dirname))
467464
# changing config file should not clear cache
468465
add_to_config('FROZEN_CACHE = True')
469466
self.do([EMCC])
470467
self.assertTrue(os.path.exists(Cache.dirname))
471-
self.assertTrue(os.path.exists(Cache.root_dirname))
472468
# building libraries is disallowed
473469
output = self.do([EMBUILDER, 'build', 'libemmalloc'])
474470
self.assertIn('FROZEN_CACHE disallows building system libs', output)
@@ -487,6 +483,7 @@ def test_emcc_multiprocess_cache_access(self):
487483
}
488484
''')
489485
cache_dir_name = self.in_dir('test_cache')
486+
libname = Cache.get_lib_name('libc.a')
490487
with env_modify({'EM_CACHE': cache_dir_name}):
491488
tasks = []
492489
num_times_libc_was_built = 0
@@ -495,13 +492,13 @@ def test_emcc_multiprocess_cache_access(self):
495492
tasks += [p]
496493
for p in tasks:
497494
print('stdout:\n', p.stdout)
498-
if 'generating system library: libc' in p.stdout:
495+
if 'generating system library: ' + libname in p.stdout:
499496
num_times_libc_was_built += 1
500497

501498
# The cache directory must exist after the build
502499
self.assertTrue(os.path.exists(cache_dir_name))
503500
# The cache directory must contain a built libc
504-
self.assertTrue(os.path.exists(os.path.join(cache_dir_name, 'wasm', 'libc.a')))
501+
self.assertTrue(os.path.exists(os.path.join(cache_dir_name, libname)))
505502
# Exactly one child process should have triggered libc build!
506503
self.assertEqual(num_times_libc_was_built, 1)
507504

@@ -535,12 +532,11 @@ def test_emcc_ports(self):
535532
restore_and_set_up()
536533

537534
# listing ports
538-
539535
out = self.do([EMCC, '--show-ports'])
540-
assert 'Available ports:' in out, out
541-
assert 'SDL2' in out, out
542-
assert 'SDL2_image' in out, out
543-
assert 'SDL2_net' in out, out
536+
self.assertContained('Available ports:', out)
537+
self.assertContained('SDL2', out)
538+
self.assertContained('SDL2_image', out)
539+
self.assertContained('SDL2_net', out)
544540

545541
# using ports
546542
RETRIEVING_MESSAGE = 'retrieving port'
@@ -555,27 +551,27 @@ def test_emcc_ports(self):
555551
try_delete(PORTS_DIR)
556552
else:
557553
self.do([EMCC, '--clear-ports'])
558-
assert not os.path.exists(PORTS_DIR)
554+
self.assertNotExists(PORTS_DIR)
559555

560556
# Building a file that doesn't need ports should not trigger anything
561557
output = self.do([EMCC, path_from_root('tests', 'hello_world_sdl.cpp')])
562558
assert RETRIEVING_MESSAGE not in output, output
563559
assert BUILDING_MESSAGE not in output
564560
print('no', output)
565-
assert not os.path.exists(PORTS_DIR)
561+
self.assertNotExists(PORTS_DIR)
566562

567563
def first_use():
568564
output = self.do([EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-s', 'USE_SDL=2'])
569-
assert RETRIEVING_MESSAGE in output, output
570-
assert BUILDING_MESSAGE in output, output
565+
self.assertContained(RETRIEVING_MESSAGE, output)
566+
self.assertContained(BUILDING_MESSAGE, output)
571567
self.assertExists(PORTS_DIR)
572568
print('yes', output)
573569

574570
def second_use():
575571
# Using it again avoids retrieve and build
576572
output = self.do([EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-s', 'USE_SDL=2'])
577-
assert RETRIEVING_MESSAGE not in output, output
578-
assert BUILDING_MESSAGE not in output, output
573+
self.assertNotContained(RETRIEVING_MESSAGE, output)
574+
self.assertNotContained(BUILDING_MESSAGE, output)
579575

580576
# Building a file that need a port does trigger stuff
581577
first_use()
@@ -699,10 +695,10 @@ def test_embuilder_wasm_backend(self):
699695
# the --lto flag makes us build wasm-bc
700696
self.do([EMCC, '--clear-cache'])
701697
self.run_process([EMBUILDER, 'build', 'libemmalloc'])
702-
self.assertExists(os.path.join(config.CACHE, 'wasm'))
698+
self.assertExists(os.path.join(config.CACHE, 'sysroot', 'lib', 'wasm32-emscripten'))
703699
self.do([EMCC, '--clear-cache'])
704700
self.run_process([EMBUILDER, 'build', 'libemmalloc', '--lto'])
705-
self.assertExists(os.path.join(config.CACHE, 'wasm-lto'))
701+
self.assertExists(os.path.join(config.CACHE, 'sysroot', 'lib', 'wasm32-emscripten', 'lto'))
706702

707703
def test_binaryen_version(self):
708704
restore_and_set_up()

tools/cache.py

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,9 @@ class Cache:
2424
# acquired.
2525
EM_EXCLUSIVE_CACHE_ACCESS = int(os.environ.get('EM_EXCLUSIVE_CACHE_ACCESS', '0'))
2626

27-
def __init__(self, dirname, use_subdir=True):
27+
def __init__(self, dirname):
2828
# figure out the root directory for all caching
2929
dirname = os.path.normpath(dirname)
30-
self.root_dirname = dirname
31-
32-
# if relevant, use a subdir of the cache
33-
if use_subdir:
34-
subdir = 'wasm'
35-
if shared.Settings.LTO:
36-
subdir += '-lto'
37-
if shared.Settings.RELOCATABLE:
38-
subdir += '-pic'
39-
if shared.Settings.MEMORY64:
40-
subdir += '-memory64'
41-
dirname = os.path.join(dirname, subdir)
42-
4330
self.dirname = dirname
4431
self.acquired_count = 0
4532

@@ -90,28 +77,57 @@ def ensure(self):
9077

9178
def erase(self):
9279
with self.lock():
93-
if os.path.exists(self.root_dirname):
94-
for f in os.listdir(self.root_dirname):
95-
tempfiles.try_delete(os.path.join(self.root_dirname, f))
80+
if os.path.exists(self.dirname):
81+
for f in os.listdir(self.dirname):
82+
tempfiles.try_delete(os.path.join(self.dirname, f))
83+
84+
def get_path(self, name):
85+
return os.path.join(self.dirname, name)
86+
87+
def get_sysroot_dir(self, absolute):
88+
if absolute:
89+
return os.path.join(self.dirname, 'sysroot')
90+
return 'sysroot'
91+
92+
def get_include_dir(self):
93+
return os.path.join(self.get_sysroot_dir(absolute=True), 'include')
9694

97-
def get_path(self, shortname, root=False):
98-
if root:
99-
return os.path.join(self.root_dirname, shortname)
100-
return os.path.join(self.dirname, shortname)
95+
def get_lib_dir(self, absolute):
96+
path = os.path.join(self.get_sysroot_dir(absolute=absolute), 'lib')
97+
if shared.Settings.MEMORY64:
98+
path = os.path.join(path, 'wasm64-emscripten')
99+
else:
100+
path = os.path.join(path, 'wasm32-emscripten')
101+
# if relevant, use a subdir of the cache
102+
subdir = []
103+
if shared.Settings.LTO:
104+
subdir.append('lto')
105+
if shared.Settings.RELOCATABLE:
106+
subdir.append('pic')
107+
if subdir:
108+
path = os.path.join(path, '-'.join(subdir))
109+
return path
110+
111+
def get_lib_name(self, name):
112+
return os.path.join(self.get_lib_dir(absolute=False), name)
113+
114+
def erase_lib(self, name):
115+
self.erase_file(self.get_lib_name(name))
101116

102117
def erase_file(self, shortname):
103118
name = os.path.join(self.dirname, shortname)
104119
if os.path.exists(name):
105120
logging.info('Cache: deleting cached file: %s', name)
106121
tempfiles.try_delete(name)
107122

123+
def getlib(self, libname, *args, **kwargs):
124+
name = self.get_lib_name(libname)
125+
return self.get(name, *args, **kwargs)
126+
108127
# Request a cached file. If it isn't in the cache, it will be created with
109128
# the given creator function
110-
def get(self, shortname, creator, what=None, force=False, root=False):
111-
if root:
112-
cachename = os.path.join(self.root_dirname, shortname)
113-
else:
114-
cachename = os.path.join(self.dirname, shortname)
129+
def get(self, shortname, creator, what=None, force=False):
130+
cachename = os.path.join(self.dirname, shortname)
115131
cachename = os.path.abspath(cachename)
116132
# Check for existence before taking the lock in case we can avoid the
117133
# lock completely.

tools/filelock.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ def acquire(self, timeout=None, poll_intervall=0.05):
274274
self._acquire()
275275

276276
if self.is_locked:
277-
logger().info('Lock %s acquired on %s', lock_id, lock_filename)
277+
logger().debug('Lock %s acquired on %s', lock_id, lock_filename)
278278
break
279279
elif timeout >= 0 and time.time() - start_time > timeout:
280280
logger().debug('Timeout on acquiring lock %s on %s', lock_id, lock_filename)
@@ -318,7 +318,7 @@ def release(self, force = False):
318318
logger().debug('Attempting to release lock %s on %s', lock_id, lock_filename)
319319
self._release()
320320
self._lock_counter = 0
321-
logger().info('Lock %s released on %s', lock_id, lock_filename)
321+
logger().debug('Lock %s released on %s', lock_id, lock_filename)
322322

323323
return None
324324

tools/gen_struct_info.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ def inspect_code(headers, cpp_opts, structs, defines):
246246
'-O0',
247247
'-Werror',
248248
'-Wno-format',
249+
'-I', shared.path_from_root(),
249250
'-s', 'BOOTSTRAPPING_STRUCT_INFO=1',
250251
'-s', 'WARN_ON_UNDEFINED_SYMBOLS=0',
251252
'-s', 'STRICT=1',

0 commit comments

Comments
 (0)