From 1a8c9d00a8578081aad8f8753cbbcadc5b0077e7 Mon Sep 17 00:00:00 2001 From: Michael Simacek Date: Fri, 29 May 2026 13:32:14 +0200 Subject: [PATCH 1/4] Use new delvewheel replace-needed command --- .../integration/advanced/NativeExtTest.java | 4 +-- .../builtins/objects/cext/copying/PEFile.java | 35 ++++--------------- mx.graalpython/mx_graalpython.py | 2 +- 3 files changed, 10 insertions(+), 31 deletions(-) diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/NativeExtTest.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/NativeExtTest.java index 0fdf6e15aa..548fbb3545 100644 --- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/NativeExtTest.java +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/NativeExtTest.java @@ -61,7 +61,7 @@ * because we cannot create multiple contexts that would load native extensions. */ public class NativeExtTest { - private static final String DELVEWHEEL_VERSION = "1.9.0"; + private static final String DELVEWHEEL_VERSION = "1.13.0"; @BeforeClass public static void setUpClass() { @@ -133,7 +133,7 @@ public void testMissingDelvewheelError() throws IOException { Value exception = ex.getGuestObject(); Assert.assertTrue(exception.isException()); Assert.assertEquals(ex.getMessage(), "SystemError", exception.getMetaObject().getMetaSimpleName()); - Assert.assertTrue(ex.getMessage(), ex.getMessage().contains("delvewheel==" + DELVEWHEEL_VERSION)); + Assert.assertTrue(ex.getMessage(), ex.getMessage().contains("delvewheel>=" + DELVEWHEEL_VERSION)); } } finally { Files.deleteIfExists(tempDir); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/PEFile.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/PEFile.java index 6227705191..cde55ce4db 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/PEFile.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/PEFile.java @@ -51,8 +51,8 @@ import com.oracle.truffle.api.TruffleFile; final class PEFile extends SharedObject { - private static final String DELVEWHEEL_VERSION = "1.9.0"; - private static final String DELVEWHEEL_INSTALL_INSTRUCTION = "IsolateNativeModules option needs `delvewheel` tool to copy libraries. Make sure you have `delvewheel==" + DELVEWHEEL_VERSION + + private static final String DELVEWHEEL_VERSION = "1.13.0"; + private static final String DELVEWHEEL_INSTALL_INSTRUCTION = "IsolateNativeModules option needs `delvewheel` tool to copy libraries. Make sure you have `delvewheel>=" + DELVEWHEEL_VERSION + "` available in the virtualenv or on PATH (needs environment access)."; private final PythonContext context; @@ -79,7 +79,7 @@ public void setId(String newId) { // TODO } - private String getDelvewheelPython() throws NativeLibraryToolException { + private String getDelvewheel() throws NativeLibraryToolException { TruffleFile delvewheel = which(context, "delvewheel.exe"); if (!delvewheel.exists()) { delvewheel = which(context, "delvewheel.bat"); @@ -90,26 +90,7 @@ private String getDelvewheelPython() throws NativeLibraryToolException { if (!delvewheel.exists()) { throw new NativeLibraryToolException("Could not find `delvewheel`. " + DELVEWHEEL_INSTALL_INSTRUCTION); } - TruffleFile python = delvewheel.resolveSibling("python.exe"); - if (!python.exists()) { - python = delvewheel.resolveSibling("python.bat"); - } - if (!python.exists()) { - python = delvewheel.resolveSibling("python.cmd"); - } - if (!python.exists()) { - python = delvewheel.getParent().resolveSibling("python.exe"); - } - if (!python.exists()) { - python = delvewheel.getParent().resolveSibling("python.bat"); - } - if (!python.exists()) { - python = delvewheel.getParent().resolveSibling("python.cmd"); - } - if (!python.exists()) { - throw new NativeLibraryToolException("Could not find Python executable next to `delvewheel` at '" + delvewheel + "'. " + DELVEWHEEL_INSTALL_INSTRUCTION); - } - return python.toString(); + return delvewheel.toString(); } @Override @@ -117,11 +98,9 @@ public void changeOrAddDependency(String oldName, String newName) throws NativeL var pb = newProcessBuilder(context); var stderr = new ByteArrayOutputStream(); pb.redirectError(pb.createRedirectToStream(stderr)); - var tempfileWithForwardSlashes = tempfile.toString().replace('\\', '/'); - String pythonExe = getDelvewheelPython(); - pb.command(pythonExe, "-c", - String.format("from delvewheel import _dll_utils; _dll_utils.replace_needed('%s', ['%s'], {'%s': '%s'}, strip=True, verbose=2, test=[])", - tempfileWithForwardSlashes, oldName, oldName, newName)); + String delvewheel = getDelvewheel(); + pb.command(delvewheel, "replace-needed", "-v", "-v", "--strip", "-change", oldName, newName, + tempfile.toString()); Process proc; try { proc = pb.start(); diff --git a/mx.graalpython/mx_graalpython.py b/mx.graalpython/mx_graalpython.py index bf5449eb6d..ac54b4337e 100644 --- a/mx.graalpython/mx_graalpython.py +++ b/mx.graalpython/mx_graalpython.py @@ -826,7 +826,7 @@ def get_path_with_patchelf(): mx.log(f"{time.strftime('[%H:%M:%S] ')} Building delvewheel-venv with {sys.executable}... [delvewheel not found on PATH]") t0 = time.time() subprocess.check_call([sys.executable, "-m", "venv", str(venv)]) - subprocess.check_call([str(venv / "Scripts" / "pip.exe"), "install", "delvewheel"]) + subprocess.check_call([str(venv / "Scripts" / "pip.exe"), "install", "delvewheel>=1.13.0"]) mx.log(f"{time.strftime('[%H:%M:%S] ')} Building delvewheel-venv with {sys.executable}... [duration: {time.time() - t0}]") return path From d717dff5a78088cddbcab8a71f8b14c4ba502791 Mon Sep 17 00:00:00 2001 From: Michael Simacek Date: Fri, 29 May 2026 16:19:59 +0200 Subject: [PATCH 2/4] Use graalpy for delvewheel-venv if cpython is too old --- mx.graalpython/mx_graalpython.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/mx.graalpython/mx_graalpython.py b/mx.graalpython/mx_graalpython.py index ac54b4337e..81ed8b5c42 100644 --- a/mx.graalpython/mx_graalpython.py +++ b/mx.graalpython/mx_graalpython.py @@ -808,7 +808,7 @@ def _dev_pythonhome(): return os.path.join(SUITE.dir, "graalpython") -def get_path_with_patchelf(): +def get_path_with_patchelf(graalpy=None): path = os.environ.get("PATH", "") if mx.is_linux() and not shutil.which("patchelf"): venv = Path(SUITE.get_output_root()).absolute() / "patchelf-venv" @@ -823,11 +823,15 @@ def get_path_with_patchelf(): venv = Path(SUITE.get_output_root()).absolute() / "delvewheel-venv" path += os.pathsep + str(venv / "Scripts") if not shutil.which("delvewheel", path=path): - mx.log(f"{time.strftime('[%H:%M:%S] ')} Building delvewheel-venv with {sys.executable}... [delvewheel not found on PATH]") + if sys.version_info < (3, 12) and graalpy: + venv_python = [graalpy, "-X", "jit=0"] + else: + venv_python = [sys.executable] + mx.log(f"{time.strftime('[%H:%M:%S] ')} Building delvewheel-venv with {shlex.join(venv_python)}... [delvewheel not found on PATH]") t0 = time.time() - subprocess.check_call([sys.executable, "-m", "venv", str(venv)]) + subprocess.check_call(venv_python + ["-m", "venv", str(venv)]) subprocess.check_call([str(venv / "Scripts" / "pip.exe"), "install", "delvewheel>=1.13.0"]) - mx.log(f"{time.strftime('[%H:%M:%S] ')} Building delvewheel-venv with {sys.executable}... [duration: {time.time() - t0}]") + mx.log(f"{time.strftime('[%H:%M:%S] ')} Building delvewheel-venv with {shlex.join(venv_python)}... [duration: {time.time() - t0}]") return path @@ -1841,8 +1845,8 @@ def graalpython_gate_runner(_, tasks): with Task('GraalPython multi-context unittests', tasks, tags=[GraalPythonTags.unittest_multi]) as task: if task: env = os.environ.copy() - env['PATH'] = get_path_with_patchelf() graalpy = graalpy_standalone_jvm() + env['PATH'] = get_path_with_patchelf(graalpy) mx.log("1. Running twice without shared engine") run_python_unittests( graalpy, From 401ed432ab2c87578684ab6224553e5cdc3950bd Mon Sep 17 00:00:00 2001 From: Michael Simacek Date: Fri, 29 May 2026 20:52:00 +0200 Subject: [PATCH 3/4] Use native standalone for multi-context tests for performance reasons --- ci.jsonnet | 4 ++-- mx.graalpython/mx_graalpython.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ci.jsonnet b/ci.jsonnet index 186acf8b51..1eb8fa7fcd 100644 --- a/ci.jsonnet +++ b/ci.jsonnet @@ -180,10 +180,10 @@ "linux:aarch64:jdk-latest" : daily + t("01:00:00"), "darwin:aarch64:jdk-latest" : daily + t("01:00:00"), }), - "python-unittest-multi-context": gpgate + require(GPY_JVM_STANDALONE) + platform_spec(no_jobs) + platform_spec({ + "python-unittest-multi-context": gpgate + require(GPY_NATIVE_STANDALONE) + platform_spec(no_jobs) + platform_spec({ "linux:amd64:jdk-latest" : tier3, "linux:aarch64:jdk-latest" : daily + t("02:00:00"), - "windows:amd64:jdk-latest" : daily + t("01:30:00"), + "windows:amd64:jdk-latest" : daily + t("02:00:00"), }), "python-unittest-jython": gpgate + platform_spec(no_jobs) + platform_spec({ "linux:amd64:jdk21" : daily + t("00:30:00") + require(GPY_JVM21_STANDALONE), diff --git a/mx.graalpython/mx_graalpython.py b/mx.graalpython/mx_graalpython.py index 81ed8b5c42..ed518683dc 100644 --- a/mx.graalpython/mx_graalpython.py +++ b/mx.graalpython/mx_graalpython.py @@ -1845,7 +1845,7 @@ def graalpython_gate_runner(_, tasks): with Task('GraalPython multi-context unittests', tasks, tags=[GraalPythonTags.unittest_multi]) as task: if task: env = os.environ.copy() - graalpy = graalpy_standalone_jvm() + graalpy = graalpy_standalone_native() env['PATH'] = get_path_with_patchelf(graalpy) mx.log("1. Running twice without shared engine") run_python_unittests( From dd0599cface799cc26b3f8b6eb2ba8ce8096c210 Mon Sep 17 00:00:00 2001 From: Michael Simacek Date: Mon, 8 Jun 2026 15:27:05 +0200 Subject: [PATCH 4/4] Use standalone for delvewheel in junit job as well --- mx.graalpython/mx_graalpython.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mx.graalpython/mx_graalpython.py b/mx.graalpython/mx_graalpython.py index ed518683dc..807be45eed 100644 --- a/mx.graalpython/mx_graalpython.py +++ b/mx.graalpython/mx_graalpython.py @@ -823,7 +823,9 @@ def get_path_with_patchelf(graalpy=None): venv = Path(SUITE.get_output_root()).absolute() / "delvewheel-venv" path += os.pathsep + str(venv / "Scripts") if not shutil.which("delvewheel", path=path): - if sys.version_info < (3, 12) and graalpy: + if sys.version_info < (3, 12): + if graalpy is None: + graalpy = graalpy_standalone_jvm() venv_python = [graalpy, "-X", "jit=0"] else: venv_python = [sys.executable]