buildmaster-config icon indicating copy to clipboard operation
buildmaster-config copied to clipboard

Add Emscripten libmpdec build step

Open hoodmane opened this issue 5 months ago • 14 comments

Goes together with: https://github.com/python/cpython/pull/136997

hoodmane avatar Jul 22 '25 13:07 hoodmane

Can these not be made already present on the host?

zware avatar Jul 22 '25 16:07 zware

Do you mean that we should build them once and then install in some prefix directory which we reuse between jobs?

hoodmane avatar Jul 22 '25 16:07 hoodmane

Building libmpdec is pretty fast, and it's nice for it to be reproducible. OTOH libssl takes several minutes to build...

hoodmane avatar Jul 22 '25 16:07 hoodmane

Do you mean that we should build them once and then install in some prefix directory which we reuse between jobs?

Right.

Building libmpdec is pretty fast, and it's nice for it to be reproducible. OTOH libssl takes several minutes to build...

Windows builds use pre-built libssl binaries, partly for this reason. They also build some libraries (statically) on every build, so I'm OK with whichever direction you want to go for Emscripten :)

zware avatar Jul 22 '25 17:07 zware

Do you mean that we should build them once and then install in some prefix directory which we reuse between jobs?

Right

FWIW - this is the approach that iOS and Android use as well - we've got standalone repos (iOS, Android) to manage binary releases; the CPython CI builds then download/cache those releases.

The complication for Emscripten is that we can't just add "bzip-1.0.8" and re-use it - we need to add "bzip-1.0.8, compiled for Emscripten 4.0.10". That's certainly possible, but it's definitely more complicated than the iOS/Android/macOS situation where the only real restriction is the minimum OS compatibility version.

@hoodmane One thought - we could cache these builds as part of the whole "Emscripten version cache" thing we discussed at EuroPython.

For the benefit of others - Emscripten doesn't provide any ABI compatibility guarantees between versions. We're effectively going to have to say "Python 3.14 uses Emscripten 4.0.11", and everything that is for Python3.14 will need to use that version of Emscripten.

This means the buildbot will need to maintain multiple versions of Emscripten; the idea @hoodmane and I discussed at EuroPython was to add an --emscripten-dir argument to the build script, and set the required Emscripten version number in the script; if -emscripten-dir is provided, that is used to find the appropriate EMSDK version on disk.

We could then expand that to also include cached builds of libffi, mpdecimal, or whatever else is a dependency. We'd end up with a directory structure that looks something like:

- emscripten-versions
  - 4.0.11
     - emsdk
     - prefix
       - include
       - lib
   - 4.0.20
     - emsdk
     - prefix
       - include
       - lib

The idea would be that if you specify an --emscripten-versions directory, it treats the content as a cache; the build target would assume cached builds exist and use the version specific prefix directory; targets for the build dependencies would install into the version-specific prefix.

Thoughts?

freakboy3742 avatar Jul 22 '25 23:07 freakboy3742

Makes sense to me. libmpdec only takes about 8 seconds to build though, so could we merge it first like this and then do the rearrangement with two libraries? I feel better about having two libs before making this change. Though I'm also okay with adding --emscripten-versions first.

hoodmane avatar Jul 23 '25 08:07 hoodmane

Makes sense to me. libmpdec only takes about 8 seconds to build though, so could we merge it first like this and then do the rearrangement with two libraries? I feel better about having two libs before making this change.

I'd be fine with merging this as is, and then re-arranging as part of the larger refactor of the build script.

freakboy3742 avatar Jul 24 '25 06:07 freakboy3742

The complication is that the 3.14 branch is now locked until 3.14.0 final, so we can't merge this change to the buildbot without breaking 3.14 builds.

freakboy3742 avatar Jul 24 '25 06:07 freakboy3742

It may be possible for the step to be made optional depending on the branch? Similar things have been done before I think: https://github.com/python/buildmaster-config/blob/847d2edfc6913945c21279cc0d709b86f1e6f926/master/custom/factories.py#L309-L314

patch
Subject: [PATCH] Make compile step not run on 3.14
---
Index: master/custom/factories.py
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/master/custom/factories.py b/master/custom/factories.py
--- a/master/custom/factories.py	(revision cacc9671eed1382c2b5cbdbde4e71a47a5c2164d)
+++ b/master/custom/factories.py	(date 1753342781896)
@@ -1339,7 +1339,7 @@
     buildersuffix = ".emscripten"
     factory_tags = ["emscripten"]
 
-    def setup(self, **kwargs):
+    def setup(self, branch, **kwargs):
         compile_environ = {
             "PATH": os.pathsep.join([
                 "/home/emscripten/emsdk",
@@ -1368,12 +1368,19 @@
                 name="Compile host libFFI",
                 command=["python3", "Tools/wasm/emscripten", "make-libffi"],
                 env=compile_environ,
-            ),
-            Compile(
-                name="Compile host libmpdec",
-                command=["python3", "Tools/wasm/emscripten", "make-mpdec"],
-                env=compile_environ,
-            ),
+            )
+        ])
+
+        # This is done because ...
+        if branch != '3.14':
+            self.addStep(
+                Compile(
+                    name="Compile host libmpdec",
+                    command=["python3", "Tools/wasm/emscripten", "make-mpdec"],
+                    env=compile_environ,
+            ))
+
+    self.addSteps([
             Configure(
                 name="Configure host Python",
                 command=["python3", "Tools/wasm/emscripten", "configure-host"],

StanFromIreland avatar Jul 24 '25 07:07 StanFromIreland

Thanks for the suggestion @StanFromIreland! I did that.

hoodmane avatar Jul 24 '25 08:07 hoodmane

@freakboy3742 does it look okay to merge now with the check that @StanFromIreland suggested?

hoodmane avatar Jul 28 '25 08:07 hoodmane

The complication with this approach is that PR builds (i.e., everything fired by the !buildbot comment trigger) all come through as "branch = 3", regardless of whether it's a PR on main, or a backport PR.

So, this change would mean we wouldn't have the ability to run CI builds on PRs against the 3.14 branch. That seems like fairly significant collateral damage, especially in the 3.14 RC window.

freakboy3742 avatar Jul 29 '25 00:07 freakboy3742

The mpdec changes have been merged into 3.14, so we can roll back to the "non-gated version" and merge.

freakboy3742 avatar Jul 29 '25 21:07 freakboy3742

The complication for Emscripten is that we can't just add "bzip-1.0.8" and re-use it - we need to add "bzip-1.0.8, compiled for Emscripten 4.0.10". That's certainly possible, but it's definitely more complicated than the iOS/Android/macOS situation where the only real restriction is the minimum OS compatibility version.

My two cents about this is that, indeed, Emscripten does not support ABI compatibility between versions, but I think it is mainly related to shared libraries, not static ones.

Static WASM libraries are standard WASM modules without any custom sections, so I think it would be quite safe to link the library built with old Emscripten versions to the runtime built with a newer Emscripten.

Maybe we can experiment this in Pyodide first and see if it works, wdyt @hoodmane?

ryanking13 avatar Sep 05 '25 06:09 ryanking13