From 3023f54ea07413c4f02a2b5e710d109072b1fdd4 Mon Sep 17 00:00:00 2001 From: Ivo Horak Date: Tue, 2 Sep 2025 17:30:38 +0200 Subject: [PATCH 1/5] WIP: Add support for macos launcher --- .../python/embedding/tools/vfs/VFSUtils.java | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java b/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java index 7845f99..f95894a 100644 --- a/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java +++ b/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java @@ -174,8 +174,9 @@ public final class VFSUtils { """; private static final boolean IS_WINDOWS = System.getProperty("os.name").startsWith("Windows"); + private static final boolean IS_MAC = System.getProperty("os.name").startsWith("Mac"); - public static final String LAUNCHER_NAME = IS_WINDOWS ? "graalpy.exe" : "graalpy.sh"; + public static final String LAUNCHER_NAME = IS_WINDOWS ? "graalpy.exe" : IS_MAC ? "graalpy" : "graalpy.sh"; private static final String GRAALPY_MAIN_CLASS = "com.oracle.graal.python.shell.GraalPythonMain"; @@ -871,7 +872,42 @@ private static void generateLaunchers(Launcher launcherArgs, BuildToolLog log) t Path java = Paths.get(System.getProperty("java.home"), "bin", "java"); String classpath = String.join(File.pathSeparator, launcherArgs.computeClassPath()); String extraJavaOptions = String.join(" ", GraalPyRunner.getExtraJavaOptions()); - if (!IS_WINDOWS) { + if (IS_MAC) { + var script = formatMultiline(""" + import os, shutil, struct, venv + from pathlib import Path + adjusted = os.path.dirname(os.path.dirname(venv.__path__[0])) + vl = os.path.join(adjusted, 'venv', 'scripts', 'macos', 'graalpy') + tl = os.path.join(r'%s') + os.makedirs(Path(tl).parent.absolute(), exist_ok=True) + shutil.copy(vl, tl) + cmd = r'%s --enable-native-access=ALL-UNNAMED %s -classpath "%s" %s' + pyvenvcfg = os.path.join(os.path.dirname(tl), "pyvenv.cfg") + with open(pyvenvcfg, 'w', encoding='utf-8') as f: + f.write('venvlauncher_command = ') + f.write(cmd) + """, launcherArgs.launcherPath, java, extraJavaOptions, classpath, GRAALPY_MAIN_CLASS); + File tmp; + try { + tmp = File.createTempFile("create_launcher", ".py"); + } catch (IOException e) { + throw new IOException("failed to create tmp launcher", e); + } + System.out.println("Created temporary launcher file: " + tmp); + tmp.deleteOnExit(); + try (var wr = new FileWriter(tmp, StandardCharsets.UTF_8)) { + wr.write(script); + } catch (IOException e) { + throw new IOException(String.format("failed to write tmp launcher %s", tmp), e); + } + + try { + GraalPyRunner.run(classpath, log, tmp.getAbsolutePath()); + } catch (InterruptedException e) { + throw new IOException("failed to run Graalpy launcher", e); + } + } + else if (!IS_WINDOWS) { // we do not bother checking if it exists and has correct java home, since it is // simple // to regenerate the launcher From 543643dcf5d83390453be80c533ea5caa52f1505 Mon Sep 17 00:00:00 2001 From: Ivo Horak Date: Tue, 9 Sep 2025 11:40:39 +0200 Subject: [PATCH 2/5] Adjusting venv_launcher path --- .../java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java b/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java index f95894a..116bedd 100644 --- a/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java +++ b/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java @@ -876,8 +876,7 @@ private static void generateLaunchers(Launcher launcherArgs, BuildToolLog log) t var script = formatMultiline(""" import os, shutil, struct, venv from pathlib import Path - adjusted = os.path.dirname(os.path.dirname(venv.__path__[0])) - vl = os.path.join(adjusted, 'venv', 'scripts', 'macos', 'graalpy') + vl = os.path.join(venv.__path__[0], 'scripts', 'macos', 'graalpy') tl = os.path.join(r'%s') os.makedirs(Path(tl).parent.absolute(), exist_ok=True) shutil.copy(vl, tl) From 1e8886541c06080ce463bdd456d75c2327002678 Mon Sep 17 00:00:00 2001 From: Ivo Horak Date: Fri, 12 Sep 2025 10:40:52 +0200 Subject: [PATCH 3/5] Merging generation of launchers for MacOS and Windows --- .../python/embedding/tools/vfs/VFSUtils.java | 51 ++++--------------- 1 file changed, 11 insertions(+), 40 deletions(-) diff --git a/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java b/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java index 116bedd..c7ad3e4 100644 --- a/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java +++ b/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java @@ -849,9 +849,9 @@ private static Path ensureLauncher(Launcher launcherArgs, BuildToolLog log) thro } } - private static boolean checkWinLauncherJavaPath(Path venvCfg, Path java) { + private static boolean checkPyVenvCfgFile(Path pyVenvCfg, Path java) { try { - for (String line : Files.readAllLines(venvCfg)) { + for (String line : Files.readAllLines(pyVenvCfg)) { if (line.trim().startsWith("venvlauncher_command = " + java)) { return true; } @@ -872,11 +872,16 @@ private static void generateLaunchers(Launcher launcherArgs, BuildToolLog log) t Path java = Paths.get(System.getProperty("java.home"), "bin", "java"); String classpath = String.join(File.pathSeparator, launcherArgs.computeClassPath()); String extraJavaOptions = String.join(" ", GraalPyRunner.getExtraJavaOptions()); - if (IS_MAC) { + if (IS_MAC || IS_WINDOWS) { + if (Files.exists(launcherArgs.launcherPath) && checkPyVenvCfgFile(launcherArgs.launcherPath.getParent().resolve("pyvenv.cfg"), java)) { + return; + } + var launcherFolder = IS_WINDOWS ? "nt" : "macos"; + var launcherName = IS_WINDOWS ? "graalpy.exe" : "graalpy"; var script = formatMultiline(""" import os, shutil, struct, venv from pathlib import Path - vl = os.path.join(venv.__path__[0], 'scripts', 'macos', 'graalpy') + vl = os.path.join(venv.__path__[0], 'scripts', '%s', '%s') tl = os.path.join(r'%s') os.makedirs(Path(tl).parent.absolute(), exist_ok=True) shutil.copy(vl, tl) @@ -885,7 +890,7 @@ private static void generateLaunchers(Launcher launcherArgs, BuildToolLog log) t with open(pyvenvcfg, 'w', encoding='utf-8') as f: f.write('venvlauncher_command = ') f.write(cmd) - """, launcherArgs.launcherPath, java, extraJavaOptions, classpath, GRAALPY_MAIN_CLASS); + """, launcherFolder, launcherName, launcherArgs.launcherPath, java, extraJavaOptions, classpath, GRAALPY_MAIN_CLASS); File tmp; try { tmp = File.createTempFile("create_launcher", ".py"); @@ -906,7 +911,7 @@ with open(pyvenvcfg, 'w', encoding='utf-8') as f: throw new IOException("failed to run Graalpy launcher", e); } } - else if (!IS_WINDOWS) { + else { // we do not bother checking if it exists and has correct java home, since it is // simple // to regenerate the launcher @@ -923,40 +928,6 @@ else if (!IS_WINDOWS) { } catch (IOException e) { throw new IOException(String.format("failed to create launcher %s", launcherArgs.launcherPath), e); } - } else if (!Files.exists(launcherArgs.launcherPath) - || !checkWinLauncherJavaPath(launcherArgs.launcherPath.getParent().resolve("pyenv.cfg"), java)) { - // on windows, generate a venv launcher that executes the java command - var script = formatMultiline(""" - import os, shutil, struct, venv - from pathlib import Path - vl = os.path.join(venv.__path__[0], 'scripts', 'nt', 'graalpy.exe') - tl = os.path.join(r'%s') - os.makedirs(Path(tl).parent.absolute(), exist_ok=True) - shutil.copy(vl, tl) - cmd = r'%s --enable-native-access=ALL-UNNAMED %s -classpath "%s" %s' - pyvenvcfg = os.path.join(os.path.dirname(tl), "pyvenv.cfg") - with open(pyvenvcfg, 'w', encoding='utf-8') as f: - f.write('venvlauncher_command = ') - f.write(cmd) - """, launcherArgs.launcherPath, java, extraJavaOptions, classpath, GRAALPY_MAIN_CLASS); - File tmp; - try { - tmp = File.createTempFile("create_launcher", ".py"); - } catch (IOException e) { - throw new IOException("failed to create tmp launcher", e); - } - tmp.deleteOnExit(); - try (var wr = new FileWriter(tmp, StandardCharsets.UTF_8)) { - wr.write(script); - } catch (IOException e) { - throw new IOException(String.format("failed to write tmp launcher %s", tmp), e); - } - - try { - GraalPyRunner.run(classpath, log, tmp.getAbsolutePath()); - } catch (InterruptedException e) { - throw new IOException("failed to run Graalpy launcher", e); - } } } From c37eb81969099cda740a8e5dc1d55f73dd93c7dc Mon Sep 17 00:00:00 2001 From: ivohorak Date: Wed, 17 Sep 2025 12:02:14 +0200 Subject: [PATCH 4/5] Remove left out code --- .../java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java | 1 - 1 file changed, 1 deletion(-) diff --git a/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java b/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java index c7ad3e4..d03885c 100644 --- a/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java +++ b/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java @@ -897,7 +897,6 @@ with open(pyvenvcfg, 'w', encoding='utf-8') as f: } catch (IOException e) { throw new IOException("failed to create tmp launcher", e); } - System.out.println("Created temporary launcher file: " + tmp); tmp.deleteOnExit(); try (var wr = new FileWriter(tmp, StandardCharsets.UTF_8)) { wr.write(script); From 795ef3b5d3fe5267894e488f63ca88f3de041789 Mon Sep 17 00:00:00 2001 From: ivohorak Date: Wed, 17 Sep 2025 13:57:49 +0200 Subject: [PATCH 5/5] fix: style issues --- .../python/embedding/tools/vfs/VFSUtils.java | 9 +++-- .../python/embedding/VirtualFileSystem.java | 39 ++++++++++++------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java b/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java index d03885c..5060966 100644 --- a/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java +++ b/org.graalvm.python.embedding.tools/src/main/java/org/graalvm/python/embedding/tools/vfs/VFSUtils.java @@ -873,7 +873,8 @@ private static void generateLaunchers(Launcher launcherArgs, BuildToolLog log) t String classpath = String.join(File.pathSeparator, launcherArgs.computeClassPath()); String extraJavaOptions = String.join(" ", GraalPyRunner.getExtraJavaOptions()); if (IS_MAC || IS_WINDOWS) { - if (Files.exists(launcherArgs.launcherPath) && checkPyVenvCfgFile(launcherArgs.launcherPath.getParent().resolve("pyvenv.cfg"), java)) { + if (Files.exists(launcherArgs.launcherPath) + && checkPyVenvCfgFile(launcherArgs.launcherPath.getParent().resolve("pyvenv.cfg"), java)) { return; } var launcherFolder = IS_WINDOWS ? "nt" : "macos"; @@ -890,7 +891,8 @@ private static void generateLaunchers(Launcher launcherArgs, BuildToolLog log) t with open(pyvenvcfg, 'w', encoding='utf-8') as f: f.write('venvlauncher_command = ') f.write(cmd) - """, launcherFolder, launcherName, launcherArgs.launcherPath, java, extraJavaOptions, classpath, GRAALPY_MAIN_CLASS); + """, launcherFolder, launcherName, launcherArgs.launcherPath, java, extraJavaOptions, classpath, + GRAALPY_MAIN_CLASS); File tmp; try { tmp = File.createTempFile("create_launcher", ".py"); @@ -909,8 +911,7 @@ with open(pyvenvcfg, 'w', encoding='utf-8') as f: } catch (InterruptedException e) { throw new IOException("failed to run Graalpy launcher", e); } - } - else { + } else { // we do not bother checking if it exists and has correct java home, since it is // simple // to regenerate the launcher diff --git a/org.graalvm.python.embedding/src/main/java/org/graalvm/python/embedding/VirtualFileSystem.java b/org.graalvm.python.embedding/src/main/java/org/graalvm/python/embedding/VirtualFileSystem.java index 4f161be..f74ab3f 100644 --- a/org.graalvm.python.embedding/src/main/java/org/graalvm/python/embedding/VirtualFileSystem.java +++ b/org.graalvm.python.embedding/src/main/java/org/graalvm/python/embedding/VirtualFileSystem.java @@ -147,7 +147,8 @@ private Builder() { * The value must be relative resources path, i.e., not starting with `/`, and * must use '/' as path separator regardless of the host OS. * - * @param directory The directory within Java resources + * @param directory + * The directory within Java resources * @return the builder * @since 24.2.0 */ @@ -163,7 +164,8 @@ public Builder resourceDirectory(String directory) { * Sets the file system to be case-insensitive. Defaults to true on Windows and * false elsewhere. * - * @param value the value to be set + * @param value + * the value to be set * @return the builder * @since 24.2.0 */ @@ -176,7 +178,8 @@ public Builder caseInsensitive(boolean value) { * Determines if and how much host IO is allowed outside the * {@link VirtualFileSystem}. * - * @param hostIO the host IO access level + * @param hostIO + * the host IO access level * @return the builder * @since 24.2.0 */ @@ -194,9 +197,11 @@ public Builder allowHostIO(HostIO hostIO) { * trailing separator. If that file or directory actually exists, it will not be * accessible. * - * @throws IllegalArgumentException if the provided mount point isn't absolute - * or ends with a trailing separator - * @param windowsMountPoint the mount point path + * @throws IllegalArgumentException + * if the provided mount point isn't absolute or ends with a + * trailing separator + * @param windowsMountPoint + * the mount point path * @return the builder * @since 24.2.0 */ @@ -216,9 +221,11 @@ public Builder windowsMountPoint(String windowsMountPoint) { * trailing separator. If that file or directory actually exists, it will not be * accessible. * - * @throws IllegalArgumentException if the provided mount point isn't absolute - * or ends with a trailing separator - * @param unixMountPoint the mount point path + * @throws IllegalArgumentException + * if the provided mount point isn't absolute or ends with a + * trailing separator + * @param unixMountPoint + * the mount point path * @return the builder * @since 24.2.0 */ @@ -236,7 +243,8 @@ public Builder unixMountPoint(String unixMountPoint) { * cases when for example VirtualFileSystem is on module path and * the jar containing the resources is on class path. * - * @param c the class for loading the resources + * @param c + * the class for loading the resources * @return the builder * @since 24.2.0 */ @@ -256,8 +264,9 @@ public Builder resourceLoadingClass(Class c) { * the operating system loader. Setting this filter to null denies * any extraction. Any other filter is combined with the default filter. * - * @param filter the extraction filter, where the provided path is an absolute - * path from the VirtualFileSystem. + * @param filter + * the extraction filter, where the provided path is an absolute path + * from the VirtualFileSystem. * @return the builder * @since 24.2.0 */ @@ -279,7 +288,8 @@ public Builder extractFilter(Predicate filter) { */ public VirtualFileSystem build() { if (mountPoint == null) { - mountPoint = VirtualFileSystemImpl.isWindows() ? Path.of(DEFAULT_WINDOWS_MOUNT_POINT) + mountPoint = VirtualFileSystemImpl.isWindows() + ? Path.of(DEFAULT_WINDOWS_MOUNT_POINT) : Path.of(DEFAULT_UNIX_MOUNT_POINT); } return new VirtualFileSystem(extractFilter, mountPoint, allowHostIO, resourceLoadingClass, @@ -343,7 +353,8 @@ public String getMountPoint() { /** * Closes the VirtualFileSystem and frees up potentially allocated resources. * - * @throws IOException if the resources could not be freed. + * @throws IOException + * if the resources could not be freed. */ @Override public void close() throws IOException {