Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/tools/settings.py
4128 views
1
# Copyright 2021 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 copy
7
import difflib
8
import os
9
import re
10
from typing import Set, Dict, Any
11
12
from .utils import path_from_root, exit_with_error
13
from . import diagnostics
14
15
# Subset of settings that take a memory size (i.e. 1Gb, 64kb etc)
16
MEM_SIZE_SETTINGS = {
17
'GLOBAL_BASE',
18
'STACK_SIZE',
19
'TOTAL_STACK',
20
'INITIAL_HEAP',
21
'INITIAL_MEMORY',
22
'MEMORY_GROWTH_LINEAR_STEP',
23
'MEMORY_GROWTH_GEOMETRIC_CAP',
24
'GL_MAX_TEMP_BUFFER_SIZE',
25
'MAXIMUM_MEMORY',
26
'DEFAULT_PTHREAD_STACK_SIZE',
27
'ASYNCIFY_STACK_SIZE',
28
}
29
30
PORTS_SETTINGS = {
31
# All port-related settings are valid at compile time
32
'USE_SDL',
33
'USE_LIBPNG',
34
'USE_BULLET',
35
'USE_ZLIB',
36
'USE_BZIP2',
37
'USE_VORBIS',
38
'USE_COCOS2D',
39
'USE_ICU',
40
'USE_MODPLUG',
41
'USE_SDL_MIXER',
42
'USE_SDL_IMAGE',
43
'USE_SDL_TTF',
44
'USE_SDL_NET',
45
'USE_SDL_GFX',
46
'USE_LIBJPEG',
47
'USE_OGG',
48
'USE_REGAL',
49
'USE_BOOST_HEADERS',
50
'USE_HARFBUZZ',
51
'USE_MPG123',
52
'USE_GIFLIB',
53
'USE_FREETYPE',
54
'SDL2_MIXER_FORMATS',
55
'SDL2_IMAGE_FORMATS',
56
'USE_SQLITE3',
57
}
58
59
# Subset of settings that apply only when generating JS
60
JS_ONLY_SETTINGS = {
61
'DEFAULT_LIBRARY_FUNCS_TO_INCLUDE',
62
'INCLUDE_FULL_LIBRARY',
63
'PROXY_TO_WORKER',
64
'PROXY_TO_WORKER_FILENAME',
65
'BUILD_AS_WORKER',
66
'STRICT_JS',
67
'SMALL_XHR_CHUNKS',
68
'MODULARIZE',
69
'EXPORT_ES6',
70
'EXPORT_NAME',
71
'DYNAMIC_EXECUTION',
72
'PTHREAD_POOL_SIZE',
73
'PTHREAD_POOL_SIZE_STRICT',
74
'PTHREAD_POOL_DELAY_LOAD',
75
'DEFAULT_PTHREAD_STACK_SIZE',
76
}
77
78
# Subset of settings that apply at compile time.
79
# (Keep in sync with [compile] comments in settings.js)
80
COMPILE_TIME_SETTINGS = {
81
'MEMORY64',
82
'INLINING_LIMIT',
83
'DISABLE_EXCEPTION_CATCHING',
84
'DISABLE_EXCEPTION_THROWING',
85
'WASM_LEGACY_EXCEPTIONS',
86
'MAIN_MODULE',
87
'SIDE_MODULE',
88
'RELOCATABLE',
89
'LINKABLE',
90
'STRICT',
91
'EMSCRIPTEN_TRACING',
92
'PTHREADS',
93
'USE_PTHREADS', # legacy name of PTHREADS setting
94
'SHARED_MEMORY',
95
'SUPPORT_LONGJMP',
96
'WASM_OBJECT_FILES',
97
'WASM_WORKERS',
98
'BULK_MEMORY',
99
100
# Internal settings used during compilation
101
'EXCEPTION_CATCHING_ALLOWED',
102
'WASM_EXCEPTIONS',
103
'LTO',
104
'OPT_LEVEL',
105
'DEBUG_LEVEL',
106
107
# Affects ports
108
'GL_ENABLE_GET_PROC_ADDRESS', # NOTE: if SDL2 is updated to not rely on eglGetProcAddress(), this can be removed
109
110
# This is legacy setting that we happen to handle very early on
111
'RUNTIME_LINKED_LIBS',
112
}.union(PORTS_SETTINGS)
113
114
# Unlike `LEGACY_SETTINGS`, deprecated settings can still be used
115
# both on the command line and in the emscripten codebase.
116
#
117
# At some point in the future, once folks have stopped using these
118
# settings we can move them to `LEGACY_SETTINGS`.
119
#
120
# All settings here should be tagged as `[deprecated]` in settings.js
121
DEPRECATED_SETTINGS = {
122
'RUNTIME_LINKED_LIBS': 'you can simply list the libraries directly on the commandline now',
123
'CLOSURE_WARNINGS': 'use -Wclosure/-Wno-closure instead',
124
'LEGALIZE_JS_FFI': 'to disable JS type legalization use `-sWASM_BIGINT` or `-sSTANDALONE_WASM`',
125
'ASYNCIFY_EXPORTS': 'please use JSPI_EXPORTS instead',
126
'ASYNCIFY_LAZY_LOAD_CODE': 'lack of usage',
127
'USE_WEBGPU': 'please try migrating to --use-port=emdawnwebgpu, which implements a newer, incompatible version of webgpu.h (see tools/ports/emdawnwebgpu.py for more info)',
128
}
129
130
# Settings that don't need to be externalized when serializing to json because they
131
# are not used by the JS compiler.
132
INTERNAL_SETTINGS = {
133
'SIDE_MODULE_IMPORTS',
134
}
135
136
user_settings: Dict[str, str] = {}
137
138
139
def default_setting(name, new_default):
140
if name not in user_settings:
141
setattr(settings, name, new_default)
142
143
144
class SettingsManager:
145
attrs: Dict[str, Any] = {}
146
defaults: Dict[str, tuple] = {}
147
types: Dict[str, Any] = {}
148
allowed_settings: Set[str] = set()
149
legacy_settings: Dict[str, tuple] = {}
150
alt_names: Dict[str, str] = {}
151
internal_settings: Set[str] = set()
152
153
def __init__(self):
154
self.attrs.clear()
155
self.legacy_settings.clear()
156
self.defaults.clear()
157
self.alt_names.clear()
158
self.internal_settings.clear()
159
self.allowed_settings.clear()
160
161
# Load the JS defaults into python.
162
def read_js_settings(filename, attrs):
163
with open(filename) as fh:
164
settings = fh.read()
165
# Use a bunch of regexs to convert the file from JS to python
166
# TODO(sbc): This is kind hacky and we should probably convert
167
# this file in format that python can read directly (since we
168
# no longer read this file from JS at all).
169
settings = settings.replace('//', '#')
170
settings = re.sub(r'var ([\w\d]+)', r'attrs["\1"]', settings)
171
settings = re.sub(r'=\s+false\s*;', '= False', settings)
172
settings = re.sub(r'=\s+true\s*;', '= True', settings)
173
exec(settings, {'attrs': attrs})
174
175
internal_attrs = {}
176
read_js_settings(path_from_root('src/settings.js'), self.attrs)
177
read_js_settings(path_from_root('src/settings_internal.js'), internal_attrs)
178
self.attrs.update(internal_attrs)
179
self.infer_types()
180
181
strict_override = False
182
if 'EMCC_STRICT' in os.environ:
183
strict_override = int(os.environ.get('EMCC_STRICT'))
184
185
# Special handling for LEGACY_SETTINGS. See src/setting.js for more
186
# details
187
for legacy in self.attrs['LEGACY_SETTINGS']:
188
if len(legacy) == 2:
189
name, new_name = legacy
190
self.legacy_settings[name] = (None, 'setting renamed to ' + new_name)
191
self.alt_names[name] = new_name
192
self.alt_names[new_name] = name
193
default_value = self.attrs[new_name]
194
else:
195
name, fixed_values, err = legacy
196
self.legacy_settings[name] = (fixed_values, err)
197
default_value = fixed_values[0]
198
assert name not in self.attrs, 'legacy setting (%s) cannot also be a regular setting' % name
199
if not strict_override:
200
self.attrs[name] = default_value
201
202
self.internal_settings.update(internal_attrs.keys())
203
# Stash a deep copy of all settings in self.defaults. This allows us to detect which settings
204
# have local mods.
205
self.defaults.update(copy.deepcopy(self.attrs))
206
207
if strict_override:
208
self.attrs['STRICT'] = strict_override
209
210
def infer_types(self):
211
for key, value in self.attrs.items():
212
self.types[key] = type(value)
213
214
def dict(self):
215
return self.attrs
216
217
def external_dict(self, skip_keys={}): # noqa
218
external_settings = {}
219
for key, value in self.dict().items():
220
if value != self.defaults.get(key) and key not in INTERNAL_SETTINGS and key not in skip_keys:
221
external_settings[key] = value # noqa: PERF403
222
if not self.attrs['STRICT']:
223
# When not running in strict mode we also externalize all legacy settings
224
# (Since the external tools do process LEGACY_SETTINGS themselves)
225
for key in self.legacy_settings:
226
external_settings[key] = self.attrs[key]
227
return external_settings
228
229
def keys(self):
230
return self.attrs.keys()
231
232
def limit_settings(self, allowed):
233
self.allowed_settings.clear()
234
if allowed:
235
self.allowed_settings.update(allowed)
236
237
def __getattr__(self, attr):
238
if self.allowed_settings:
239
assert attr in self.allowed_settings, f"internal error: attempt to read setting '{attr}' while in limited settings mode"
240
241
if attr in self.attrs:
242
return self.attrs[attr]
243
else:
244
raise AttributeError(f"no such setting: '{attr}'")
245
246
def __setattr__(self, name, value):
247
if self.allowed_settings:
248
assert name in self.allowed_settings, f"internal error: attempt to write setting '{name}' while in limited settings mode"
249
250
if name == 'STRICT' and value:
251
for a in self.legacy_settings:
252
self.attrs.pop(a, None)
253
254
if name in self.legacy_settings:
255
# TODO(sbc): Rather then special case this we should have STRICT turn on the
256
# legacy-settings warning below
257
if self.attrs['STRICT']:
258
exit_with_error('legacy setting used in strict mode: %s', name)
259
fixed_values, error_message = self.legacy_settings[name]
260
if fixed_values and value not in fixed_values:
261
exit_with_error(f'invalid command line setting `-s{name}={value}`: {error_message}')
262
diagnostics.warning('legacy-settings', 'use of legacy setting: %s (%s)', name, error_message)
263
264
if name in self.alt_names:
265
alt_name = self.alt_names[name]
266
self.attrs[alt_name] = value
267
268
if name not in self.attrs:
269
msg = "Attempt to set a non-existent setting: '%s'\n" % name
270
valid_keys = set(self.attrs.keys()).difference(self.internal_settings)
271
suggestions = difflib.get_close_matches(name, valid_keys)
272
suggestions = [s for s in suggestions if s not in self.legacy_settings]
273
suggestions = ', '.join(suggestions)
274
if suggestions:
275
msg += ' - did you mean one of %s?\n' % suggestions
276
msg += " - perhaps a typo in emcc's -sX=Y notation?\n"
277
msg += ' - (see src/settings.js for valid values)'
278
exit_with_error(msg)
279
280
self.check_type(name, value)
281
self.attrs[name] = value
282
283
def check_type(self, name, value):
284
# These settings have a variable type so cannot be easily type checked.
285
if name in ('SUPPORT_LONGJMP', 'PTHREAD_POOL_SIZE', 'SEPARATE_DWARF', 'LTO', 'MODULARIZE'):
286
return
287
expected_type = self.types.get(name)
288
if not expected_type:
289
return
290
# Allow integers 1 and 0 for type `bool`
291
if expected_type == bool:
292
if value in (1, 0):
293
value = bool(value)
294
if value in ('True', 'False', 'true', 'false'):
295
exit_with_error('attempt to set `%s` to `%s`; use 1/0 to set boolean settings' % (name, value))
296
if type(value) is not expected_type:
297
exit_with_error('setting `%s` expects `%s` but got `%s`' % (name, expected_type.__name__, type(value).__name__))
298
299
def __getitem__(self, key):
300
return self.attrs[key]
301
302
def __setitem__(self, key, value):
303
self.attrs[key] = value
304
305
def backup(self):
306
return copy.deepcopy(self.attrs)
307
308
def restore(self, previous):
309
self.attrs.update(previous)
310
311
312
settings = SettingsManager()
313
314