Skip to content

Commit 2813537

Browse files
committed
Merge remote-tracking branch 'upstream/main' into rpath-nested-deps
2 parents ee7070d + 95d2bf9 commit 2813537

File tree

118 files changed

+1764
-1306
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+1764
-1306
lines changed

.circleci/config.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,8 @@ commands:
452452
name: download firefox
453453
shell: powershell.exe -ExecutionPolicy Bypass
454454
command: |
455+
$ErrorActionPreference = 'Stop'
456+
455457
# To download Firefox, we must first figure out what the latest Firefox version name is.
456458
# This is because there does not exist a stable/static URL to download latest Firefox from.
457459
$html = Invoke-WebRequest "https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/"
@@ -774,6 +776,7 @@ jobs:
774776
omitexports0.test_asyncify_longjmp
775777
omitexports0.test_emscripten_api
776778
strict.test_no_declare_asm_module_exports
779+
strict.test_dylink_global_inits_reversed
777780
"
778781
test-modularize-instance:
779782
executor: focal

.github/workflows/rebaseline-tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
name: Rebaseline Tests
1616
runs-on: ubuntu-latest
1717
env:
18-
GH_TOKEN: ${{ github.token }}
18+
GH_TOKEN: ${{ secrets.EMSCRIPTEN_BOT_TOKEN }}
1919
steps:
2020
- name: Checkout repo
2121
uses: actions/checkout@v4
@@ -58,5 +58,5 @@ jobs:
5858
fi
5959
fi
6060
git push origin rebaseline_tests
61-
gh pr create --fill --base ${{ github.ref_name }}
61+
gh pr create --fill --base ${{ github.ref_name }} --reviewer sbc100,kripken
6262
gh pr merge --squash --auto
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Update website
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches: [ main ]
7+
8+
jobs:
9+
update-website:
10+
name: Update website
11+
runs-on: ubuntu-latest
12+
env:
13+
GITHUB_TOKEN: ${{ secrets.EMSCRIPTEN_BOT_TOKEN }}
14+
steps:
15+
- name: Checkout repo
16+
uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0
19+
submodules: true
20+
- name: Checkout website repo
21+
uses: actions/checkout@v4
22+
with:
23+
repository: kripken/emscripten-site
24+
ref: gh-pages
25+
path: site/emscripten-site
26+
token: ${{ secrets.EMSCRIPTEN_BOT_TOKEN }}
27+
- name: pip install
28+
run: |
29+
which python3
30+
python3 --version
31+
python3 -m pip install -r requirements-dev.txt
32+
- name: Update docs
33+
run: |
34+
set -o errexit
35+
set -o xtrace
36+
git config --global user.name emscripten-bot
37+
git config --global user.email [email protected]
38+
if ./tools/maint/update_docs.py; then
39+
echo "update_docs.py returned zero, expectations up-to-date"
40+
# Exit early and don't create a PR
41+
exit 0
42+
else
43+
code=$?
44+
if [[ $code != 2 ]] ; then
45+
echo "update_docs.py failed with unexpected error $code (expected 2)"
46+
exit 1
47+
fi
48+
fi
49+
# Create a PR against the emscripten-site repo
50+
cd site/emscripten-site
51+
git push -f origin update
52+
gh pr create --fill --head update --base gh-pages --reviewer sbc100,kripken
53+
# TODO: Enable this once we figure out how to enforce review
54+
# requirment
55+
#gh pr merge --squash --auto

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ coverage.xml
3030
# Test output
3131
/out/
3232

33+
# When updating the website we check it out here.
34+
/site/emscripten-site/
35+
3336
# Windows ps1 launchers (created by ./tools/maint/create_entry_points.py)
3437
*.ps1
3538
# ...except the templates.

ChangeLog.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,21 @@ to browse the changes between the tags.
1818

1919
See docs/process.md for more on how version tagging works.
2020

21-
4.0.20 (in development)
21+
4.0.21 (in development)
2222
-----------------------
2323

