Skip to content

gh-145219: Cache Emscripten libffi and mpdec builds, add install-emscripten cmd#145664

Merged
freakboy3742 merged 4 commits intopython:mainfrom
hoodmane:cache-emsdk-libs
Mar 11, 2026
Merged

gh-145219: Cache Emscripten libffi and mpdec builds, add install-emscripten cmd#145664
freakboy3742 merged 4 commits intopython:mainfrom
hoodmane:cache-emsdk-libs

Conversation

@hoodmane
Copy link
Contributor

@hoodmane hoodmane commented Mar 9, 2026

This moves the emsdk install from {emsdk_cache}/{emscripten_version} to {emsdk_cache}/{emscripten_version}/emsdk so that we can put the prefix at `emsdk_cache_dir/{emscripten_version}/prefix.

I moved the data about mpdec & libffi version, url, and shasum into config.toml along with the emscripten version. Then as a cache key I write the library config section to disk in the prefix dir like {libname}.json. I write it as a json file since tomllib can't write toml. On subsequent builds, if prefix/{libname}.json contains the same data then we can skip a rebuild.

I also added an install-emscripten command to make it easy to get this directory structure correct.

…l-emscripten cmd

This moves the emsdk install from `{emsdk_cache}/{emscripten_version}` to
`{emsdk_cache}/{emscripten_version}/emsdk` so that we can put the prefix at
`emsdk_cache_dir/{emscripten_version}/prefix.

I moved the data about mpdec & libffi version, url, and shasum into config.toml along
with the emscripten version. Then as a cache key I write the library config section to
disk in the prefix dir like `{libname}.json`. I write it as a json file since tomllib
can't write toml. On subsequent builds, if prefix/{libname}.json contains the same data
then we can skip a rebuild.

I also added an install-emscripten command to make it easy to get this directory
structure correct.
@freakboy3742
Copy link
Contributor

!buildbot emscripten

@bedevere-bot
Copy link

🤖 New build scheduled with the buildbot fleet by @freakboy3742 for commit ccad6f4 🤖

Results will be shown at:

https://buildbot.python.org/all/#/grid?branch=refs%2Fpull%2F145664%2Fmerge

The command will test the builders whose names match following regular expression: emscripten

The builders matched are:

  • WASM Emscripten PR

Copy link
Contributor

@freakboy3742 freakboy3742 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all looks good; I've added one minor bugfix (another path .absolute() call), plus added a wrapper around "configure + make" targets that I've found helpful in the iOS case.

I'm running buildbot CI now; assuming that passes, I'll merge in the morning - but that also gives you a chance to check that I haven't missed anything in the modifications I've made.

@functools.cache
def emsdk_cache_root(emsdk_cache):
required_version = required_emscripten_version()
return Path(emsdk_cache) / required_version
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return Path(emsdk_cache) / required_version
return Path(emsdk_cache).absolute() / required_version

Comment on lines 70 to 71
"host_build_dir": host_triple_dir / "build",
"host_dir": host_triple_dir / "build" / "python",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only part that I'd like to clean up - dropping the build part of the path. With this is place, we end up with cross-build/wasm32-emscripten/build/python and cross-build/wasm32-emscripten/build/mpdec etc - but nothing else at the same level as build.

However, we can't remove that level without also changing the buildbot config, so we might want to include that as part of the "move to Platforms" change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the buildbot config hard code build as part of the path somewhere? I suppose when it goes to run tests?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - it's hardcoded in the buildbot config.

One option to consider would be to add a python3 Tools/wasm/emscripten test target (and/or python3 Tools/wasm/emscripten run target). That would break the dependency on the internals of the build location at least.

@hoodmane
Copy link
Contributor Author

Thanks @freakboy3742!

Comment on lines +547 to +548
make_emscripten_libffi,
make_mpdec,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surely these should be in host?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So - they're things that need to be compiled once, and then never built again. I put them in host on the basis that they need to be done once, and then never again - which to me makes them more like build than host.

However, the fact that the target has a shortcut if they're already there means there's no real overhead to putting them in build.

@hoodmane
Copy link
Contributor Author

!buildbot emscripten

@bedevere-bot
Copy link

🤖 New build scheduled with the buildbot fleet by @hoodmane for commit 6b90ac2 🤖

Results will be shown at:

https://buildbot.python.org/all/#/grid?branch=refs%2Fpull%2F145664%2Fmerge

The command will test the builders whose names match following regular expression: emscripten

The builders matched are:

  • WASM Emscripten PR

Copy link
Contributor

@freakboy3742 freakboy3742 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Thanks for those updates!

@freakboy3742 freakboy3742 merged commit ebb150e into python:main Mar 11, 2026
46 checks passed
@miss-islington-app
Copy link

