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