24+
4.0.20 - 11/18/25
25+
-----------------
26+
- Linker flags specified on the command line are now passed to `wasm-ld` after
27+
the internal emscripten linker flags. This means that users can now override
28+
emscripten defaults with things `-Wl,--stack-first`. (#25803)
29+
- Added `emscripten_html5_remove_event_listener` function in `html5.h` in order
30+
to be able to remove a single callback. (#25535)
31+
- The standalone `file_packager.py` script no longer supports `--embed` with JS
32+
output (use `--obj-output` is now required for embedding data). This usage
33+
has been producing a warning since #16050 which is now an error. (#25049)
34+
- Embind now requires C++17 or newer. See #24850.
35+
2436
4.0.19 - 11/04/25
2537
-----------------
2638
- The `RETAIN_COMPILER_SETTINGS` setting and the corresponding

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ It can probably port your codebase, too!
2424
While Emscripten mostly focuses on compiling C and C++ using
2525
[Clang](https://clang.llvm.org/), it can be integrated with other LLVM-using
2626
compilers (for example, Rust has Emscripten integration, with the
27-
`wasm32-unknown-emscripten` and `asmjs-unknown-emscripten` targets).
27+
`wasm32-unknown-emscripten` target).
2828

2929
License
3030
-------

emrun.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -364,10 +364,10 @@ def kill_browser_process():
364364
global browser_process, processname_killed_atexit, current_browser_processes
365365
if browser_process and browser_process.poll() is None:
366366
try:
367-
logv('Terminating browser process pid=' + str(browser_process.pid) + '..')
367+
logv(f'Terminating browser process pid={browser_process.pid}..')
368368
browser_process.kill()
369369
except Exception as e:
370-
logv('Failed with error ' + str(e) + '!')
370+
logv(f'Failed with error {e}!')
371371

372372
browser_process = None
373373
# We have a hold of the target browser process explicitly, no need to resort to killall,
@@ -377,10 +377,10 @@ def kill_browser_process():
377377
if current_browser_processes:
378378
for pid in current_browser_processes:
379379
try:
380-
logv('Terminating browser process pid=' + str(pid['pid']) + '..')
380+
logv(f'Terminating browser process pid={pid["pid"]}..')
381381
os.kill(pid['pid'], 9)
382382
except Exception as e:
383-
logv('Failed with error ' + str(e) + '!')
383+
logv(f'Failed with error {e}!')
384384

385385
current_browser_processes = None
386386
# We have a hold of the target browser process explicitly, no need to resort to killall,
@@ -434,7 +434,7 @@ def pid_existed(pid):
434434
return False
435435

436436
for p in running_browser_processes:
437-
logv('Detected running browser process id: ' + str(p['pid']) + ', existed already at emrun startup? ' + str(pid_existed(p['pid'])))
437+
logv(f'Detected running browser process id: {p["pid"]}, existed already at emrun startup? {pid_existed(p["pid"])}')
438438

439439
current_browser_processes = [p for p in running_browser_processes if not pid_existed(p['pid'])]
440440

@@ -539,15 +539,15 @@ def serve_forever(self, timeout=0.5):
539539
time_since_message = now - last_message_time
540540
if emrun_options.silence_timeout != 0 and time_since_message > emrun_options.silence_timeout:
541541
self.shutdown()
542-
logi('No activity in ' + str(emrun_options.silence_timeout) + ' seconds. Quitting web server with return code ' + str(emrun_options.timeout_returncode) + '. (--silence-timeout option)')
542+
logi(f'No activity in {emrun_options.silence_timeout} seconds. Quitting web server with return code {emrun_options.timeout_returncode}. (--silence-timeout option)')
543543
page_exit_code = emrun_options.timeout_returncode
544544
emrun_options.kill_exit = True
545545

546546
# If the page has been running too long as a whole, kill process.
547547
time_since_start = now - page_start_time
548548
if emrun_options.timeout != 0 and time_since_start > emrun_options.timeout:
549549
self.shutdown()
550-
logi('Page has not finished in ' + str(emrun_options.timeout) + ' seconds. Quitting web server with return code ' + str(emrun_options.timeout_returncode) + '. (--timeout option)')
550+
logi(f'Page has not finished in {emrun_options.timeout} seconds. Quitting web server with return code {emrun_options.timeout_returncode}. (--timeout option)')
551551
emrun_options.kill_exit = True
552552
page_exit_code = emrun_options.timeout_returncode
553553

@@ -685,7 +685,7 @@ def do_POST(self): # # noqa: DC04
685685
pass
686686
with open(filename, 'wb') as fh:
687687
fh.write(data)
688-
logi('Wrote ' + str(len(data)) + ' bytes to file "' + filename + '".')
688+
logi(f'Wrote {len(data)} bytes to file "{filename}".')
689689
have_received_messages = True
690690
elif path == '/system_info':
691691
system_info = json.loads(get_system_info(format_json=True))
@@ -714,7 +714,7 @@ def do_POST(self): # # noqa: DC04
714714
elif data.startswith('^exit^'):
715715
if not emrun_options.serve_after_exit:
716716
page_exit_code = int(data[6:])
717-
logv('Web page has quit with a call to exit() with return code ' + str(page_exit_code) + '. Shutting down web server. Pass --serve-after-exit to keep serving even after the page terminates with exit().')
717+
logv(f'Web page has quit with a call to exit() with return code ${page_exit_code}. Shutting down web server. Pass --serve-after-exit to keep serving even after the page terminates with exit().')
718718
# Set server socket to nonblocking on shutdown to avoid sporadic deadlocks
719719
self.server.socket.setblocking(False)
720720
self.server.shutdown()
@@ -787,7 +787,7 @@ def get_cpu_info():
787787
except Exception as e:
788788
import traceback
789789
loge(traceback.format_exc())
790-
return {'model': 'Unknown ("' + str(e) + '")',
790+
return {'model': f'Unknown ("{e}")',
791791
'physicalCores': 1,
792792
'logicalCores': 1,
793793
'frequency': 0,
@@ -811,7 +811,7 @@ def get_android_cpu_infoline():
811811
hardware = line[line.find(':') + 1:].strip()
812812

813813
freq = int(check_output([ADB, 'shell', 'cat', '/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq']).strip()) // 1000
814-
return 'CPU: ' + processor + ', ' + hardware + ' @ ' + str(freq) + ' MHz'
814+
return f'CPU: {processor}, {hardware} @ {freq} MHz'
815815

816816

817817
def win_get_gpu_info():
@@ -1435,7 +1435,7 @@ def list_processes_by_name(exe_full_path):
14351435
# Fail gracefully if psutil not available
14361436
logv('import psutil failed, unable to detect browser processes')
14371437

1438-
logv('Searching for processes by full path name "' + exe_full_path + '".. found ' + str(len(pids)) + ' entries')
1438+
logv(f'Searching for processes by full path name "{exe_full_path}".. found {len(pids)} entries')
14391439

14401440
return pids
14411441

@@ -1681,7 +1681,7 @@ def run(args): # noqa: C901, PLR0912, PLR0915
16811681
else:
16821682
hostname = options.hostname
16831683
# create url for browser after opening the server so we have the final port number in case we are binding to port 0
1684-
url = 'http://' + hostname + ':' + str(options.port) + '/' + url
1684+
url = f'http://{hostname}:{options.port}/{url}'
16851685

16861686
if options.android:
16871687
if options.run_browser or options.browser_info:
@@ -1715,7 +1715,7 @@ def run(args): # noqa: C901, PLR0912, PLR0915
17151715
# 5. Locate the name of the main activity for the browser in manifest.txt and add an entry to above list in form 'appname/mainactivityname'
17161716

17171717
if options.android_tunnel:
1718-
subprocess.check_call([ADB, 'reverse', 'tcp:' + str(options.port), 'tcp:' + str(options.port)])
1718+
subprocess.check_call([ADB, 'reverse', f'tcp:{options.port}', f'tcp:{options.port}'])
17191719

17201720
url = url.replace('&', '\\&')
17211721
browser = [ADB, 'shell', 'am', 'start', '-a', 'android.intent.action.VIEW', '-n', browser_app, '-d', url]
@@ -1727,7 +1727,7 @@ def run(args): # noqa: C901, PLR0912, PLR0915
17271727
if options.run_browser or options.browser_info:
17281728
browser = find_browser(str(options.browser))
17291729
if not browser:
1730-
loge('Unable to find browser "' + str(options.browser) + '"! Check the correctness of the passed --browser=xxx parameter!')
1730+
loge(f'Unable to find browser "{options.browser}"! Check the correctness of the passed --browser=xxx parameter!')
17311731
return 1
17321732
browser_exe = browser[0]
17331733
browser_args = shlex.split(unwrap(options.browser_args))
@@ -1783,7 +1783,7 @@ def run(cmd):
17831783
run(['adb', 'shell', 'mkdir', '/mnt/sdcard/safe_firefox_profile'])
17841784
run(['adb', 'push', os.path.join(profile_dir, 'prefs.js'), '/mnt/sdcard/safe_firefox_profile/prefs.js'])
17851785
except Exception as e:
1786-
loge('Creating Firefox profile prefs.js file to internal storage in /mnt/sdcard failed with error ' + str(e) + '!')
1786+
loge(f'Creating Firefox profile prefs.js file to internal storage in /mnt/sdcard failed with error {e}!')
17871787
loge('Try running without --safe-firefox-profile flag if unattended execution mode is not important, or')
17881788
loge('enable rooted debugging on the Android device to allow adb to write files to /mnt/sdcard.')
17891789
browser += ['--es', 'args', '"--profile /mnt/sdcard/safe_firefox_profile"']
@@ -1833,9 +1833,9 @@ def run(cmd):
18331833
logv(browser_exe)
18341834
previous_browser_processes = list_processes_by_name(browser_exe)
18351835
for p in previous_browser_processes:
1836-
logv('Before spawning web browser, found a running ' + os.path.basename(browser_exe) + ' browser process id: ' + str(p['pid']))
1836+
logv(f'Before spawning web browser, found a running {os.path.basename(browser_exe)} browser process id: {p["pid"]}')
18371837
browser_process = subprocess.Popen(browser, env=subprocess_env())
1838-
logv('Launched browser process with pid=' + str(browser_process.pid))
1838+
logv(f'Launched browser process with pid={browser_process.pid}')
18391839
if options.kill_exit:
18401840
atexit.register(kill_browser_process)
18411841
# For Android automation, we execute adb, so this process does not
@@ -1847,7 +1847,7 @@ def run(cmd):
18471847
premature_quit_code = browser_process.poll()
18481848
if premature_quit_code is not None:
18491849
options.serve_after_close = True
1850-
logv('Warning: emrun got immediately detached from the target browser process (the process quit with exit code ' + str(premature_quit_code) + '). Cannot detect when user closes the browser. Behaving as if --serve-after-close was passed in.')
1850+
logv(f'Warning: emrun got immediately detached from the target browser process (the process quit with exit code {premature_quit_code}). Cannot detect when user closes the browser. Behaving as if --serve-after-close was passed in.')
18511851
if not options.browser:
18521852
logv('Try passing the --browser=/path/to/browser option to avoid this from occurring. See https://github.com/emscripten-core/emscripten/issues/3234 for more discussion.')
18531853

emscripten-version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
4.0.20-git
1+
4.0.21-git

site/source/docs/api_reference/html5.h.rst

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,54 @@ The ``useCapture`` parameter maps to ``useCapture`` in `EventTarget.addEventLis
9090

9191
Most functions return the result using the type :c:data:`EMSCRIPTEN_RESULT`. Zero and positive values denote success. Negative values signal failure. None of the functions fail or abort by throwing a JavaScript or C++ exception. If a particular browser does not support the given feature, the value :c:data:`EMSCRIPTEN_RESULT_NOT_SUPPORTED` will be returned at the time the callback is registered.
9292

93+
Unregister function
94+
-------------------
95+
96+
In order to unregister a single event handler callback, call the following function:
97+
98+
.. code-block:: cpp
99+
100+
EMSCRIPTEN_RESULT emscripten_html5_remove_event_listener(
101+
const char *target, // ID of the target HTML element.
102+
void *userData, // User-defined data (passed to the callback).
103+
int eventTypeId, // The event type ID (EMSCRIPTEN_EVENT_XXX).
104+
void *callback // Callback function.
105+
);
106+
107+
108+
The ``target``, ``userData`` and ``callback`` parameters are the same parameters provided in ``emscripten_set_some_callback`` with the only difference being that, since this function applies to all types of callbacks, the type of ``callback`` is ``void *``.
109+
110+
Note in particular that the value of ``userData`` will need to match with the call that was used to register the callback. If you are having trouble, double check the value of ``userData``.
111+
112+
The ``eventTypeId`` represents the event type, the same Id received in the callback functions.
113+
114+
The function returns ``EMSCRIPTEN_RESULT_SUCCESS`` when the event handler callback is removed and ``EMSCRIPTEN_RESULT_INVALID_PARAM`` otherwise.
115+
116+
.. code-block:: cpp
117+
118+
// Example
119+
120+
bool my_mouse_callback_1(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) {
121+
// ...
122+
}
123+
124+
bool my_mouse_callback_2(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) {
125+
// ...
126+
}
127+
128+
void main() {
129+
130+
// 1. set callbacks for mouse down and mouse move
131+
emscripten_set_mousedown_callback("#mydiv", 0, my_mouse_callback_1);
132+
emscripten_set_mousedown_callback("#mydiv", (void *) 34, my_mouse_callback_2);
133+
emscripten_set_mousemove_callback("#mydiv", 0, my_mouse_callback_1);
134+
135+
// 2. remove these callbacks
136+
emscripten_html5_remove_event_listener("#mydiv", 0, EMSCRIPTEN_EVENT_MOUSEDOWN, my_mouse_callback_1);
137+
emscripten_html5_remove_event_listener("#mydiv", (void *) 34, EMSCRIPTEN_EVENT_MOUSEDOWN, my_mouse_callback_2);
138+
emscripten_html5_remove_event_listener("#mydiv", 0, EMSCRIPTEN_EVENT_MOUSEMOVE, my_mouse_callback_1);
139+
}
140+
93141
94142
Callback functions
95143
------------------

site/source/docs/api_reference/wasm_audio_worklets.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@ and then, these processors are instantiated one or more times in the audio
4444
processing graph as AudioWorkletNodes.
4545

4646
Once a class type is instantiated on the Web Audio graph and the graph is
47-
running, a C/C++ function pointer callback will be invoked for each 128
48-
samples of the processed audio stream that flows through the node. Newer Web
49-
Audio API specs allow this to be changed, so for future compatibility use the
50-
``AudioSampleFrame``'s ``samplesPerChannel`` to get the value.
47+
running, a C/C++ function pointer callback will be invoked for each N samples
48+
of the processed audio stream that flows through the node (where N is is the
49+
number of samples per channel, exposed as ``AudioSampleFrame``'s
50+
``samplesPerChannel``, always 128 in the 1.0 Web Audio API, though with the 1.1
51+
API ``emscripten_create_audio_context()`` accepts a ``renderSizeHint`` option).
5152

5253
This callback will be executed on a dedicated separate audio processing
5354
thread with real-time processing priority. Each Web Audio context will

0 commit comments

Comments
 (0)