Thanks @hoodmane for the PR, and @freakboy3742 for merging it 🌮🎉.. I'm working now to backport this PR to: 3.14.
🐍🍒⛏🤖

miss-islington pushed a commit to miss-islington/cpython that referenced this pull request Mar 11, 2026
…cripten (pythonGH-145664)

Modifies the Emscripten build script to allow for caching of dependencies, and
for automated installation of new EMSDK versions.
(cherry picked from commit ebb150e76ab4988fdcd5e8caa36b9014497573a5)

Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
Co-authored-by: Russell Keith-Magee <russell@keith-magee.com>
@bedevere-app
Copy link

bedevere-app bot commented Mar 11, 2026

GH-145790 is a backport of this pull request to the 3.14 branch.

@bedevere-app bedevere-app bot removed the needs backport to 3.14 bugs and security fixes label Mar 11, 2026
freakboy3742 added a commit that referenced this pull request Mar 11, 2026
…scripten (GH-145664) (#145790)

Modifies the Emscripten build script to allow for caching of dependencies, and
for automated installation of new EMSDK versions.
(cherry picked from commit ebb150e)

Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
Co-authored-by: Russell Keith-Magee <russell@keith-magee.com>
@bedevere-bot
Copy link

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot AMD64 Fedora Stable Refleaks 3.x (tier-1) has failed when building commit ebb150e.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/320/builds/3807) and take a look at the build logs.
  4. Check if the failure is related to this commit (ebb150e) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/320/builds/3807

Failed tests:

  • test.test_multiprocessing_fork.test_processes

Failed subtests:

  • test_interrupt - test.test_multiprocessing_fork.test_processes.WithProcessesTestProcess.test_interrupt

Summary of the results of the build (if available):

==

Click to see traceback logs
Traceback (most recent call last):
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/multiprocessing/process.py", line 320, in _bootstrap
    self.run()
    ~~~~~~~~^^
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/test/_test_multiprocessing.py", line 525, in _sleep_some_event
    time.sleep(100)
    ~~~~~~~~~~^^^^^
KeyboardInterrupt
k


Traceback (most recent call last):
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/contextlib.py", line 85, in inner
    return func(*args, **kwds)
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/test/_test_multiprocessing.py", line 597, in test_interrupt
    exitcode = self._kill_process(multiprocessing.Process.interrupt)
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/contextlib.py", line 85, in inner
    return func(*args, **kwds)
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/test/_test_multiprocessing.py", line 578, in _kill_process
    self.assertEqual(join(), None)
                     ~~~~^^
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/test/_test_multiprocessing.py", line 251, in __call__
    return self.func(*args, **kwds)
           ~~~~~~~~~^^^^^^^^^^^^^^^
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/multiprocessing/process.py", line 156, in join
    res = self._popen.wait(timeout)
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/multiprocessing/popen_fork.py", line 44, in wait
    return self.poll(os.WNOHANG if timeout == 0.0 else 0)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/multiprocessing/popen_fork.py", line 28, in poll
    pid, sts = os.waitpid(self.pid, flag)
               ~~~~~~~~~~^^^^^^^^^^^^^^^^
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/test/_test_multiprocessing.py", line 574, in handler
    raise RuntimeError('join took too long: %s' % p)
RuntimeError: join took too long: <Process name='Process-540' pid=624137 parent=621620 started daemon>


Traceback (most recent call last):
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/contextlib.py", line 85, in inner
    return func(*args, **kwds)
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/test/_test_multiprocessing.py", line 597, in test_interrupt
    exitcode = self._kill_process(multiprocessing.Process.interrupt)
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/contextlib.py", line 85, in inner
    return func(*args, **kwds)
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/test/_test_multiprocessing.py", line 578, in _kill_process
    self.assertEqual(join(), None)
                     ~~~~^^
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/test/_test_multiprocessing.py", line 251, in __call__
    return self.func(*args, **kwds)
           ~~~~~~~~~^^^^^^^^^^^^^^^
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/multiprocessing/process.py", line 156, in join
    res = self._popen.wait(timeout)
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/multiprocessing/popen_fork.py", line 44, in wait
    return self.poll(os.WNOHANG if timeout == 0.0 else 0)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/multiprocessing/popen_fork.py", line 28, in poll
    pid, sts = os.waitpid(self.pid, flag)
               ~~~~~~~~~~^^^^^^^^^^^^^^^^
  File "/home/buildbot-worker/cstratak-fedora-stable-x86_64/3.x.cstratak-fedora-stable-x86_64.refleak/build/Lib/test/_test_multiprocessing.py", line 574, in handler
    raise RuntimeError('join took too long: %s' % p)
RuntimeError: join took too long: <Process name='Process-1' pid=657150 parent=657148 started daemon>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants