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