Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/tools/system_libs.py
6171 views
1
# Copyright 2014 The Emscripten Authors. All rights reserved.
2
# Emscripten is available under two separate licenses, the MIT license and the
3
# University of Illinois/NCSA Open Source License. Both these licenses can be
4
# found in the LICENSE file.
5
6
import itertools
7
import logging
8
import os
9
import re
10
import shlex
11
import shutil
12
import subprocess
13
import textwrap
14
from enum import IntEnum, auto
15
from glob import iglob
16
from time import time
17
18
from . import building, cache, diagnostics, shared, utils
19
from .settings import settings
20
from .toolchain_profiler import ToolchainProfiler
21
from .utils import read_file
22
23
logger = logging.getLogger('system_libs')
24
25
# Files that are part of libsockets.a and so should be excluded from libc.a
26
LIBC_SOCKETS = ['socket.c', 'socketpair.c', 'shutdown.c', 'bind.c', 'connect.c',
27
'listen.c', 'accept.c', 'getsockname.c', 'getpeername.c', 'send.c',
28
'recv.c', 'sendto.c', 'recvfrom.c', 'sendmsg.c', 'recvmsg.c',
29
'getsockopt.c', 'setsockopt.c', 'freeaddrinfo.c',
30
'gethostbyaddr.c', 'gethostbyaddr_r.c', 'gethostbyname.c',
31
'gethostbyname_r.c', 'gethostbyname2.c', 'gethostbyname2_r.c',
32
'in6addr_any.c', 'in6addr_loopback.c', 'accept4.c']
33
34
# Experimental: Setting EMCC_USE_NINJA will cause system libraries to get built with ninja rather
35
# than simple subprocesses. The primary benefit here is that we get accurate dependency tracking.
36
# This means we can avoid completely rebuilding a library and just rebuild based on what changed.
37
#
38
# Setting EMCC_USE_NINJA=2 means that ninja will automatically be run for each library needed at
39
# link time.
40
USE_NINJA = int(os.environ.get('EMCC_USE_NINJA', '0'))
41
42
# A (fake) deterministic emscripten path to use in __FILE__ macro and debug info
43
# to produce reproducible builds across platforms.
44
DETERMINISTIC_PREFIX = '/emsdk/emscripten'
45
46
47
def files_in_path(path, filenames):
48
srcdir = utils.path_from_root(path)
49
return [os.path.join(srcdir, f) for f in filenames]
50
51
52
def glob_in_path(path, glob_pattern, excludes=()):
53
srcdir = utils.path_from_root(path)
54
files = iglob(os.path.join(srcdir, glob_pattern), recursive=True)
55
return sorted(f for f in files if os.path.basename(f) not in excludes)
56
57
58
def get_base_cflags(build_dir, force_object_files=False, preprocess=True):
59
# Always build system libraries with debug information. Non-debug builds
60
# will ignore this at link time because we link with `-strip-debug`.
61
flags = ['-g', '-sSTRICT', '-Werror']
62
if settings.LTO and not force_object_files:
63
flags += ['-flto=' + settings.LTO]
64
if settings.RELOCATABLE or settings.MAIN_MODULE:
65
# Explicitly include `-sRELOCATABLE` when building system libraries.
66
# `-fPIC` alone is not enough to configure trigger the building and
67
# caching of `pic` libraries (see `get_lib_dir` in `cache.py`)
68
# FIXME(sbc): `-fPIC` should really be enough here.
69
flags += ['-fPIC', '-sRELOCATABLE']
70
if preprocess:
71
flags += ['-DEMSCRIPTEN_DYNAMIC_LINKING']
72
if settings.MEMORY64:
73
flags += ['-sMEMORY64=' + str(settings.MEMORY64)]
74
75
source_dir = utils.path_from_root()
76
relative_source_dir = os.path.relpath(source_dir, build_dir)
77
flags += [f'-ffile-prefix-map={source_dir}={DETERMINISTIC_PREFIX}',
78
f'-ffile-prefix-map={relative_source_dir}={DETERMINISTIC_PREFIX}',
79
f'-fdebug-compilation-dir={DETERMINISTIC_PREFIX}']
80
return flags
81
82
83
def get_build_dir():
84
return cache.get_path('build')
85
86
87
def clean_env():
88
# building system libraries and ports should be hermetic in that it is not
89
# affected by things like EMCC_CFLAGS which the user may have set.
90
# At least one port also uses autoconf (harfbuzz) so we also need to clear
91
# CFLAGS/LDFLAGS which we don't want to affect the inner call to configure.
92
safe_env = os.environ.copy()
93
for opt in ['CPATH', 'C_INCLUDE_PATH', 'CPLUS_INCLUDE_PATH', 'OBJC_INCLUDE_PATH',
94
'CFLAGS', 'CXXFLAGS', 'LDFLAGS',
95
'EMCC_CFLAGS',
96
'EMCC_AUTODEBUG',
97
'EMCC_FORCE_STDLIBS',
98
'EMCC_ONLY_FORCED_STDLIBS',
99
'EMMAKEN_JUST_CONFIGURE']:
100
if opt in safe_env:
101
del safe_env[opt]
102
return safe_env
103
104
105
def run_build_commands(commands, num_inputs, build_dir=None):
106
# Before running a set of build commands make sure the common sysroot
107
# headers are installed. This prevents each sub-process from attempting
108
# to setup the sysroot itself.
109
ensure_sysroot()
110
start_time = time()
111
shared.run_multiple_processes(commands, env=clean_env(), cwd=build_dir)
112
logger.info(f'compiled {num_inputs} inputs in {time() - start_time:.2f}s')
113
114
115
def objectfile_sort_key(filename):
116
"""Sort object files that we pass to llvm-ar."""
117
# In general, we simply use alphabetical order, but we special case certain
118
# object files such they come first. For example, `vmlock.o` contains the
119
# definition of `__vm_wait`, but `mmap.o` also contains a dummy/weak definition
120
# for use by `mmap.o` when `vmlock.o` is not otherwise included.
121
#
122
# When another object looks for `__vm_wait` we prefer that it always find the
123
# real definition first and not refer to the dummy one (which is really
124
# intended to be local to `mmap.o` but due to the fact the weak aliases can't
125
# have internal linkage).
126
#
127
# The reason we care is that once an object file is pulled into certain aspects
128
# of it cannot be undone/removed by the linker. For example, static
129
# constructors or stub library dependencies.
130
#
131
# In the case of `mmap.o`, once it get included by the linker, it pulls in the
132
# the reverse dependencies of the mmap syscall (memalign). If we don't do this
133
# sorting we see a slight regression in test_metadce_minimal_pthreads due to
134
# memalign being included.
135
basename = os.path.basename(filename)
136
if basename in {'vmlock.o'}:
137
return 'AAA_' + basename
138
else:
139
return basename
140
141
142
def create_lib(libname, inputs):
143
"""Create a library from a set of input objects."""
144
suffix = utils.suffix(libname)
145
146
inputs = sorted(inputs, key=objectfile_sort_key)
147
if suffix in ('.bc', '.o'):
148
if len(inputs) == 1:
149
if inputs[0] != libname:
150
shutil.copyfile(inputs[0], libname)
151
else:
152
building.link_to_object(inputs, libname)
153
else:
154
assert suffix == '.a'
155
building.emar('cr', libname, inputs)
156
157
158
def get_top_level_ninja_file():
159
return os.path.join(get_build_dir(), 'build.ninja')
160
161
162
def run_ninja(build_dir):
163
cmd = ['ninja', '-C', build_dir, f'-j{utils.get_num_cores()}']
164
if shared.PRINT_SUBPROCS:
165
cmd.append('-v')
166
shared.check_call(cmd, env=clean_env())
167
168
169
def ensure_target_in_ninja_file(ninja_file, target):
170
if os.path.isfile(ninja_file) and target in read_file(ninja_file):
171
return
172
with open(ninja_file, 'a') as f:
173
f.write(target + '\n')
174
175
176
def escape_ninja_path(path):
177
"""Escape a path to be used in a ninja file."""
178
# Replace Windows backslashes with forward slashes.
179
path = path.replace('\\', '/')
180
# Escape special Ninja chars.
181
return re.sub(r'([ :$])', r'$\1', path)
182
183
184
def create_ninja_file(input_files, filename, libname, cflags, asflags=None, customize_build_flags=None):
185
if asflags is None:
186
asflags = []
187
188
join_cmd = shlex.join if os.name == 'posix' else subprocess.list2cmdline
189
190
out = f'''\
191
# Automatically generated by tools/system_libs.py. DO NOT EDIT
192
193
ninja_required_version = 1.5
194
195
ASFLAGS = {join_cmd(asflags)}
196
CFLAGS = {join_cmd(cflags)}
197
EMCC = {shared.EMCC}
198
EMXX = {shared.EMXX}
199
EMAR = {shared.EMAR}
200
201
rule cc
202
depfile = $out.d
203
command = $EMCC -MD -MF $out.d $CFLAGS -c $in -o $out
204
description = CC $out
205
206
rule cxx
207
depfile = $out.d
208
command = $EMXX -MD -MF $out.d $CFLAGS -c $in -o $out
209
description = CXX $out
210
211
rule asm
212
command = $EMCC $ASFLAGS -c $in -o $out
213
description = ASM $out
214
215
rule asm_cpp
216
depfile = $out.d
217
command = $EMCC -MD -MF $out.d $CFLAGS -c $in -o $out
218
description = ASM $out
219
220
rule direct_cc
221
depfile = $with_depfile
222
command = $EMCC -MD -MF $with_depfile $CFLAGS -c $in -o $out
223
description = CC $out
224
225
rule archive
226
# Workaround command line too long issue (https://github.com/ninja-build/ninja/pull/217) by using a response file.
227
rspfile = $out.rsp
228
rspfile_content = $in
229
command = $EMAR cr $out @$rspfile
230
description = AR $out
231
232
'''
233
suffix = utils.suffix(libname)
234
build_dir = os.path.dirname(filename)
235
236
if suffix == '.o':
237
assert len(input_files) == 1
238
input_file = escape_ninja_path(input_files[0])
239
depfile = utils.unsuffixed_basename(input_file) + '.d'
240
out += f'build {escape_ninja_path(libname)}: direct_cc {input_file}\n'
241
out += f' with_depfile = {depfile}\n'
242
else:
243
objects = []
244
for src in input_files:
245
# Resolve duplicates by appending unique. This is needed on case
246
# insensitive filesystem to handle, for example, _exit.o and _Exit.o.
247
# This is done even on case sensitive filesystem so that builds are
248
# reproducible across platforms.
249
object_basename = utils.unsuffixed_basename(src).lower()
250
o = os.path.join(build_dir, object_basename + '.o')
251
object_uuid = 0
252
# Find a unique basename
253
while o in objects:
254
object_uuid += 1
255
o = os.path.join(build_dir, f'{object_basename}__{object_uuid}.o')
256
objects.append(o)
257
ext = utils.suffix(src)
258
match ext:
259
case '.s':
260
cmd = 'asm'
261
flags = asflags
262
case '.S':
263
cmd = 'asm_cpp'
264
flags = cflags
265
case '.c':
266
cmd = 'cc'
267
flags = cflags
268
case _:
269
cmd = 'cxx'
270
flags = cflags
271
out += f'build {escape_ninja_path(o)}: {cmd} {escape_ninja_path(src)}\n'
272
if customize_build_flags:
273
custom_flags = customize_build_flags(flags, src)
274
if custom_flags != flags:
275
out += f' CFLAGS = {join_cmd(custom_flags)}'
276
out += '\n'
277
278
objects = sorted(objects, key=objectfile_sort_key)
279
objects = ' '.join(escape_ninja_path(o) for o in objects)
280
out += f'build {escape_ninja_path(libname)}: archive {objects}\n'
281
282
utils.write_file(filename, out)
283
ensure_target_in_ninja_file(get_top_level_ninja_file(), f'subninja {escape_ninja_path(filename)}')
284
285
286
class Library:
287
"""
288
`Library` is the base class of all system libraries.
289
290
There are two types of libraries: abstract and concrete.
291
* An abstract library, e.g. MTLibrary, is a subclass of `Library` that
292
implements certain behaviour common to multiple libraries. The features
293
of multiple abstract libraries can be used through multiple inheritance.
294
* A concrete library, e.g. libc, is a subclass of `Library` that describes
295
how to build a particular library, and its properties, such as name and
296
dependencies.
297
298
This library system is meant to handle having many versions of the same library,
299
which we call *variations*. For example, some libraries (those that inherit
300
from MTLibrary), have both single-threaded and multi-threaded versions.
301
302
An instance of a `Library` subclass represents a specific variation of the
303
library. Instance methods perform operations relating to this variation.
304
For example, `get_cflags()` would return the emcc flags needed to build this
305
variation, and `build()` would generate the library file for this variation.
306
The constructor takes keyword arguments that defines the variation.
307
308
Class methods perform tasks relating to all variations. For example,
309
`variations()` returns a list of all variations that exists for this library,
310
and `get_default_variation()` returns the variation suitable for the current
311
environment.
312
313
Other class methods act upon a group of libraries. For example,
314
`Library.get_all_variations()` returns a mapping of all variations of
315
existing libraries.
316
317
To add a new type of variation, you must add an parameter to `__init__` that
318
selects the variant. Then, override one of `vary_on` or `variations`, as well
319
as `get_default_variation`.
320
321
If the parameter is boolean, overriding `vary_on` to add the parameter name
322
to the returned list is sufficient:
323
324
@classmethod
325
def vary_on(cls):
326
return super().vary_on() + ['my_parameter']
327
328
Otherwise, you must override `variations`:
329
330
@classmethod
331
def variations(cls):
332
return [{'my_parameter': value, **other} for value, other in
333
itertools.product([1, 2, 3], super().variations())]
334
335
Overriding either `vary_on` or `variations` allows `embuilder.py` to know all
336
possible variations so it can build all of them.
337
338
You then need to modify `get_default_variation` to detect the correct value
339
for your new parameter based on the settings:
340
341
@classmethod
342
def get_default_variation(cls, **kwargs):
343
return super().get_default_variation(my_parameter=settings.MY_PARAMETER, **kwargs)
344
345
This allows the correct variation of the library to be selected when building
346
code with Emscripten.
347
"""
348
349
# The simple name of the library. When linking, this is the name to use to
350
# automatically get the correct version of the library.
351
# This should only be overridden in a concrete library class, e.g. libc,
352
# and left as None in an abstract library class, e.g. MTLibrary.
353
name: str | None = None
354
355
# Set to true to prevent EMCC_FORCE_STDLIBS from linking this library.
356
never_force = False
357
358
# A list of flags to pass to emcc.
359
# The flags for the parent class is automatically inherited.
360
# TODO: Investigate whether perf gains from loop unrolling would be worth the
361
# extra code size. The -fno-unroll-loops flags was added here when loop
362
# unrolling landed upstream in LLVM to avoid changing behavior but was not
363
# specifically evaluated.
364
cflags = ['-O2', '-Wall', '-fno-unroll-loops']
365
366
# A list of directories to put in the include path when building.
367
# This is a list of tuples of path components.
368
# For example, to put system/lib/a and system/lib/b under the emscripten
369
# directory into the include path, you would write:
370
# includes = [('system', 'lib', 'a'), ('system', 'lib', 'b')]
371
# The include path of the parent class is automatically inherited.
372
includes: list[str] = []
373
374
# By default, `get_files` look for source files for this library under `src_dir`.
375
# It will either use the files listed in `src_files`, or use the glob pattern in
376
# `src_glob`. You may not specify both `src_files` and `src_glob`.
377
# When using `src_glob`, you can specify a list of files in `src_glob_exclude`
378
# to be excluded from the library.
379
# Alternatively, you can override `get_files` to use your own logic.
380
src_dir: str | None = None
381
src_files: list[str] | None = []
382
src_glob: str | None = None
383
src_glob_exclude: list[str] | None = None
384
385
# Whether to always generate WASM object files, even when LTO is set
386
force_object_files = False
387
388
def __init__(self):
389
"""
390
Creates a variation of this library.
391
392
A variation is a specific combination of settings a library can have.
393
For example, libc++-mt-noexcept is a variation of libc++.
394
There might be only one variation of a library.
395
396
The constructor keyword arguments will define what variation to use.
397
398
Use the `variations` classmethod to get the list of all possible constructor
399
arguments for this library.
400
401
Use the `get_default_variation` classmethod to construct the variation
402
suitable for the current invocation of emscripten.
403
"""
404
if not self.name:
405
raise NotImplementedError('Cannot instantiate an abstract library')
406
407
def can_use(self):
408
"""
409
Whether this library can be used in the current environment.
410
411
For example, libmalloc would override this and return False
412
if the user requested no malloc.
413
"""
414
return True
415
416
def can_build(self):
417
"""
418
Whether this library can be built in the current environment.
419
420
Override this if, for example, the library can only be built on WASM backend.
421
"""
422
return True
423
424
def erase(self):
425
cache.erase_file(self.get_path())
426
427
def get_path(self, absolute=False):
428
return cache.get_lib_name(self.get_filename(), absolute=absolute)
429
430
def build(self):
431
"""
432
Gets the cached path of this library.
433
434
This will trigger a build if this library is not in the cache.
435
"""
436
return cache.get(self.get_path(), self.do_build, force=USE_NINJA == 2, quiet=USE_NINJA)
437
438
def generate(self):
439
return cache.get(self.get_path(), self.do_generate, force=USE_NINJA == 2, quiet=USE_NINJA)
440
441
def get_link_flag(self):
442
"""
443
Gets the link flags needed to use the library.
444
445
This will trigger a build if this library is not in the cache.
446
"""
447
fullpath = self.build()
448
# For non-libraries (e.g. crt1.o) we pass the entire path to the linker
449
if self.get_ext() != '.a':
450
return fullpath
451
# For libraries (.a) files, we pass the abbreviated `-l` form.
452
base = utils.unsuffixed_basename(fullpath)
453
return '-l' + base.removeprefix('lib')
454
455
def get_files(self):
456
"""
457
Gets a list of source files for this library.
458
459
Typically, you will use `src_dir`, `src_files`, `src_glob` and `src_glob_exclude`.
460
If those are insufficient to describe the files needed, you can override this method.
461
"""
462
if self.src_dir:
463
if self.src_files and self.src_glob:
464
raise Exception('Cannot use src_files and src_glob together')
465
466
if self.src_files:
467
return files_in_path(self.src_dir, self.src_files)
468
elif self.src_glob:
469
return glob_in_path(self.src_dir, self.src_glob, self.src_glob_exclude or ())
470
471
raise NotImplementedError()
472
473
def generate_ninja(self, build_dir, libname):
474
ensure_sysroot()
475
utils.safe_ensure_dirs(build_dir)
476
self.build_dir = build_dir
477
478
cflags = self.get_cflags()
479
asflags = get_base_cflags(self.build_dir, preprocess=False)
480
input_files = self.get_files()
481
ninja_file = os.path.join(build_dir, 'build.ninja')
482
create_ninja_file(input_files, ninja_file, libname, cflags, asflags=asflags, customize_build_flags=self.customize_build_cmd)
483
484
def build_objects(self, build_dir):
485
"""
486
Returns a list of compiled object files for this library.
487
488
By default, this builds all the source files returned by `self.get_files()`,
489
with the `cflags` returned by `self.get_cflags()`.
490
"""
491
batch_inputs = int(os.environ.get('EMCC_BATCH_BUILD', '1'))
492
self.build_dir = build_dir
493
batches = {}
494
commands = []
495
objects = set()
496
objects_lowercase = set()
497
cflags = self.get_cflags()
498
for src in self.get_files():
499
ext = utils.suffix(src)
500
if ext in {'.s', '.S', '.c'}:
501
cmd = shared.EMCC
502
else:
503
cmd = shared.EMXX
504
cmd = [cmd, '-c']
505
if ext == '.s':
506
# .s files are processed directly by the assembler. In this case we can't pass
507
# pre-processor flags such as `-I` and `-D` but we still want core flags such as
508
# `-sMEMORY64`.
509
cmd += get_base_cflags(self.build_dir, preprocess=False)
510
else:
511
cmd += cflags
512
cmd = self.customize_build_cmd(cmd, src)
513
514
object_basename = utils.unsuffixed_basename(src)
515
o = os.path.join(build_dir, object_basename + '.o')
516
if o.lower() in objects_lowercase:
517
# If we have seen a file with the same name before, we need a separate
518
# command to compile this file with a custom unique output object
519
# filename, as batch compile doesn't allow such customization.
520
#
521
# This is needed to handle, for example, _exit.o and _Exit.o.
522
object_uuid = 0
523
# Find a unique basename
524
while o.lower() in objects_lowercase:
525
object_uuid += 1
526
o = os.path.join(build_dir, f'{object_basename}__{object_uuid}.o')
527
commands.append(cmd + [src, '-o', o])
528
elif batch_inputs:
529
# Use relative paths to reduce the length of the command line.
530
# This allows to avoid switching to a response file as often.
531
src = os.path.relpath(src, build_dir)
532
src = utils.normalize_path(src)
533
batches.setdefault(tuple(cmd), []).append(src)
534
else:
535
commands.append(cmd + [src, '-o', o])
536
objects.add(o)
537
objects_lowercase.add(o.lower())
538
539
if batch_inputs:
540
# Choose a chunk size that is large enough to avoid too many subprocesses
541
# but not too large to avoid task starvation.
542
# For now the heuristic is to split inputs by 2x number of cores.
543
chunk_size = max(1, len(objects) // (2 * utils.get_num_cores()))
544
# Convert batches to commands.
545
for cmd, srcs in batches.items():
546
cmd = list(cmd)
547
for i in range(0, len(srcs), chunk_size):
548
chunk_srcs = srcs[i:i + chunk_size]
549
commands.append(building.get_command_with_possible_response_file(cmd + chunk_srcs))
550
551
run_build_commands(commands, num_inputs=len(objects), build_dir=build_dir)
552
return objects
553
554
def customize_build_cmd(self, cmd, _filename):
555
"""Allows libraries to customize the build command used on per-file basis.
556
557
For example, libc uses this to replace -Oz with -O2 for some subset of files."""
558
return cmd
559
560
def do_build(self, out_filename, generate_only=False):
561
"""Builds the library and returns the path to the file."""
562
assert out_filename == self.get_path(absolute=True)
563
build_dir = os.path.join(get_build_dir(), self.get_base_name())
564
if USE_NINJA:
565
self.generate_ninja(build_dir, out_filename)
566
if not generate_only:
567
run_ninja(build_dir)
568
else:
569
# Use a separate build directory to the ninja flavor so that building without
570
# EMCC_USE_NINJA doesn't clobber the ninja build tree
571
build_dir += '-tmp'
572
utils.safe_ensure_dirs(build_dir)
573
create_lib(out_filename, self.build_objects(build_dir))
574
if not shared.DEBUG:
575
utils.delete_dir(build_dir)
576
577
def do_generate(self, out_filename):
578
self.do_build(out_filename, generate_only=True)
579
580
@classmethod
581
def _inherit_list(cls, attr):
582
# Some properties, like cflags and includes, makes more sense to inherit
583
# via concatenation than replacement.
584
result = []
585
for item in cls.__mro__[::-1]:
586
# Using __dict__ to avoid inheritance
587
result += item.__dict__.get(attr, [])
588
return result
589
590
def get_cflags(self):
591
"""
592
Returns the list of flags to pass to emcc when building this variation
593
of the library.
594
595
Override and add any flags as needed to handle new variations.
596
"""
597
cflags = self._inherit_list('cflags')
598
cflags += get_base_cflags(self.build_dir, force_object_files=self.force_object_files)
599
600
if self.includes:
601
cflags += ['-I' + utils.path_from_root(i) for i in self._inherit_list('includes')]
602
return cflags
603
604
def get_base_name_prefix(self):
605
"""
606
Returns the base name of the library without any suffixes.
607
"""
608
return self.name
609
610
def get_base_name(self):
611
"""
612
Returns the base name of the library file.
613
614
This will include suffixes such as -mt, but will not include a file extension.
615
"""
616
return self.get_base_name_prefix()
617
618
def get_ext(self):
619
"""
620
Return the appropriate file extension for this library.
621
"""
622
return '.a'
623
624
def get_filename(self):
625
"""
626
Return the full name of the library file, including the file extension.
627
"""
628
return self.get_base_name() + self.get_ext()
629
630
@classmethod
631
def vary_on(cls):
632
"""
633
Returns a list of strings that are the names of boolean constructor
634
arguments that defines the variations of this library.
635
636
This is used by the default implementation of `cls.variations()` to generate
637
every possible combination of boolean values to pass to these arguments.
638
"""
639
return []
640
641
@classmethod
642
def variations(cls):
643
"""
644
Returns a list of keyword arguments to pass to the constructor to create
645
every possible variation of this library.
646
647
By default, this is every possible combination of boolean values to pass
648
to the list of arguments returned by `vary_on`, but you can override
649
the behaviour.
650
"""
651
vary_on = cls.vary_on()
652
return [dict(zip(vary_on, toggles, strict=True)) for toggles in
653
itertools.product([False, True], repeat=len(vary_on))]
654
655
@classmethod
656
def get_default_variation(cls, **kwargs):
657
"""
658
Construct the variation suitable for the current invocation of emscripten.
659
660
Subclasses should pass the keyword arguments they introduce to the
661
superclass version, and propagate **kwargs. The base class collects
662
all the keyword arguments and creates the instance.
663
"""
664
return cls(**kwargs)
665
666
@classmethod
667
def get_inheritance_tree(cls):
668
"""Returns all the classes in the inheritance tree of the current class."""
669
yield cls
670
for subclass in cls.__subclasses__():
671
yield from subclass.get_inheritance_tree()
672
673
@classmethod
674
def get_all_variations(cls):
675
"""
676
Gets all the variations of libraries in the inheritance tree of the current
677
library.
678
679
Calling Library.get_all_variations() returns the variations of ALL libraries
680
that can be built as a dictionary of variation names to Library objects.
681
"""
682
result = {}
683
for library in cls.get_inheritance_tree():
684
if library.name:
685
for flags in library.variations():
686
variation = library(**flags)
687
if variation.can_build():
688
result[variation.get_base_name()] = variation
689
return result
690
691
@classmethod
692
def get_usable_variations(cls):
693
"""
694
Gets all libraries suitable for the current invocation of emscripten.
695
696
This returns a dictionary of simple names to Library objects.
697
"""
698
if not hasattr(cls, 'usable_variations'):
699
cls.usable_variations = {}
700
for subclass in cls.get_inheritance_tree():
701
if subclass.name:
702
library = subclass.get_default_variation()
703
if library.can_build() and library.can_use():
704
cls.usable_variations[subclass.name] = library
705
return cls.usable_variations
706
707
708
class MTLibrary(Library):
709
def __init__(self, **kwargs):
710
self.is_mt = kwargs.pop('is_mt')
711
self.is_ww = kwargs.pop('is_ww') and not self.is_mt
712
super().__init__(**kwargs)
713
714
def get_cflags(self):
715
cflags = super().get_cflags()
716
if self.is_mt:
717
cflags += ['-pthread', '-sWASM_WORKERS']
718
if self.is_ww:
719
cflags += ['-sWASM_WORKERS']
720
return cflags
721
722
def get_base_name(self):
723
name = super().get_base_name()
724
if self.is_mt:
725
name += '-mt'
726
if self.is_ww:
727
name += '-ww'
728
return name
729
730
@classmethod
731
def vary_on(cls):
732
return super().vary_on() + ['is_mt', 'is_ww']
733
734
@classmethod
735
def get_default_variation(cls, **kwargs):
736
return super().get_default_variation(
737
is_mt=settings.PTHREADS,
738
is_ww=settings.SHARED_MEMORY and not settings.PTHREADS,
739
**kwargs,
740
)
741
742
@classmethod
743
def variations(cls):
744
combos = super().variations()
745
746
# These are mutually exclusive, only one flag will be set at any give time.
747
return [combo for combo in combos if not combo['is_mt'] or not combo['is_ww']]
748
749
750
class DebugLibrary(Library):
751
def __init__(self, **kwargs):
752
self.is_debug = kwargs.pop('is_debug')
753
super().__init__(**kwargs)
754
755
def get_cflags(self):
756
cflags = super().get_cflags()
757
if not self.is_debug:
758
cflags += ['-DNDEBUG']
759
return cflags
760
761
def get_base_name(self):
762
name = super().get_base_name()
763
if self.is_debug:
764
name += '-debug'
765
return name
766
767
@classmethod
768
def vary_on(cls):
769
return super().vary_on() + ['is_debug']
770
771
@classmethod
772
def get_default_variation(cls, **kwargs):
773
return super().get_default_variation(is_debug=settings.ASSERTIONS, **kwargs)
774
775
776
class Exceptions(IntEnum):
777
"""
778
This represents exception handling mode of Emscripten. Currently there are
779
three modes of exception handling:
780
- NONE: Does not handle exceptions. This includes -fno-exceptions, which
781
prevents both throwing and catching, and -fignore-exceptions, which only
782
allows throwing, but library-wise they use the same version.
783
- EMSCRIPTEN: Emscripten provides exception handling capability using JS
784
emulation. This causes code size increase and performance degradation.
785
- WASM_LEGACY: Wasm native exception handling support (legacy)
786
- WASM: Wasm native exception handling support
787
"""
788
NONE = auto()
789
EMSCRIPTEN = auto()
790
WASM_LEGACY = auto()
791
WASM = auto()
792
793
794
class ExceptionLibrary(Library):
795
def __init__(self, **kwargs):
796
self.eh_mode = kwargs.pop('eh_mode')
797
super().__init__(**kwargs)
798
799
def get_cflags(self):
800
cflags = super().get_cflags()
801
match self.eh_mode:
802
case Exceptions.NONE:
803
cflags += ['-fno-exceptions']
804
case Exceptions.EMSCRIPTEN:
805
cflags += ['-sDISABLE_EXCEPTION_CATCHING=0']
806
case Exceptions.WASM_LEGACY:
807
cflags += ['-fwasm-exceptions', '-sWASM_LEGACY_EXCEPTIONS']
808
case Exceptions.WASM:
809
cflags += ['-fwasm-exceptions', '-sWASM_LEGACY_EXCEPTIONS=0']
810
811
return cflags
812
813
def get_base_name(self):
814
name = super().get_base_name()
815
# TODO Currently emscripten-based exception is the default mode, thus no
816
# suffixes. Change the default to wasm exception later.
817
match self.eh_mode:
818
case Exceptions.NONE:
819
name += '-noexcept'
820
case Exceptions.WASM_LEGACY:
821
name += '-legacyexcept'
822
case Exceptions.WASM:
823
name += '-wasmexcept'
824
return name
825
826
@classmethod
827
def variations(cls):
828
combos = super().variations()
829
return ([dict(eh_mode=Exceptions.NONE, **combo) for combo in combos] +
830
[dict(eh_mode=Exceptions.EMSCRIPTEN, **combo) for combo in combos] +
831
[dict(eh_mode=Exceptions.WASM_LEGACY, **combo) for combo in combos] +
832
[dict(eh_mode=Exceptions.WASM, **combo) for combo in combos])
833
834
@classmethod
835
def get_default_variation(cls, **kwargs):
836
if settings.WASM_EXCEPTIONS:
837
if settings.WASM_LEGACY_EXCEPTIONS:
838
eh_mode = Exceptions.WASM_LEGACY
839
else:
840
eh_mode = Exceptions.WASM
841
elif settings.DISABLE_EXCEPTION_CATCHING == 1:
842
eh_mode = Exceptions.NONE
843
else:
844
eh_mode = Exceptions.EMSCRIPTEN
845
return super().get_default_variation(eh_mode=eh_mode, **kwargs)
846
847
848
class SjLjLibrary(Library):
849
def __init__(self, **kwargs):
850
# Which EH method we use for SjLj support
851
self.eh_mode = kwargs.pop('eh_mode')
852
super().__init__(**kwargs)
853
854
def get_cflags(self):
855
cflags = super().get_cflags()
856
match self.eh_mode:
857
case Exceptions.EMSCRIPTEN:
858
cflags += ['-sSUPPORT_LONGJMP=emscripten',
859
'-sDISABLE_EXCEPTION_THROWING=0']
860
case Exceptions.WASM_LEGACY:
861
cflags += ['-sSUPPORT_LONGJMP=wasm',
862
'-sWASM_LEGACY_EXCEPTIONS',
863
'-sDISABLE_EXCEPTION_THROWING',
864
'-D__WASM_SJLJ__']
865
case Exceptions.WASM:
866
cflags += ['-sSUPPORT_LONGJMP=wasm',
867
'-sWASM_LEGACY_EXCEPTIONS=0',
868
'-sDISABLE_EXCEPTION_THROWING',
869
'-D__WASM_SJLJ__']
870
return cflags
871
872
def get_base_name(self):
873
name = super().get_base_name()
874
# TODO Currently emscripten-based SjLj is the default mode, thus no
875
# suffixes. Change the default to wasm exception later.
876
match self.eh_mode:
877
case Exceptions.WASM_LEGACY:
878
name += '-legacysjlj'
879
case Exceptions.WASM:
880
name += '-wasmsjlj'
881
return name
882
883
@classmethod
884
def variations(cls):
885
combos = super().variations()
886
return ([dict(eh_mode=Exceptions.EMSCRIPTEN, **combo) for combo in combos] +
887
[dict(eh_mode=Exceptions.WASM_LEGACY, **combo) for combo in combos] +
888
[dict(eh_mode=Exceptions.WASM, **combo) for combo in combos])
889
890
@classmethod
891
def get_default_variation(cls, **kwargs):
892
if settings.SUPPORT_LONGJMP == 'wasm':
893
if settings.WASM_LEGACY_EXCEPTIONS:
894
eh_mode = Exceptions.WASM_LEGACY
895
else:
896
eh_mode = Exceptions.WASM
897
else:
898
eh_mode = Exceptions.EMSCRIPTEN
899
return super().get_default_variation(eh_mode=eh_mode, **kwargs)
900
901
902
class MuslInternalLibrary(Library):
903
includes = [
904
'system/lib/libc/musl/src/internal',
905
'system/lib/libc/musl/src/include',
906
'system/lib/libc/musl/include',
907
'system/lib/libc',
908
'system/lib/pthread',
909
]
910
911
cflags = [
912
'-std=c99',
913
'-D_XOPEN_SOURCE=700',
914
'-Wno-unused-result', # system call results are often ignored in musl, and in wasi that warns
915
]
916
917
918
class AsanInstrumentedLibrary(Library):
919
def __init__(self, **kwargs):
920
self.is_asan = kwargs.pop('is_asan', False)
921
super().__init__(**kwargs)
922
923
def get_cflags(self):
924
cflags = super().get_cflags()
925
if self.is_asan:
926
cflags += ['-fsanitize=address']
927
return cflags
928
929
def get_base_name(self):
930
name = super().get_base_name()
931
if self.is_asan:
932
name += '-asan'
933
return name
934
935
@classmethod
936
def vary_on(cls):
937
return super().vary_on() + ['is_asan']
938
939
@classmethod
940
def get_default_variation(cls, **kwargs):
941
return super().get_default_variation(is_asan=settings.USE_ASAN, **kwargs)
942
943
944
# Subclass of SjLjLibrary because emscripten_setjmp.c uses SjLj support
945
class libcompiler_rt(MTLibrary, SjLjLibrary):
946
name = 'libcompiler_rt'
947
# compiler_rt files can't currently be part of LTO although we are hoping to remove this
948
# restriction soon: https://reviews.llvm.org/D71738
949
force_object_files = True
950
951
cflags = ['-fno-builtin', '-DNDEBUG', '-DCOMPILER_RT_HAS_ATOMICS=1']
952
src_dir = 'system/lib/compiler-rt/lib/builtins'
953
profile_src_dir = 'system/lib/compiler-rt/lib/profile'
954
includes = ['system/lib/libc', 'system/lib/compiler-rt/include']
955
excludes = [
956
# gcc_personality_v0.c depends on libunwind, which don't include by default.
957
'gcc_personality_v0.c',
958
# bfloat16
959
'extendbfsf2.c',
960
'truncdfbf2.c',
961
'truncxfbf2.c',
962
'truncsfbf2.c',
963
'trunctfbf2.c',
964
# We provide our own crt
965
'crtbegin.c',
966
'crtend.c',
967
# 80-bit long double (xf_float)
968
'divxc3.c',
969
'extendhfxf2.c',
970
'extendxftf2.c',
971
'fixxfdi.c',
972
'fixxfti.c',
973
'fixunsxfdi.c',
974
'fixunsxfsi.c',
975
'fixunsxfti.c',
976
'floatdixf.c',
977
'floattixf.c',
978
'floatundixf.c',
979
'floatuntixf.c',
980
'mulxc3.c',
981
'powixf2.c',
982
'trunctfxf2.c',
983
'truncxfhf2.c',
984
]
985
src_files = glob_in_path(src_dir, '*.c', excludes=excludes)
986
src_files += glob_in_path(profile_src_dir, '*.c')
987
src_files += glob_in_path(profile_src_dir, '*.cpp')
988
src_files += files_in_path(
989
path='system/lib/compiler-rt',
990
filenames=[
991
'stack_ops.S',
992
'stack_limits.S',
993
'emscripten_setjmp.c',
994
'emscripten_exception_builtins.c',
995
'emscripten_tempret.s',
996
'__c_longjmp.S',
997
'__trap.c',
998
])
999
1000
1001
class libnoexit(Library):
1002
name = 'libnoexit'
1003
src_dir = 'system/lib/libc'
1004
src_files = ['atexit_dummy.c']
1005
1006
1007
class llvmlibc(DebugLibrary, AsanInstrumentedLibrary, MTLibrary):
1008
name = 'libllvmlibc'
1009
never_force = True
1010
includes = ['system/lib/llvm-libc']
1011
cflags = [
1012
'-Os',
1013
'-DLIBC_NAMESPACE=__llvm_libc',
1014
'-DLLVM_LIBC',
1015
'-DLIBC_COPT_PUBLIC_PACKAGING',
1016
# Disable accurate pass to speed up certain math operations
1017
'-DLIBC_MATH=LIBC_MATH_FAST',
1018
'-D__LIBC_USE_BUILTIN_CEIL_FLOOR_RINT_TRUNC',
1019
# Reduce size bloats from string conversions.
1020
'-DLIBC_COPT_STRTOFLOAT_DISABLE_EISEL_LEMIRE',
1021
# To Enable FMA, we need to set the following flags. But we can't really ship this in a default libc build.
1022
# Once llvm-libc gets used, we might need to have a FMA-enalbed flavor to enable these following flags.
1023
'-Wno-unused-variable',
1024
'-mrelaxed-simd',
1025
'-ffp-contract=fast',
1026
]
1027
1028
def get_files(self):
1029
files = glob_in_path('system/lib/llvm-libc/src/assert', '*.cpp')
1030
files += glob_in_path('system/lib/llvm-libc/src/complex', '**/*.cpp')
1031
files += glob_in_path('system/lib/llvm-libc/src/string', '**/*.cpp', excludes=['memset.cpp', 'memcpy.cpp'] if self.is_asan else [])
1032
files += glob_in_path('system/lib/llvm-libc/src/intypes', '*.cpp')
1033
files += glob_in_path('system/lib/llvm-libc/src/strings', '**/*.cpp')
1034
files += glob_in_path('system/lib/llvm-libc/src/errno', '**/*.cpp')
1035
files += glob_in_path('system/lib/llvm-libc/src/math', '*.cpp')
1036
# Overlay mode doesn't support mbstate_t which is used by these wchar sources.
1037
files += glob_in_path('system/lib/llvm-libc/src/wchar', '*.cpp', excludes=['wcrtomb.cpp', 'mbrtowc.cpp', 'wctomb.cpp', 'mbtowc.cpp'])
1038
files += glob_in_path('system/lib/llvm-libc/src/setjmp', '*.cpp')
1039
files += glob_in_path('system/lib/llvm-libc/src/setjmp', '**/*.cpp')
1040
files += glob_in_path('system/lib/llvm-libc/src/stdlib', '*.cpp', excludes=['at_quick_exit.cpp',
1041
'quick_exit.cpp',
1042
'atexit.cpp',
1043
'exit.cpp',
1044
'_Exit.cpp',
1045
'getenv.cpp'])
1046
files += glob_in_path('system/lib/llvm-libc/src/math/generic', '**/*.cpp', excludes=['atan2l.cpp', 'exp_utils.cpp'])
1047
files += glob_in_path('system/lib/llvm-libc/src/__support/StringUtil', '**/*.cpp')
1048
return files
1049
1050
1051
class libc(MuslInternalLibrary,
1052
DebugLibrary,
1053
AsanInstrumentedLibrary,
1054
MTLibrary):
1055
name = 'libc'
1056
1057
# Without -fno-builtin, LLVM can optimize away or convert calls to library
1058
# functions to something else based on assumptions that they behave exactly
1059
# like the standard library. This can cause unexpected bugs when we use our
1060
# custom standard library. The same for other libc/libm builds.
1061
cflags = ['-Os', '-fno-inline-functions', '-fno-builtin']
1062
1063
# Disable certain warnings for code patterns that are contained in upstream musl
1064
cflags += ['-Wno-ignored-attributes',
1065
# tre.h defines NDEBUG internally itself
1066
'-Wno-macro-redefined',
1067
'-Wno-shift-op-parentheses',
1068
'-Wno-string-plus-int',
1069
'-Wno-missing-braces',
1070
'-Wno-logical-op-parentheses',
1071
'-Wno-bitwise-op-parentheses',
1072
'-Wno-unused-but-set-variable',
1073
'-Wno-unused-variable',
1074
'-Wno-unused-label',
1075
'-Wno-pointer-sign']
1076
1077
def __init__(self, **kwargs):
1078
self.non_lto_files = self.get_libcall_files()
1079
super().__init__(**kwargs)
1080
1081
def get_libcall_files(self):
1082
# Combining static linking with LTO is tricky under LLVM. The codegen that
1083
# happens during LTO can generate references to new symbols that didn't exist
1084
# in the linker inputs themselves.
1085
# These symbols are called libcalls in LLVM and are the result of intrinsics
1086
# and builtins at the LLVM level. These libcalls cannot themselves be part
1087
# of LTO because once the linker is running the LTO phase new bitcode objects
1088
# cannot be added to link. Another way of putting it: by the time LTO happens
1089
# the decision about which bitcode symbols to compile has already been made.
1090
# See: https://bugs.llvm.org/show_bug.cgi?id=44353.
1091
# To solve this we force certain parts of libc to never be compiled as LTO/bitcode.
1092
# Note that this also includes things that may be depended on by those
1093
# functions - fmin uses signbit, for example, so signbit must be here (so if
1094
# fmin is added by codegen, it will have all it needs).
1095
math_files = [
1096
'fmin.c', 'fminf.c', 'fminl.c',
1097
'fmax.c', 'fmaxf.c', 'fmaxl.c',
1098
'fmod.c', 'fmodf.c', 'fmodl.c',
1099
'logf.c', 'logf_data.c',
1100
'log2f.c', 'log2f_data.c',
1101
'log10.c', 'log10f.c',
1102
'exp.c', 'exp_data.c',
1103
'exp2.c',
1104
'exp2f.c', 'exp2f_data.c',
1105
'exp10.c', 'exp10f.c',
1106
'ldexp.c', 'ldexpf.c', 'ldexpl.c',
1107
'scalbn.c', '__fpclassifyl.c',
1108
'__signbitl.c', '__signbitf.c', '__signbit.c',
1109
'__math_divzero.c', '__math_divzerof.c',
1110
'__math_oflow.c', '__math_oflowf.c',
1111
'__math_uflow.c', '__math_uflowf.c',
1112
'__math_invalid.c', '__math_invalidf.c', '__math_invalidl.c',
1113
'pow.c', 'pow_data.c', 'log.c', 'log_data.c', 'log2.c', 'log2_data.c',
1114
'scalbnf.c',
1115
]
1116
math_files = files_in_path(path='system/lib/libc/musl/src/math', filenames=math_files)
1117
1118
exit_files = files_in_path(
1119
path='system/lib/libc/musl/src/exit',
1120
filenames=['atexit.c'])
1121
1122
other_files = files_in_path(
1123
path='system/lib/libc',
1124
filenames=['emscripten_memcpy.c', 'emscripten_memset.c',
1125
'emscripten_memcpy_bulkmem.S', 'emscripten_memset_bulkmem.S',
1126
'emscripten_scan_stack.c',
1127
'emscripten_get_heap_size.c', # needed by malloc
1128
'emscripten_memmove.c'])
1129
# Calls to iprintf can be generated during codegen. Ideally we wouldn't
1130
# compile these with -O2 like we do the rest of compiler-rt since its
1131
# probably not performance sensitive. However we don't currently have
1132
# a way to set per-file compiler flags. And hopefully we should be able
1133
# move all this stuff back into libc once we it LTO compatible.
1134
iprintf_files = files_in_path(
1135
path='system/lib/libc/musl/src/stdio',
1136
filenames=['__towrite.c', '__overflow.c', 'fwrite.c', 'fputs.c',
1137
'getc.c',
1138
'fputc.c',
1139
'fgets.c',
1140
'putc.c', 'putc_unlocked.c',
1141
'putchar.c', 'putchar_unlocked.c',
1142
'printf.c', 'puts.c', '__lockfile.c'])
1143
iprintf_files += files_in_path(
1144
path='system/lib/libc/musl/src/string',
1145
filenames=['strlen.c'])
1146
1147
# Transitively required by many system call imports
1148
errno_files = files_in_path(
1149
path='system/lib/libc/musl/src/errno',
1150
filenames=['__errno_location.c', 'strerror.c'])
1151
1152
return math_files + exit_files + other_files + iprintf_files + errno_files
1153
1154
def get_files(self):
1155
libc_files = []
1156
musl_srcdir = utils.path_from_root('system/lib/libc/musl/src')
1157
1158
# musl modules
1159
ignore = [
1160
'ipc', 'passwd', 'signal', 'sched', 'time', 'linux',
1161
'aio', 'legacy', 'mq', 'setjmp',
1162
'ldso', 'malloc',
1163
]
1164
1165
# individual files
1166
ignore += [
1167
'memcpy.c', 'memset.c', 'memmove.c', 'getaddrinfo.c', 'getnameinfo.c',
1168
'res_query.c', 'res_querydomain.c',
1169
'proto.c',
1170
'syscall.c', 'popen.c', 'pclose.c',
1171
'getgrouplist.c', 'initgroups.c', 'wordexp.c', 'timer_create.c',
1172
'getauxval.c',
1173
'lookup_name.c',
1174
# 'process' exclusions
1175
'fork.c', 'vfork.c', 'posix_spawn.c', 'posix_spawnp.c', 'execve.c', 'waitid.c', 'system.c',
1176
'_Fork.c',
1177
# 'env' exclusions
1178
'__reset_tls.c', '__init_tls.c', '__libc_start_main.c',
1179
# 'exit' exclusions
1180
'assert.c', 'exit.c',
1181
]
1182
1183
ignore += LIBC_SOCKETS
1184
1185
if self.is_mt:
1186
ignore += [
1187
'clone.c',
1188
'pthread_create.c',
1189
'pthread_kill.c', 'pthread_sigmask.c',
1190
'__set_thread_area.c', 'synccall.c',
1191
'__syscall_cp.c', '__tls_get_addr.c',
1192
'__unmapself.c',
1193
# Empty files, simply ignore them.
1194
'syscall_cp.c', 'tls.c',
1195
# TODO: Support these. See #12216.
1196
'pthread_setname_np.c',
1197
'pthread_getname_np.c',
1198
]
1199
libc_files += files_in_path(
1200
path='system/lib/pthread',
1201
filenames=[
1202
'library_pthread.c',
1203
'em_task_queue.c',
1204
'proxying.c',
1205
'proxying_legacy.c',
1206
'thread_mailbox.c',
1207
'pthread_create.c',
1208
'pthread_kill.c',
1209
'emscripten_thread_init.c',
1210
'emscripten_thread_state.S',
1211
'emscripten_futex_wait.c',
1212
'emscripten_futex_wake.c',
1213
'emscripten_yield.c',
1214
])
1215
else:
1216
ignore += ['thread']
1217
libc_files += files_in_path(
1218
path='system/lib/libc/musl/src/thread',
1219
filenames=[
1220
'pthread_self.c',
1221
'pthread_cleanup_push.c',
1222
'pthread_attr_init.c',
1223
'pthread_attr_destroy.c',
1224
'pthread_attr_get.c',
1225
'pthread_attr_setdetachstate.c',
1226
'pthread_attr_setguardsize.c',
1227
'pthread_attr_setinheritsched.c',
1228
'pthread_attr_setschedparam.c',
1229
'pthread_attr_setschedpolicy.c',
1230
'pthread_attr_setscope.c',
1231
'pthread_attr_setstack.c',
1232
'pthread_attr_setstacksize.c',
1233
'pthread_getattr_np.c',
1234
'pthread_getconcurrency.c',
1235
'pthread_getcpuclockid.c',
1236
'pthread_getschedparam.c',
1237
'pthread_setschedprio.c',
1238
'pthread_setconcurrency.c',
1239
'default_attr.c',
1240
# C11 thread library functions
1241
'call_once.c',
1242
'tss_create.c',
1243
'tss_delete.c',
1244
'tss_set.c',
1245
'cnd_broadcast.c',
1246
'cnd_destroy.c',
1247
'cnd_init.c',
1248
'cnd_signal.c',
1249
'cnd_timedwait.c',
1250
'cnd_wait.c',
1251
'mtx_destroy.c',
1252
'mtx_init.c',
1253
'mtx_lock.c',
1254
'mtx_timedlock.c',
1255
'mtx_trylock.c',
1256
'mtx_unlock.c',
1257
'thrd_create.c',
1258
'thrd_exit.c',
1259
'thrd_join.c',
1260
'thrd_sleep.c',
1261
'thrd_yield.c',
1262
])
1263
libc_files += files_in_path(
1264
path='system/lib/pthread',
1265
filenames=[
1266
'library_pthread_stub.c',
1267
'pthread_self_stub.c',
1268
'proxying_stub.c',
1269
])
1270
1271
# These files are in libc directories, but only built in libc_optz.
1272
ignore += ['pow_small.c', 'log_small.c', 'log2_small.c']
1273
1274
ignore = set(ignore)
1275
for dirpath, dirnames, filenames in os.walk(musl_srcdir):
1276
# Don't recurse into ignored directories
1277
remove = [d for d in dirnames if d in ignore]
1278
for r in remove:
1279
dirnames.remove(r)
1280
1281
for f in filenames:
1282
if f.endswith('.c') and f not in ignore:
1283
libc_files.append(os.path.join(musl_srcdir, dirpath, f))
1284
1285
# Allowed files from ignored modules
1286
libc_files += files_in_path(
1287
path='system/lib/libc/musl/src/time',
1288
filenames=[
1289
'clock_settime.c',
1290
'asctime_r.c',
1291
'asctime.c',
1292
'ctime.c',
1293
'difftime.c',
1294
'ftime.c',
1295
'gmtime.c',
1296
'localtime.c',
1297
'nanosleep.c',
1298
'clock.c',
1299
'clock_nanosleep.c',
1300
'clock_getres.c',
1301
'clock_gettime.c',
1302
'ctime_r.c',
1303
'timespec_get.c',
1304
'utime.c',
1305
'__map_file.c',
1306
'strftime.c',
1307
'__tz.c',
1308
'__tm_to_secs.c',
1309
'__year_to_secs.c',
1310
'__month_to_secs.c',
1311
'wcsftime.c',
1312
])
1313
1314
libc_files += files_in_path(
1315
path='system/lib/libc/musl/src/legacy',
1316
filenames=['getpagesize.c', 'err.c', 'euidaccess.c'])
1317
1318
libc_files += files_in_path(
1319
path='system/lib/libc/musl/src/linux',
1320
filenames=['getdents.c', 'gettid.c', 'utimes.c', 'statx.c', 'wait4.c', 'wait3.c'])
1321
1322
libc_files += files_in_path(
1323
path='system/lib/libc/musl/src/malloc',
1324
filenames=['reallocarray.c'])
1325
1326
libc_files += files_in_path(
1327
path='system/lib/libc/musl/src/sched',
1328
filenames=['sched_yield.c'])
1329
1330
libc_files += files_in_path(
1331
path='system/lib/libc/musl/src/ldso',
1332
filenames=['dladdr.c', 'dlerror.c', 'dlsym.c', 'dlclose.c'])
1333
1334
libc_files += files_in_path(
1335
path='system/lib/libc/musl/src/signal',
1336
filenames=[
1337
'block.c',
1338
'getitimer.c',
1339
'killpg.c',
1340
'setitimer.c',
1341
'sigorset.c',
1342
'sigandset.c',
1343
'sigaddset.c',
1344
'sigdelset.c',
1345
'sigemptyset.c',
1346
'sigisemptyset.c',
1347
'sigfillset.c',
1348
'sigismember.c',
1349
'siginterrupt.c',
1350
'signal.c',
1351
'sigprocmask.c',
1352
'sigrtmax.c',
1353
'sigrtmin.c',
1354
'sigwait.c',
1355
'sigwaitinfo.c',
1356
])
1357
1358
libc_files += files_in_path(
1359
path='system/lib/libc',
1360
filenames=[
1361
'emscripten_console.c',
1362
'emscripten_fiber.c',
1363
'emscripten_get_heap_size.c',
1364
'emscripten_memcpy.c',
1365
'emscripten_memmove.c',
1366
'emscripten_memset.c',
1367
'emscripten_memcpy_bulkmem.S',
1368
'emscripten_memset_bulkmem.S',
1369
'emscripten_mmap.c',
1370
'emscripten_scan_stack.c',
1371
'emscripten_time.c',
1372
'mktime.c',
1373
'kill.c',
1374
'lookup_name.c',
1375
'pthread_sigmask.c',
1376
'raise.c',
1377
'sigaction.c',
1378
'sigtimedwait.c',
1379
'wasi-helpers.c',
1380
'system.c',
1381
])
1382
1383
if settings.RELOCATABLE or settings.MAIN_MODULE:
1384
libc_files += files_in_path(path='system/lib/libc', filenames=['dynlink.c'])
1385
1386
libc_files += files_in_path(
1387
path='system/lib/pthread',
1388
filenames=['thread_profiler.c'])
1389
1390
libc_files += glob_in_path('system/lib/libc/compat', '*.c')
1391
1392
# Check for missing file in non_lto_files list. Do this here
1393
# rather than in the constructor so it only happens when the
1394
# library is actually built (not when its instantiated).
1395
for f in self.non_lto_files:
1396
assert os.path.exists(f), f
1397
1398
return libc_files
1399
1400
def customize_build_cmd(self, cmd, filename):
1401
if filename in self.non_lto_files:
1402
# These files act more like the part of compiler-rt in that
1403
# references to them can be generated at compile time.
1404
# Treat them like compiler-rt in as much as never compile
1405
# them as LTO and build them with -O2 rather then -Os (which
1406
# use used for the rest of libc) because this set of files
1407
# also contains performance sensitive math functions.
1408
cmd = [a for a in cmd if not a.startswith('-flto')]
1409
cmd = [a for a in cmd if not a.startswith('-O')]
1410
cmd += ['-O2']
1411
return cmd
1412
1413
1414
# Contains the files from libc that are optimized differently in -Oz mode, where
1415
# we want to aggressively optimize them for size. This is linked in before libc
1416
# so we can override those specific files, when in -Oz.
1417
class libc_optz(libc):
1418
name = 'libc_optz'
1419
1420
cflags = ['-Os', '-fno-inline-functions', '-fno-builtin', '-DEMSCRIPTEN_OPTIMIZE_FOR_OZ']
1421
1422
def __init__(self, **kwargs):
1423
super().__init__(**kwargs)
1424
self.non_lto_files = self.get_libcall_files()
1425
1426
def get_libcall_files(self):
1427
# see comments in libc.customize_build_cmd
1428
1429
# some files also appear in libc, and a #define affects them
1430
mem_files = files_in_path(
1431
path='system/lib/libc',
1432
filenames=['emscripten_memcpy.c', 'emscripten_memset.c',
1433
'emscripten_memmove.c'])
1434
1435
# some functions have separate files
1436
math_files = files_in_path(
1437
path='system/lib/libc/musl/src/math',
1438
filenames=['pow_small.c', 'log_small.c', 'log2_small.c'])
1439
1440
return mem_files + math_files
1441
1442
def get_files(self):
1443
libcall_files = self.get_libcall_files()
1444
1445
# some files also appear in libc, and a #define affects them
1446
mem_files = files_in_path(
1447
path='system/lib/libc/musl/src/string',
1448
filenames=['memcmp.c'])
1449
1450
return libcall_files + mem_files
1451
1452
def customize_build_cmd(self, cmd, filename):
1453
if filename in self.non_lto_files:
1454
# see comments in libc.customize_build_cmd
1455
cmd = [a for a in cmd if not a.startswith('-flto')]
1456
cmd = [a for a in cmd if not a.startswith('-O')]
1457
cmd += ['-O2']
1458
return cmd
1459
1460
def can_use(self):
1461
# Because libc_optz overrides parts of libc, it is not compatible with
1462
# dynamic linking which uses --whole-archive. In addition,
1463
# EMCC_FORCE_STDLIBS can have a similar effect of forcing all libraries.
1464
# In both cases, the build is not one that is hyper-focused on code size,
1465
# and so optz is not that important.
1466
return super().can_use() and settings.SHRINK_LEVEL >= 2 and \
1467
not settings.LINKABLE and not os.environ.get('EMCC_FORCE_STDLIBS')
1468
1469
1470
class libprintf_long_double(libc):
1471
name = 'libprintf_long_double'
1472
cflags = ['-DEMSCRIPTEN_PRINTF_LONG_DOUBLE']
1473
1474
def get_files(self):
1475
return files_in_path(
1476
path='system/lib/libc/musl/src/stdio',
1477
filenames=['vfprintf.c'])
1478
1479
def can_use(self):
1480
return super().can_use() and settings.PRINTF_LONG_DOUBLE
1481
1482
1483
class libwasm_workers(DebugLibrary):
1484
name = 'libwasm_workers'
1485
includes = ['system/lib/libc']
1486
1487
def __init__(self, **kwargs):
1488
self.is_stub = kwargs.pop('stub')
1489
super().__init__(**kwargs)
1490
1491
def get_cflags(self):
1492
cflags = super().get_cflags()
1493
if self.is_debug:
1494
cflags += ['-D_DEBUG']
1495
# library_wasm_worker.c contains an assert that a nonnull parameter
1496
# is no NULL, which llvm now warns is redundant/tautological.
1497
cflags += ['-Wno-tautological-pointer-compare']
1498
# Override the `-O2` default. Building library_wasm_worker.c with
1499
# `-O1` or `-O2` currently causes tests to fail.
1500
# https://github.com/emscripten-core/emscripten/issues/19331
1501
cflags += ['-O0']
1502
else:
1503
cflags += ['-DNDEBUG', '-Oz']
1504
if settings.MAIN_MODULE:
1505
cflags += ['-fPIC']
1506
if not self.is_stub:
1507
cflags += ['-sWASM_WORKERS']
1508
return cflags
1509
1510
def get_base_name(self):
1511
name = super().get_base_name()
1512
if self.is_stub:
1513
name += '-stub'
1514
return name
1515
1516
@classmethod
1517
def vary_on(cls):
1518
return super().vary_on() + ['stub']
1519
1520
@classmethod
1521
def get_default_variation(cls, **kwargs):
1522
return super().get_default_variation(stub=not settings.WASM_WORKERS, **kwargs)
1523
1524
def get_files(self):
1525
files = []
1526
if self.is_stub:
1527
files = [
1528
'library_wasm_worker_stub.c',
1529
]
1530
else:
1531
files = [
1532
'library_wasm_worker.c',
1533
'wasm_worker_initialize.S',
1534
]
1535
return files_in_path(
1536
path='system/lib/wasm_worker',
1537
filenames=files)
1538
1539
def can_use(self):
1540
# see src/library_wasm_worker.js
1541
return super().can_use() and not settings.SINGLE_FILE and not settings.RELOCATABLE
1542
1543
1544
class libsockets(MuslInternalLibrary, MTLibrary):
1545
name = 'libsockets'
1546
1547
cflags = ['-Os', '-fno-builtin', '-Wno-shift-op-parentheses']
1548
1549
def get_files(self):
1550
return files_in_path(
1551
path='system/lib/libc/musl/src/network',
1552
filenames=LIBC_SOCKETS)
1553
1554
def can_use(self):
1555
return super().can_use() and not settings.PROXY_POSIX_SOCKETS
1556
1557
1558
class libsockets_proxy(MTLibrary):
1559
name = 'libsockets_proxy'
1560
1561
cflags = ['-Os']
1562
1563
def get_files(self):
1564
return [utils.path_from_root('system/lib/websocket/websocket_to_posix_socket.c')]
1565
1566
def can_use(self):
1567
return super().can_use() and settings.PROXY_POSIX_SOCKETS
1568
1569
1570
class crt1(MuslInternalLibrary):
1571
name = 'crt1'
1572
src_dir = 'system/lib/libc'
1573
src_files = ['crt1.c']
1574
1575
force_object_files = True
1576
1577
def get_ext(self):
1578
return '.o'
1579
1580
def can_use(self):
1581
return super().can_use() and settings.STANDALONE_WASM
1582
1583
1584
class crt1_reactor(MuslInternalLibrary):
1585
name = 'crt1_reactor'
1586
src_dir = 'system/lib/libc'
1587
src_files = ['crt1_reactor.c']
1588
1589
force_object_files = True
1590
1591
def get_ext(self):
1592
return '.o'
1593
1594
def can_use(self):
1595
return super().can_use() and settings.STANDALONE_WASM
1596
1597
1598
class crt1_proxy_main(MuslInternalLibrary):
1599
name = 'crt1_proxy_main'
1600
src_dir = 'system/lib/libc'
1601
src_files = ['crt1_proxy_main.c']
1602
1603
force_object_files = True
1604
1605
def get_ext(self):
1606
return '.o'
1607
1608
def can_use(self):
1609
return super().can_use() and settings.PROXY_TO_PTHREAD
1610
1611
1612
class crtbegin(MuslInternalLibrary):
1613
name = 'crtbegin'
1614
cflags = ['-pthread']
1615
src_dir = 'system/lib/pthread'
1616
src_files = ['emscripten_tls_init.c']
1617
1618
force_object_files = True
1619
1620
def get_ext(self):
1621
return '.o'
1622
1623
def can_use(self):
1624
return super().can_use() and settings.SHARED_MEMORY
1625
1626
1627
class libcxxabi(ExceptionLibrary, MTLibrary, DebugLibrary):
1628
name = 'libc++abi'
1629
cflags = [
1630
'-Oz',
1631
'-D_LIBCPP_BUILDING_LIBRARY',
1632
'-D_LIBCXXABI_BUILDING_LIBRARY',
1633
'-DLIBCXXABI_NON_DEMANGLING_TERMINATE',
1634
'-std=c++23',
1635
]
1636
includes = ['system/lib/libcxx/src']
1637
1638
def __init__(self, **kwargs):
1639
super().__init__(**kwargs)
1640
# TODO EXCEPTION_STACK_TRACES currently requires the debug version of
1641
# libc++abi, causing the debug version of libc++abi to be linked, which
1642
# increases code size. libc++abi is not a big library to begin with, but if
1643
# this becomes a problem, consider making EXCEPTION_STACK_TRACES work with
1644
# the non-debug version of libc++abi.
1645
self.is_debug |= settings.EXCEPTION_STACK_TRACES
1646
1647
def get_cflags(self):
1648
cflags = super().get_cflags()
1649
if not self.is_mt and not self.is_ww:
1650
cflags.append('-D_LIBCXXABI_HAS_NO_THREADS')
1651
match self.eh_mode:
1652
case Exceptions.NONE:
1653
cflags.append('-D_LIBCXXABI_NO_EXCEPTIONS')
1654
case Exceptions.EMSCRIPTEN:
1655
cflags.append('-D__EMSCRIPTEN_EXCEPTIONS__')
1656
# The code used to interpret exceptions during terminate
1657
# is not compatible with emscripten exceptions.
1658
cflags.append('-DLIBCXXABI_SILENT_TERMINATE')
1659
return cflags
1660
1661
def get_files(self):
1662
filenames = [
1663
'abort_message.cpp',
1664
'cxa_aux_runtime.cpp',
1665
'cxa_default_handlers.cpp',
1666
'cxa_demangle.cpp',
1667
'cxa_guard.cpp',
1668
'cxa_handlers.cpp',
1669
'cxa_virtual.cpp',
1670
'cxa_thread_atexit.cpp',
1671
'fallback_malloc.cpp',
1672
'stdlib_new_delete.cpp',
1673
'stdlib_exception.cpp',
1674
'stdlib_stdexcept.cpp',
1675
'stdlib_typeinfo.cpp',
1676
'private_typeinfo.cpp',
1677
'cxa_exception_js_utils.cpp',
1678
'__cpp_exception.S',
1679
]
1680
match self.eh_mode:
1681
case Exceptions.NONE:
1682
filenames += ['cxa_noexception.cpp']
1683
case Exceptions.EMSCRIPTEN:
1684
filenames += ['cxa_exception_emscripten.cpp']
1685
case Exceptions.WASM_LEGACY | Exceptions.WASM:
1686
filenames += [
1687
'cxa_exception_storage.cpp',
1688
'cxa_exception.cpp',
1689
'cxa_personality.cpp',
1690
]
1691
case _:
1692
assert False
1693
1694
return files_in_path(
1695
path='system/lib/libcxxabi/src',
1696
filenames=filenames)
1697
1698
1699
class libcxx(ExceptionLibrary, MTLibrary, DebugLibrary):
1700
name = 'libc++'
1701
1702
cflags = [
1703
'-Oz',
1704
'-DLIBCXX_BUILDING_LIBCXXABI=1',
1705
'-D_LIBCPP_BUILDING_LIBRARY',
1706
'-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS',
1707
# TODO(sbc): clang recently introduced this new warning which is triggered
1708
# by `filesystem/directory_iterator.cpp`: https://reviews.llvm.org/D119670
1709
'-Wno-unqualified-std-cast-call',
1710
'-Wno-unknown-warning-option',
1711
'-std=c++23',
1712
'-DLIBC_NAMESPACE=__llvm_libc',
1713
]
1714
1715
includes = ['system/lib/libcxx/src', 'system/lib/llvm-libc']
1716
1717
src_dir = 'system/lib/libcxx/src'
1718
src_glob = '**/*.cpp'
1719
src_glob_exclude = [
1720
'xlocale_zos.cpp',
1721
'mbsnrtowcs.cpp',
1722
'wcsnrtombs.cpp',
1723
'int128_builtins.cpp',
1724
'libdispatch.cpp',
1725
# Win32-specific files
1726
'locale_win32.cpp',
1727
'thread_win32.cpp',
1728
'support.cpp',
1729
'compiler_rt_shims.cpp',
1730
# Emscripten does not have C++20's time zone support which requires access
1731
# to IANA Time Zone Database. TODO Implement this using JS timezone
1732
'time_zone.cpp',
1733
'tzdb.cpp',
1734
'tzdb_list.cpp',
1735
]
1736
1737
1738
class libunwind(ExceptionLibrary, MTLibrary):
1739
name = 'libunwind'
1740
# Because calls to _Unwind_CallPersonality are generated during LTO, libunwind
1741
# can't currently be part of LTO.
1742
# See https://bugs.llvm.org/show_bug.cgi?id=44353
1743
force_object_files = True
1744
1745
cflags = ['-Oz', '-D_LIBUNWIND_HIDE_SYMBOLS',
1746
# TODO Remove this once
1747
# https://github.com/llvm/llvm-project/pull/175776 lands
1748
'-Wno-c23-extensions']
1749
src_dir = 'system/lib/libunwind/src'
1750
# Without this we can't build libunwind since it will pickup the unwind.h
1751
# that is part of llvm (which is not compatible for some reason).
1752
includes = ['system/lib/libunwind/include']
1753
src_files = ['Unwind-wasm.c']
1754
1755
def __init__(self, **kwargs):
1756
super().__init__(**kwargs)
1757
1758
def can_use(self):
1759
return super().can_use() and self.eh_mode in (Exceptions.WASM_LEGACY, Exceptions.WASM)
1760
1761
def get_cflags(self):
1762
cflags = super().get_cflags()
1763
cflags.append('-DNDEBUG')
1764
if not self.is_mt and not self.is_ww:
1765
cflags.append('-D_LIBUNWIND_HAS_NO_THREADS')
1766
match self.eh_mode:
1767
case Exceptions.NONE:
1768
cflags.append('-D_LIBUNWIND_HAS_NO_EXCEPTIONS')
1769
case Exceptions.EMSCRIPTEN:
1770
cflags.append('-D__EMSCRIPTEN_EXCEPTIONS__')
1771
return cflags
1772
1773
1774
class libmalloc(MTLibrary):
1775
name = 'libmalloc'
1776
1777
cflags = ['-fno-builtin', '-Wno-unused-function', '-Wno-unused-but-set-variable', '-Wno-unused-variable']
1778
# malloc/free/calloc are runtime functions and can be generated during LTO
1779
# Therefore they cannot themselves be part of LTO.
1780
force_object_files = True
1781
1782
def __init__(self, **kwargs):
1783
self.malloc = kwargs.pop('malloc')
1784
if self.malloc not in ('dlmalloc', 'emmalloc', 'emmalloc-debug', 'emmalloc-memvalidate', 'emmalloc-verbose', 'emmalloc-memvalidate-verbose', 'mimalloc', 'none'):
1785
raise Exception('malloc must be one of "emmalloc[-debug|-memvalidate][-verbose]", "mimalloc", "dlmalloc" or "none", see settings.js')
1786
1787
self.is_tracing = kwargs.pop('is_tracing')
1788
self.memvalidate = kwargs.pop('memvalidate')
1789
self.verbose = kwargs.pop('verbose')
1790
self.is_debug = kwargs.pop('is_debug') or self.memvalidate or self.verbose
1791
1792
super().__init__(**kwargs)
1793
1794
def get_files(self):
1795
malloc_base = self.malloc.replace('-memvalidate', '').replace('-verbose', '').replace('-debug', '')
1796
malloc = utils.path_from_root('system/lib', {
1797
'dlmalloc': 'dlmalloc.c', 'emmalloc': 'emmalloc.c',
1798
}[malloc_base])
1799
# Include sbrk.c in libc, it uses tracing and libc itself doesn't have a tracing variant.
1800
sbrk = utils.path_from_root('system/lib/libc/sbrk.c')
1801
return [malloc, sbrk]
1802
1803
def get_cflags(self):
1804
cflags = super().get_cflags()
1805
if self.memvalidate:
1806
cflags += ['-DEMMALLOC_MEMVALIDATE']
1807
if self.verbose:
1808
cflags += ['-DEMMALLOC_VERBOSE']
1809
if self.is_debug:
1810
cflags += ['-UNDEBUG', '-DDLMALLOC_DEBUG']
1811
else:
1812
cflags += ['-DNDEBUG']
1813
if self.is_tracing:
1814
cflags += ['--tracing']
1815
return cflags
1816
1817
def get_base_name_prefix(self):
1818
return 'lib%s' % self.malloc
1819
1820
def get_base_name(self):
1821
name = super().get_base_name()
1822
if self.is_debug and not self.memvalidate and not self.verbose:
1823
name += '-debug'
1824
if self.is_tracing:
1825
name += '-tracing'
1826
return name
1827
1828
def can_use(self):
1829
return super().can_use() and settings.MALLOC not in {'none', 'mimalloc'}
1830
1831
@classmethod
1832
def vary_on(cls):
1833
return super().vary_on() + ['is_debug', 'is_tracing', 'memvalidate', 'verbose']
1834
1835
@classmethod
1836
def get_default_variation(cls, **kwargs):
1837
return super().get_default_variation(
1838
malloc=settings.MALLOC,
1839
is_debug=settings.ASSERTIONS,
1840
is_tracing=settings.EMSCRIPTEN_TRACING,
1841
memvalidate='memvalidate' in settings.MALLOC,
1842
verbose='verbose' in settings.MALLOC,
1843
**kwargs,
1844
)
1845
1846
@classmethod
1847
def variations(cls):
1848
combos = super().variations()
1849
return ([dict(malloc='dlmalloc', **combo) for combo in combos if not combo['memvalidate'] and not combo['verbose']] +
1850
[dict(malloc='emmalloc', **combo) for combo in combos if not combo['memvalidate'] and not combo['verbose']] +
1851
[dict(malloc='emmalloc-memvalidate-verbose', **combo) for combo in combos if combo['memvalidate'] and combo['verbose']] +
1852
[dict(malloc='emmalloc-memvalidate', **combo) for combo in combos if combo['memvalidate'] and not combo['verbose']] +
1853
[dict(malloc='emmalloc-verbose', **combo) for combo in combos if combo['verbose'] and not combo['memvalidate']])
1854
1855
1856
class libmimalloc(MTLibrary):
1857
name = 'libmimalloc'
1858
1859
cflags = [
1860
'-fno-builtin',
1861
'-Wno-unused-function',
1862
'-Wno-unused-but-set-variable',
1863
'-Wno-unused-variable',
1864
'-Wno-deprecated-pragma',
1865
# build emmalloc as only a system allocator, without exporting itself onto
1866
# malloc/free in the global scope
1867
'-DEMMALLOC_NO_STD_EXPORTS',
1868
# build mimalloc with an override of malloc/free
1869
'-DMI_MALLOC_OVERRIDE',
1870
# TODO: add build modes that include debug checks 1,2,3
1871
'-DMI_DEBUG=0',
1872
# disable `assert()` in the underlying emmalloc allocator
1873
'-DNDEBUG',
1874
# avoid use of `__builtin_thread_pointer()`
1875
'-DMI_LIBC_MUSL',
1876
]
1877
1878
# malloc/free/calloc are runtime functions and can be generated during LTO
1879
# Therefore they cannot themselves be part of LTO.
1880
force_object_files = True
1881
1882
includes = ['system/lib/mimalloc/include']
1883
1884
# Build all of mimalloc, and also emmalloc which is used as the system
1885
# allocator underneath it.
1886
src_dir = 'system/lib/'
1887
src_files = glob_in_path(
1888
path='system/lib/mimalloc/src',
1889
glob_pattern='*.c',
1890
# mimalloc includes some files at the source level, so exclude them here.
1891
excludes=['alloc-override.c', 'free.c', 'page-queue.c', 'static.c'],
1892
)
1893
src_files += [utils.path_from_root('system/lib/mimalloc/src/prim/prim.c')]
1894
src_files += [utils.path_from_root('system/lib/emmalloc.c')]
1895
# Include sbrk.c in libc, it uses tracing and libc itself doesn't have a tracing variant.
1896
src_files += [utils.path_from_root('system/lib/libc/sbrk.c')]
1897
1898
def can_use(self):
1899
return super().can_use() and settings.MALLOC == 'mimalloc'
1900
1901
1902
class libal(Library):
1903
name = 'libal'
1904
1905
cflags = ['-Os']
1906
src_dir = 'system/lib'
1907
src_files = ['al.c']
1908
1909
1910
class libGL(MTLibrary):
1911
name = 'libGL'
1912
1913
src_dir = 'system/lib/gl'
1914
src_files = ['gl.c', 'webgl1.c', 'libprocaddr.c', 'webgl2.c']
1915
1916
cflags = ['-Oz']
1917
1918
def __init__(self, **kwargs):
1919
self.is_legacy = kwargs.pop('is_legacy')
1920
self.is_webgl2 = kwargs.pop('is_webgl2')
1921
self.is_ofb = kwargs.pop('is_ofb')
1922
self.is_full_es3 = kwargs.pop('is_full_es3')
1923
self.is_enable_get_proc_address = kwargs.pop('is_enable_get_proc_address')
1924
super().__init__(**kwargs)
1925
1926
def get_base_name(self):
1927
name = super().get_base_name()
1928
if self.is_legacy:
1929
name += '-emu'
1930
if self.is_webgl2:
1931
name += '-webgl2'
1932
if self.is_ofb:
1933
name += '-ofb'
1934
if self.is_full_es3:
1935
name += '-full_es3'
1936
if self.is_enable_get_proc_address:
1937
name += '-getprocaddr'
1938
return name
1939
1940
def get_cflags(self):
1941
cflags = super().get_cflags()
1942
if self.is_legacy:
1943
cflags += ['-DLEGACY_GL_EMULATION=1']
1944
cflags += [f'-DMAX_WEBGL_VERSION={2 if self.is_webgl2 else 1}']
1945
if self.is_ofb:
1946
cflags += ['-D__EMSCRIPTEN_OFFSCREEN_FRAMEBUFFER__']
1947
if self.is_full_es3:
1948
cflags += ['-D__EMSCRIPTEN_FULL_ES3__']
1949
if self.is_enable_get_proc_address:
1950
cflags += ['-DGL_ENABLE_GET_PROC_ADDRESS=1']
1951
return cflags
1952
1953
@classmethod
1954
def vary_on(cls):
1955
return super().vary_on() + ['is_legacy', 'is_webgl2', 'is_ofb', 'is_full_es3', 'is_enable_get_proc_address']
1956
1957
@classmethod
1958
def get_default_variation(cls, **kwargs):
1959
return super().get_default_variation(
1960
is_legacy=settings.LEGACY_GL_EMULATION,
1961
is_webgl2=settings.MAX_WEBGL_VERSION >= 2,
1962
is_ofb=settings.OFFSCREEN_FRAMEBUFFER,
1963
is_full_es3=settings.FULL_ES3,
1964
is_enable_get_proc_address=settings.GL_ENABLE_GET_PROC_ADDRESS,
1965
**kwargs,
1966
)
1967
1968
1969
class libembind(MTLibrary):
1970
name = 'libembind'
1971
never_force = True
1972
1973
def __init__(self, **kwargs):
1974
self.with_rtti = kwargs.pop('with_rtti', False)
1975
super().__init__(**kwargs)
1976
1977
def get_cflags(self):
1978
cflags = super().get_cflags()
1979
cflags.append('-std=c++20')
1980
if not self.with_rtti:
1981
cflags += ['-fno-rtti', '-DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0']
1982
return cflags
1983
1984
@classmethod
1985
def vary_on(cls):
1986
return super().vary_on() + ['with_rtti']
1987
1988
def get_base_name(self):
1989
name = super().get_base_name()
1990
if self.with_rtti:
1991
name += '-rtti'
1992
return name
1993
1994
def get_files(self):
1995
return [utils.path_from_root('system/lib/embind/bind.cpp')]
1996
1997
@classmethod
1998
def get_default_variation(cls, **kwargs):
1999
return super().get_default_variation(with_rtti=settings.USE_RTTI, **kwargs)
2000
2001
2002
class libfetch(MTLibrary):
2003
name = 'libfetch'
2004
never_force = True
2005
includes = ['system/lib/libc']
2006
2007
def get_files(self):
2008
return [utils.path_from_root('system/lib/fetch/emscripten_fetch.c')]
2009
2010
2011
class libstb_image(Library):
2012
name = 'libstb_image'
2013
never_force = True
2014
includes = ['third_party']
2015
2016
def get_files(self):
2017
return [utils.path_from_root('system/lib/stb_image.c')]
2018
2019
2020
class libwasmfs(DebugLibrary, AsanInstrumentedLibrary, MTLibrary):
2021
name = 'libwasmfs'
2022
2023
cflags = ['-fno-exceptions', '-std=c++17']
2024
2025
includes = ['system/lib/wasmfs', 'system/lib/pthread']
2026
2027
def __init__(self, **kwargs):
2028
self.ignore_case = kwargs.pop('ignore_case')
2029
super().__init__(**kwargs)
2030
2031
def get_cflags(self):
2032
cflags = super().get_cflags()
2033
if self.ignore_case:
2034
cflags += ['-DWASMFS_CASE_INSENSITIVE']
2035
return cflags
2036
2037
def get_base_name(self):
2038
name = super().get_base_name()
2039
if self.ignore_case:
2040
name += '-icase'
2041
return name
2042
2043
@classmethod
2044
def vary_on(cls):
2045
return super().vary_on() + ['ignore_case']
2046
2047
@classmethod
2048
def get_default_variation(cls, **kwargs):
2049
return super().get_default_variation(ignore_case=settings.CASE_INSENSITIVE_FS, **kwargs)
2050
2051
def get_files(self):
2052
backends = files_in_path(
2053
path='system/lib/wasmfs/backends',
2054
filenames=['fetch_backend.cpp',
2055
'ignore_case_backend.cpp',
2056
'js_file_backend.cpp',
2057
'memory_backend.cpp',
2058
'node_backend.cpp',
2059
'opfs_backend.cpp'])
2060
return backends + files_in_path(
2061
path='system/lib/wasmfs',
2062
filenames=['file.cpp',
2063
'file_table.cpp',
2064
'js_api.cpp',
2065
'emscripten.cpp',
2066
'paths.cpp',
2067
'special_files.cpp',
2068
'support.cpp',
2069
'syscalls.cpp',
2070
'wasmfs.cpp'])
2071
2072
def can_use(self):
2073
return settings.WASMFS
2074
2075
2076
# Minimal syscall implementation, enough for printf. If this can be linked in
2077
# instead of the full WasmFS then it saves a lot of code size for simple
2078
# programs that don't need a full FS implementation.
2079
class libwasmfs_no_fs(Library):
2080
name = 'libwasmfs_no_fs'
2081
2082
src_dir = 'system/lib/wasmfs'
2083
src_files = ['no_fs.c']
2084
2085
def can_use(self):
2086
# If the filesystem is forced then we definitely do not need this library.
2087
return settings.WASMFS and not settings.FORCE_FILESYSTEM
2088
2089
2090
class libwasmfs_noderawfs(Library):
2091
name = 'libwasmfs_noderawfs'
2092
2093
cflags = ['-fno-exceptions', '-std=c++17']
2094
2095
includes = ['system/lib/wasmfs']
2096
2097
def get_files(self):
2098
return files_in_path(
2099
path='system/lib/wasmfs/backends',
2100
filenames=['noderawfs_root.cpp'])
2101
2102
def can_use(self):
2103
return settings.WASMFS and settings.NODERAWFS
2104
2105
2106
class libhtml5(Library):
2107
name = 'libhtml5'
2108
2109
includes = ['system/lib/libc']
2110
cflags = ['-Oz']
2111
src_dir = 'system/lib/html5'
2112
src_glob = '*.c'
2113
2114
2115
class CompilerRTLibrary(Library):
2116
cflags = ['-fno-builtin']
2117
# compiler_rt files can't currently be part of LTO although we are hoping to remove this
2118
# restriction soon: https://reviews.llvm.org/D71738
2119
force_object_files = True
2120
2121
2122
class libubsan_minimal_rt(CompilerRTLibrary, MTLibrary):
2123
name = 'libubsan_minimal_rt'
2124
never_force = True
2125
2126
includes = ['system/lib/compiler-rt/lib']
2127
src_dir = 'system/lib/compiler-rt/lib/ubsan_minimal'
2128
src_files = ['ubsan_minimal_handlers.cpp']
2129
2130
2131
class libsanitizer_common_rt(CompilerRTLibrary, MTLibrary):
2132
name = 'libsanitizer_common_rt'
2133
includes = ['system/lib/compiler-rt/lib',
2134
'system/lib/libc']
2135
never_force = True
2136
cflags = [
2137
'-D_LARGEFILE64_SOURCE',
2138
# The upstream code has many format violations and suppresses it with
2139
# -Wno-format, so we match that.
2140
# https://github.com/llvm/llvm-project/blob/da675b922cca3dc9a76642d792e882979a3d8c82/compiler-rt/lib/sanitizer_common/CMakeLists.txt#L225-L226
2141
# TODO Remove this when the issues are resolved.
2142
'-Wno-format',
2143
]
2144
2145
src_dir = 'system/lib/compiler-rt/lib/sanitizer_common'
2146
src_glob = '*.cpp'
2147
src_glob_exclude = ['sanitizer_common_nolibc.cpp']
2148
2149
2150
class SanitizerLibrary(CompilerRTLibrary, MTLibrary):
2151
never_force = True
2152
2153
includes = ['system/lib/compiler-rt/lib']
2154
src_glob = '*.cpp'
2155
2156
2157
class libubsan_rt(SanitizerLibrary):
2158
name = 'libubsan_rt'
2159
2160
includes = ['system/lib/libc']
2161
cflags = ['-DUBSAN_CAN_USE_CXXABI']
2162
src_dir = 'system/lib/compiler-rt/lib/ubsan'
2163
src_glob_exclude = ['ubsan_diag_standalone.cpp']
2164
2165
2166
class liblsan_common_rt(SanitizerLibrary):
2167
name = 'liblsan_common_rt'
2168
2169
src_dir = 'system/lib/compiler-rt/lib/lsan'
2170
src_glob = 'lsan_common*.cpp'
2171
2172
2173
class liblsan_rt(SanitizerLibrary):
2174
name = 'liblsan_rt'
2175
2176
includes = ['system/lib/libc']
2177
src_dir = 'system/lib/compiler-rt/lib/lsan'
2178
src_glob_exclude = ['lsan_common.cpp', 'lsan_common_mac.cpp', 'lsan_common_linux.cpp',
2179
'lsan_common_emscripten.cpp']
2180
2181
2182
class libasan_rt(SanitizerLibrary):
2183
name = 'libasan_rt'
2184
2185
includes = ['system/lib/libc']
2186
src_dir = 'system/lib/compiler-rt/lib/asan'
2187
2188
2189
# This library is used when STANDALONE_WASM is set. In that mode, we don't
2190
# want to depend on JS, and so this library contains implementations of
2191
# things that we'd normally do in JS. That includes some general things
2192
# as well as some additional musl components (that normally we reimplement
2193
# in JS as it's more efficient that way).
2194
class libstandalonewasm(MuslInternalLibrary):
2195
name = 'libstandalonewasm'
2196
# LTO defeats the weak linking trick used in __original_main.c
2197
force_object_files = True
2198
2199
cflags = ['-Os', '-fno-builtin']
2200
src_dir = 'system/lib'
2201
2202
def __init__(self, **kwargs):
2203
self.is_mem_grow = kwargs.pop('is_mem_grow')
2204
self.is_pure = kwargs.pop('is_pure')
2205
self.nocatch = kwargs.pop('nocatch')
2206
super().__init__(**kwargs)
2207
2208
def get_base_name(self):
2209
name = super().get_base_name()
2210
if self.nocatch:
2211
name += '-nocatch'
2212
if self.is_mem_grow:
2213
name += '-memgrow'
2214
if self.is_pure:
2215
name += '-pure'
2216
return name
2217
2218
def get_cflags(self):
2219
cflags = super().get_cflags()
2220
cflags += ['-DNDEBUG', '-DEMSCRIPTEN_STANDALONE_WASM']
2221
if self.is_mem_grow:
2222
cflags += ['-DEMSCRIPTEN_MEMORY_GROWTH']
2223
if self.is_pure:
2224
cflags += ['-DEMSCRIPTEN_PURE_WASI']
2225
if self.nocatch:
2226
cflags.append('-DEMSCRIPTEN_NOCATCH')
2227
return cflags
2228
2229
@classmethod
2230
def vary_on(cls):
2231
return super().vary_on() + ['is_mem_grow', 'is_pure', 'nocatch']
2232
2233
@classmethod
2234
def get_default_variation(cls, **kwargs):
2235
return super().get_default_variation(
2236
is_mem_grow=settings.ALLOW_MEMORY_GROWTH,
2237
is_pure=settings.PURE_WASI or settings.GROWABLE_ARRAYBUFFERS,
2238
nocatch=settings.DISABLE_EXCEPTION_CATCHING and not settings.WASM_EXCEPTIONS,
2239
**kwargs,
2240
)
2241
2242
def get_files(self):
2243
files = files_in_path(
2244
path='system/lib/standalone',
2245
filenames=['standalone.c',
2246
'standalone_wasm_stdio.c',
2247
'__main_void.c'])
2248
# It is more efficient to use JS methods for time, normally.
2249
files += files_in_path(
2250
path='system/lib/libc/musl/src/time',
2251
filenames=['__secs_to_tm.c',
2252
'__tz.c',
2253
'gettimeofday.c',
2254
'localtime_r.c',
2255
'gmtime_r.c',
2256
'mktime.c',
2257
'strptime.c',
2258
'timegm.c',
2259
'time.c'])
2260
# It is more efficient to use JS for __assert_fail, as it avoids always
2261
# including fprintf etc.
2262
files += files_in_path(
2263
path='system/lib/libc/musl/src/exit',
2264
filenames=['assert.c', 'exit.c'])
2265
return files
2266
2267
def can_use(self):
2268
return super().can_use() and settings.STANDALONE_WASM
2269
2270
2271
class libjsmath(Library):
2272
name = 'libjsmath'
2273
cflags = ['-Os']
2274
src_dir = 'system/lib'
2275
src_files = ['jsmath.c']
2276
2277
def can_use(self):
2278
return super().can_use() and settings.JS_MATH
2279
2280
2281
class libstubs(DebugLibrary):
2282
name = 'libstubs'
2283
src_dir = 'system/lib/libc'
2284
includes = ['system/lib/libc/musl/src/include']
2285
src_files = ['emscripten_syscall_stubs.c', 'emscripten_libc_stubs.c']
2286
2287
2288
def get_libs_to_link(options):
2289
libs_to_link = []
2290
2291
if options.nostdlib:
2292
return libs_to_link
2293
2294
already_included = set()
2295
system_libs_map = Library.get_usable_variations()
2296
2297
# Setting this in the environment will avoid checking dependencies and make
2298
# building big projects a little faster 1 means include everything; otherwise
2299
# it can be the name of a lib (libc++, etc.).
2300
# You can provide 1 to include everything, or a comma-separated list with the
2301
# ones you want
2302
force_include = []
2303
force = os.environ.get('EMCC_FORCE_STDLIBS')
2304
# Setting this will only use the forced libs in EMCC_FORCE_STDLIBS. This
2305
# avoids spending time checking for unresolved symbols in your project files,
2306
# which can speed up linking, but if you do not have the proper list of
2307
# actually needed libraries, errors can occur.
2308
only_forced = os.environ.get('EMCC_ONLY_FORCED_STDLIBS')
2309
if only_forced:
2310
# One of the purposes EMCC_ONLY_FORCED_STDLIBS was to skip the scanning
2311
# of the input files for reverse dependencies.
2312
diagnostics.warning('deprecated', 'EMCC_ONLY_FORCED_STDLIBS is deprecated. Use `-nostdlib` to avoid linking standard libraries')
2313
if force == '1':
2314
force_include = [name for name, lib in system_libs_map.items() if not lib.never_force]
2315
elif force is not None:
2316
force_include = force.split(',')
2317
if force_include:
2318
logger.debug(f'forcing stdlibs: {force_include}')
2319
2320
def add_library(libname, whole_archive=False):
2321
lib = system_libs_map[libname]
2322
if lib.name in already_included:
2323
return
2324
already_included.add(lib.name)
2325
2326
logger.debug('including %s (%s)' % (lib.name, lib.get_filename()))
2327
2328
need_whole_archive = lib.name in force_include and lib.get_ext() == '.a'
2329
libs_to_link.append((lib.get_link_flag(), whole_archive or need_whole_archive))
2330
2331
if not options.nostartfiles:
2332
if settings.SHARED_MEMORY:
2333
add_library('crtbegin')
2334
2335
if not settings.SIDE_MODULE:
2336
if settings.STANDALONE_WASM:
2337
if settings.EXPECT_MAIN:
2338
add_library('crt1')
2339
else:
2340
add_library('crt1_reactor')
2341
elif settings.PROXY_TO_PTHREAD:
2342
add_library('crt1_proxy_main')
2343
2344
if settings.SIDE_MODULE:
2345
return libs_to_link
2346
2347
# We add the forced libs last so that any libraries that are added in the normal
2348
# sequence below are added in the correct order even when they are also part of
2349
# EMCC_FORCE_STDLIBS.
2350
def add_forced_libs():
2351
for forced in force_include:
2352
if forced not in system_libs_map:
2353
utils.exit_with_error('invalid forced library: %s', forced)
2354
add_library(forced)
2355
2356
if options.nodefaultlibs:
2357
add_forced_libs()
2358
return libs_to_link
2359
2360
sanitize = settings.USE_LSAN or settings.USE_ASAN or settings.UBSAN_RUNTIME
2361
2362
def add_sanitizer_libs():
2363
if settings.USE_ASAN:
2364
force_include.append('libasan_rt')
2365
add_library('libasan_rt')
2366
elif settings.USE_LSAN:
2367
force_include.append('liblsan_rt')
2368
add_library('liblsan_rt')
2369
2370
if settings.UBSAN_RUNTIME == 1:
2371
add_library('libubsan_minimal_rt')
2372
elif settings.UBSAN_RUNTIME == 2:
2373
add_library('libubsan_rt')
2374
2375
if settings.USE_LSAN or settings.USE_ASAN:
2376
add_library('liblsan_common_rt')
2377
2378
if sanitize:
2379
add_library('libsanitizer_common_rt')
2380
2381
if only_forced:
2382
add_library('libcompiler_rt')
2383
add_sanitizer_libs()
2384
add_forced_libs()
2385
return libs_to_link
2386
2387
if settings.AUTO_NATIVE_LIBRARIES:
2388
add_library('libGL')
2389
add_library('libal')
2390
add_library('libhtml5')
2391
2392
# JS math must come before anything else, so that it overrides the normal
2393
# libc math.
2394
if settings.JS_MATH:
2395
add_library('libjsmath')
2396
2397
# C libraries that override libc must come before it
2398
if settings.PRINTF_LONG_DOUBLE:
2399
add_library('libprintf_long_double')
2400
# See comment in libc_optz itself
2401
if settings.SHRINK_LEVEL >= 2 and not settings.LINKABLE and \
2402
not os.environ.get('EMCC_FORCE_STDLIBS'):
2403
add_library('libc_optz')
2404
if settings.STANDALONE_WASM:
2405
add_library('libstandalonewasm')
2406
if settings.ALLOW_UNIMPLEMENTED_SYSCALLS:
2407
add_library('libstubs')
2408
if not options.nolibc:
2409
if not settings.EXIT_RUNTIME:
2410
add_library('libnoexit')
2411
add_library('libc')
2412
if settings.MALLOC == 'mimalloc':
2413
add_library('libmimalloc')
2414
if settings.USE_ASAN:
2415
# See https://github.com/emscripten-core/emscripten/issues/23288#issuecomment-2571648258
2416
utils.exit_with_error('mimalloc is not compatible with -fsanitize=address')
2417
elif settings.MALLOC != 'none':
2418
add_library('libmalloc')
2419
add_library('libcompiler_rt')
2420
if settings.LINK_AS_CXX:
2421
add_library('libc++')
2422
if settings.LINK_AS_CXX or sanitize:
2423
add_library('libc++abi')
2424
if settings.WASM_EXCEPTIONS:
2425
add_library('libunwind')
2426
2427
if settings.PROXY_POSIX_SOCKETS:
2428
add_library('libsockets_proxy')
2429
else:
2430
add_library('libsockets')
2431
2432
if settings.WASM_WORKERS and (not settings.SINGLE_FILE and not settings.RELOCATABLE):
2433
# When we include libwasm_workers we use `--whole-archive` to ensure
2434
# that the static constructor (`emscripten_wasm_worker_main_thread_initialize`)
2435
# is run.
2436
add_library('libwasm_workers', whole_archive=True)
2437
2438
if settings.WASMFS:
2439
# Link in the no-fs version first, so that if it provides all the needed
2440
# system libraries then WasmFS is not linked in at all. (We only do this if
2441
# the filesystem is not forced; if it is then we know we definitely need the
2442
# whole thing, and it would be unnecessary work to try to link in the no-fs
2443
# version).
2444
if not settings.FORCE_FILESYSTEM:
2445
add_library('libwasmfs_no_fs')
2446
add_library('libwasmfs')
2447
2448
add_sanitizer_libs()
2449
add_forced_libs()
2450
return libs_to_link
2451
2452
2453
def calculate(options):
2454
libs_to_link = get_libs_to_link(options)
2455
2456
# When LINKABLE is set the entire link command line is wrapped in --whole-archive by
2457
# building.link_ldd. And since --whole-archive/--no-whole-archive processing does not nest we
2458
# shouldn't add any extra `--no-whole-archive` or we will undo the intent of building.link_ldd.
2459
if settings.LINKABLE or settings.SIDE_MODULE:
2460
return [l[0] for l in libs_to_link]
2461
2462
# Wrap libraries in --whole-archive, as needed. We need to do this last
2463
# since otherwise the abort sorting won't make sense.
2464
ret = []
2465
in_group = False
2466
for name, need_whole_archive in libs_to_link:
2467
if need_whole_archive and not in_group:
2468
ret.append('--whole-archive')
2469
in_group = True
2470
if in_group and not need_whole_archive:
2471
ret.append('--no-whole-archive')
2472
in_group = False
2473
ret.append(name)
2474
if in_group:
2475
ret.append('--no-whole-archive')
2476
2477
return ret
2478
2479
2480
def safe_copytree(src, dst, excludes=None):
2481
# We cannot use `shutil.copytree` there because we need to ensure the
2482
# output tree is writable, and in some cases the emscripten tree
2483
# itself is readonly (e.g. NixOS).
2484
# Even if we pass copy_function=safe_copy python's `shutil.copytree`
2485
# will use its internal logic for copying directories and it will
2486
# unconditionally copy the source directory's mode bits.
2487
os.makedirs(dst, exist_ok=True)
2488
for entry in os.scandir(src):
2489
if excludes and entry.name in excludes:
2490
continue
2491
srcname = os.path.join(src, entry.name)
2492
dstname = os.path.join(dst, entry.name)
2493
if entry.is_dir():
2494
safe_copytree(srcname, dstname, excludes)
2495
else:
2496
utils.safe_copy(srcname, dstname)
2497
2498
2499
def install_system_headers(stamp):
2500
install_dirs = {
2501
'system/include': '',
2502
'system/lib/compiler-rt/include': '',
2503
'system/lib/libunwind/include': '',
2504
# Copy the generic arch files first then
2505
'system/lib/libc/musl/arch/generic': '',
2506
# Then overlay the emscripten directory on top.
2507
# This mimics how musl itself installs its headers.
2508
'system/lib/libc/musl/arch/emscripten': '',
2509
'system/lib/libc/musl/include': '',
2510
'system/lib/libcxx/include': 'c++/v1',
2511
'system/lib/libcxxabi/include': 'c++/v1',
2512
'system/lib/mimalloc/include': '',
2513
}
2514
2515
target_include_dir = cache.get_include_dir()
2516
for src, dest in install_dirs.items():
2517
src = utils.path_from_root(src)
2518
dest = os.path.join(target_include_dir, dest)
2519
safe_copytree(src, dest, excludes={'alltypes.h.in'})
2520
2521
pkgconfig_src = utils.path_from_root('system/lib/pkgconfig')
2522
pkgconfig_dest = cache.get_sysroot_dir('lib/pkgconfig')
2523
safe_copytree(pkgconfig_src, pkgconfig_dest)
2524
2525
bin_src = utils.path_from_root('system/bin')
2526
bin_dest = cache.get_sysroot_dir('bin')
2527
safe_copytree(bin_src, bin_dest)
2528
2529
# Create a version header based on the emscripten-version.txt
2530
version_file = cache.get_include_dir('emscripten/version.h')
2531
utils.write_file(version_file, textwrap.dedent(f'''\
2532
/* Automatically generated by tools/system_libs.py */
2533
#define __EMSCRIPTEN_MAJOR__ {utils.EMSCRIPTEN_VERSION_MAJOR}
2534
#define __EMSCRIPTEN_MINOR__ {utils.EMSCRIPTEN_VERSION_MINOR}
2535
#define __EMSCRIPTEN_TINY__ {utils.EMSCRIPTEN_VERSION_TINY}
2536
2537
// Legacy mixed-case macros:
2538
#define __EMSCRIPTEN_major__ __EMSCRIPTEN_MAJOR__
2539
#define __EMSCRIPTEN_minor__ __EMSCRIPTEN_MINOR__
2540
#define __EMSCRIPTEN_tiny__ __EMSCRIPTEN_TINY__
2541
#pragma clang deprecated(__EMSCRIPTEN_major__, "Use __EMSCRIPTEN_MAJOR__ instead")
2542
#pragma clang deprecated(__EMSCRIPTEN_minor__, "Use __EMSCRIPTEN_MINOR__ instead")
2543
#pragma clang deprecated(__EMSCRIPTEN_tiny__, "Use __EMSCRIPTEN_TINY__ instead")
2544
'''))
2545
2546
# Create a stamp file that signal that the headers have been installed
2547
# Removing this file, or running `emcc --clear-cache` or running
2548
# `./embuilder build sysroot --force` will cause the re-installation of
2549
# the system headers.
2550
utils.write_file(stamp, 'x')
2551
return stamp
2552
2553
2554
@ToolchainProfiler.profile()
2555
def ensure_sysroot():
2556
cache.get('sysroot_install.stamp', install_system_headers, what='system headers')
2557
2558
2559
def build_deferred():
2560
assert USE_NINJA
2561
top_level_ninja = get_top_level_ninja_file()
2562
if os.path.isfile(top_level_ninja):
2563
run_ninja(os.path.dirname(top_level_ninja))
2564
2565