Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/embuilder.py
6162 views
1
#!/usr/bin/env python3
2
# Copyright 2014 The Emscripten Authors. All rights reserved.
3
# Emscripten is available under two separate licenses, the MIT license and the
4
# University of Illinois/NCSA Open Source License. Both these licenses can be
5
# found in the LICENSE file.
6
7
"""Tool to manage building of system libraries and ports.
8
9
In general emcc will build them automatically on demand, so you do not
10
strictly need to use this tool, but it gives you more control over the
11
process (in particular, if emcc does this automatically, and you are
12
running multiple build commands in parallel, confusion can occur).
13
"""
14
15
import argparse
16
import fnmatch
17
import logging
18
import os
19
import sys
20
import time
21
from contextlib import contextmanager
22
23
from tools import cache, ports, shared, system_libs, utils
24
from tools.settings import settings
25
from tools.system_libs import USE_NINJA
26
27
# Minimal subset of targets used by CI systems to build enough to be useful
28
MINIMAL_TASKS = [
29
'libcompiler_rt',
30
'libcompiler_rt-mt',
31
'libcompiler_rt-legacysjlj',
32
'libcompiler_rt-wasmsjlj',
33
'libcompiler_rt-ww',
34
'libc',
35
'libc-debug',
36
'libc-mt-debug',
37
'libc-ww-debug',
38
'libc_optz',
39
'libc_optz-debug',
40
'libc++abi',
41
'libc++abi-legacyexcept',
42
'libc++abi-wasmexcept',
43
'libc++abi-noexcept',
44
'libc++abi-debug',
45
'libc++abi-debug-legacyexcept',
46
'libc++abi-debug-wasmexcept',
47
'libc++abi-debug-noexcept',
48
'libc++abi-debug-mt-noexcept',
49
'libc++abi-debug-ww-noexcept',
50
'libc++',
51
'libc++-legacyexcept',
52
'libc++-wasmexcept',
53
'libc++-noexcept',
54
'libc++-ww-noexcept',
55
'libc++-debug',
56
'libc++-debug-wasmexcept',
57
'libc++-debug-legacyexcept',
58
'libc++-debug-noexcept',
59
'libc++-debug-mt-noexcept',
60
'libc++-debug-ww-noexcept',
61
'libal',
62
'libdlmalloc',
63
'libdlmalloc-tracing',
64
'libdlmalloc-debug',
65
'libdlmalloc-mt-debug',
66
'libdlmalloc-ww',
67
'libdlmalloc-ww-debug',
68
'libembind',
69
'libembind-rtti',
70
'libembind-mt-rtti',
71
'libemmalloc',
72
'libemmalloc-debug',
73
'libemmalloc-memvalidate',
74
'libemmalloc-verbose',
75
'libemmalloc-memvalidate-verbose',
76
'libmimalloc',
77
'libmimalloc-mt',
78
'libGL',
79
'libGL-getprocaddr',
80
'libGL-mt-getprocaddr',
81
'libGL-emu-getprocaddr',
82
'libGL-emu-webgl2-ofb-getprocaddr',
83
'libGL-webgl2-ofb-getprocaddr',
84
'libGL-ww-getprocaddr',
85
'libhtml5',
86
'libsockets',
87
'libsockets-mt',
88
'libsockets-ww',
89
'libstubs',
90
'libstubs-debug',
91
'libstandalonewasm-nocatch',
92
'crt1',
93
'crt1_proxy_main',
94
'crtbegin',
95
'libunwind-legacyexcept',
96
'libunwind-wasmexcept',
97
'libnoexit',
98
'bullet',
99
]
100
101
# Additional tasks on top of MINIMAL_TASKS that are necessary for PIC testing on
102
# CI (which has slightly more tests than other modes that want to use MINIMAL)
103
MINIMAL_PIC_TASKS = MINIMAL_TASKS + [
104
'libc-mt',
105
'libc_optz-mt',
106
'libc_optz-mt-debug',
107
'libc++abi-mt',
108
'libc++abi-mt-noexcept',
109
'libc++abi-debug-mt',
110
'libc++-mt',
111
'libc++-mt-noexcept',
112
'libc++-debug-mt',
113
'libdlmalloc-mt',
114
'libGL-emu',
115
'libGL-emu-webgl2-getprocaddr',
116
'libGL-mt-emu',
117
'libGL-mt-emu-webgl2-getprocaddr',
118
'libGL-mt-emu-webgl2-ofb-getprocaddr',
119
'libsockets_proxy',
120
'crtbegin',
121
'libsanitizer_common_rt',
122
'libubsan_rt',
123
'libwasm_workers-debug',
124
'libwasm_workers-debug-stub',
125
'libfetch',
126
'libfetch-mt',
127
'libwasmfs',
128
'libwasmfs-debug',
129
'libwasmfs_no_fs',
130
'giflib',
131
'sdl2',
132
'sdl2_gfx',
133
'sdl3',
134
]
135
136
PORTS = sorted(list(ports.ports_by_name.keys()) + list(ports.port_variants.keys()))
137
138
temp_files = shared.get_temp_files()
139
logger = logging.getLogger('embuilder')
140
legacy_prefixes = {
141
'libgl': 'libGL',
142
}
143
144
145
def get_help():
146
all_tasks = get_all_tasks()
147
all_tasks.sort()
148
return '''
149
Available targets:
150
151
build / clear
152
%s
153
154
Issuing 'embuilder build ALL' causes each task to be built.
155
''' % '\n '.join(all_tasks)
156
157
158
@contextmanager
159
def get_port_variant(name):
160
if name in ports.port_variants:
161
name, extra_settings = ports.port_variants[name]
162
old_settings = settings.dict().copy()
163
for key, value in extra_settings.items():
164
setattr(settings, key, value)
165
else:
166
old_settings = None
167
168
yield name
169
170
if old_settings:
171
settings.dict().update(old_settings)
172
173
174
def clear_port(port_name):
175
with get_port_variant(port_name) as port_name:
176
ports.clear_port(port_name, settings)
177
178
179
def build_port(port_name):
180
with get_port_variant(port_name) as port_name_base:
181
ports.build_port(port_name_base, settings)
182
183
184
def get_system_tasks():
185
system_libraries = system_libs.Library.get_all_variations()
186
system_tasks = list(system_libraries.keys())
187
return system_libraries, system_tasks
188
189
190
def get_all_tasks():
191
return get_system_tasks()[1] + PORTS
192
193
194
def handle_port_error(target, message):
195
utils.exit_with_error(f'error building port `{target}` | {message}')
196
197
198
def main():
199
all_build_start_time = time.time()
200
201
parser = argparse.ArgumentParser(description=__doc__,
202
formatter_class=argparse.RawDescriptionHelpFormatter,
203
epilog=get_help())
204
parser.add_argument('--lto', action='store_const', const='full', help='build bitcode object for LTO')
205
parser.add_argument('--lto=thin', dest='lto', action='store_const', const='thin', help='build bitcode object for ThinLTO')
206
parser.add_argument('--pic', action='store_true',
207
help='build relocatable objects suitable for dynamic linking')
208
parser.add_argument('-f', '--force', action='store_true',
209
help='force rebuild of target (by removing it first)')
210
parser.add_argument('-v', '--verbose', action='store_true',
211
help='show build commands')
212
parser.add_argument('--wasm64', action='store_true',
213
help='use wasm64 architecture')
214
parser.add_argument('operation', choices=['build', 'clear', 'rebuild'])
215
parser.add_argument('targets', nargs='*', help='see below')
216
args = parser.parse_args()
217
218
if args.operation != 'rebuild' and len(args.targets) == 0:
219
utils.exit_with_error('no build targets specified')
220
221
if args.operation == 'rebuild' and not USE_NINJA:
222
utils.exit_with_error('"rebuild" operation is only valid when using Ninja')
223
224
# process flags
225
226
# Check sanity so that if settings file has changed, the cache is cleared here.
227
# Otherwise, the cache will clear in an emcc process, which is invoked while building
228
# a system library into the cache, causing trouble.
229
cache.setup()
230
shared.check_sanity()
231
232
if args.lto:
233
settings.LTO = args.lto
234
235
if args.verbose:
236
shared.PRINT_SUBPROCS = True
237
238
if args.pic:
239
settings.RELOCATABLE = 1
240
241
if args.wasm64:
242
settings.MEMORY64 = 2
243
MINIMAL_TASKS[:] = [t for t in MINIMAL_TASKS if 'emmalloc' not in t]
244
245
do_build = args.operation == 'build'
246
do_clear = args.operation == 'clear'
247
if args.force:
248
do_clear = True
249
250
system_libraries, system_tasks = get_system_tasks()
251
252
# process tasks
253
auto_tasks = False
254
task_targets = dict.fromkeys(args.targets) # use dict to keep targets order
255
256
# substitute
257
predefined_tasks = {
258
'SYSTEM': system_tasks,
259
'USER': PORTS,
260
'MINIMAL': MINIMAL_TASKS,
261
'MINIMAL_PIC': MINIMAL_PIC_TASKS,
262
'ALL': system_tasks + PORTS,
263
}
264
for name, tasks in predefined_tasks.items():
265
if name in task_targets:
266
task_targets[name] = tasks
267
auto_tasks = True
268
269
# flatten tasks
270
tasks = []
271
for name, targets in task_targets.items():
272
if targets is None:
273
# Use target name as task
274
if '*' in name:
275
tasks.extend(fnmatch.filter(get_all_tasks(), name))
276
else:
277
tasks.append(name)
278
else:
279
# There are some ports that we don't want to build as part
280
# of ALL since they are not well tested or widely used:
281
if 'cocos2d' in targets:
282
targets.remove('cocos2d')
283
284
# Use targets from predefined_tasks
285
tasks.extend(targets)
286
287
if auto_tasks:
288
print('Building targets: %s' % ' '.join(tasks))
289
290
if USE_NINJA:
291
os.environ['EMBUILDER_PORT_BUILD_DEFERRED'] = '1'
292
293
for what in tasks:
294
for old, new in legacy_prefixes.items():
295
if what.startswith(old):
296
what = what.replace(old, new)
297
if do_build:
298
logger.info('building ' + what)
299
else:
300
logger.info('clearing ' + what)
301
start_time = time.time()
302
if what in system_libraries:
303
library = system_libraries[what]
304
if do_clear:
305
library.erase()
306
if do_build:
307
if USE_NINJA:
308
library.generate()
309
else:
310
library.build()
311
elif what == 'sysroot':
312
if do_clear:
313
cache.erase_file('sysroot_install.stamp')
314
if do_build:
315
system_libs.ensure_sysroot()
316
elif what in PORTS:
317
if do_clear:
318
clear_port(what)
319
if do_build:
320
build_port(what)
321
elif ':' in what or what.endswith('.py'):
322
name = ports.handle_use_port_arg(settings, what, lambda message: handle_port_error(what, message))
323
if do_clear:
324
clear_port(name)
325
if do_build:
326
build_port(name)
327
else:
328
logger.error('unfamiliar build target: ' + what)
329
return 1
330
331
time_taken = time.time() - start_time
332
logger.info('...success. Took %s(%.2fs)' % (('%02d:%02d mins ' % (time_taken // 60, time_taken % 60) if time_taken >= 60 else ''), time_taken))
333
334
if USE_NINJA and args.operation != 'clear':
335
system_libs.build_deferred()
336
337
if len(tasks) > 1 or USE_NINJA:
338
all_build_time_taken = time.time() - all_build_start_time
339
logger.info('Built %d targets in %s(%.2fs)' % (len(tasks), ('%02d:%02d mins ' % (all_build_time_taken // 60, all_build_time_taken % 60) if all_build_time_taken >= 60 else ''), all_build_time_taken))
340
341
return 0
342
343
344
if __name__ == '__main__':
345
try:
346
sys.exit(main())
347
except KeyboardInterrupt:
348
logger.warning("KeyboardInterrupt")
349
sys.exit(1)
350
351