Skip to content

Commit 28f6160

Browse files
committed
Allow users to opt-out of early failure on unsupported platforms.
1 parent 19d6928 commit 28f6160

File tree

8 files changed

+106
-39
lines changed

8 files changed

+106
-39
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
This changelog summarizes major changes between GraalVM versions of the Python
44
language runtime. The main focus is on user-observable behavior of the engine.
55

6+
## Version 25.0.1
7+
* Allow users to keep going on unsupported JDK/OS/ARCH combinations at their own risk by opting out of early failure using `-Dtruffle.UseFallbackRuntime=true`, `-Dpolyglot.engine.userResourceCache=/set/to/a/writeable/dir`, `-Dpolyglot.engine.allowUnsupportedPlatform=true`, and `-Dpolyglot.python.UnsupportedPlatformEmulates=[linux|macos|windows]` and `-Dorg.graalvm.python.resources.exclude=native.files`.
8+
69
## Version 25.0.0
710
* `sys.implementation.version` now returns the GraalPy version instead of the Python version it implements. Also available as `sys.graalpy_version_info` for better discoverability by people already familiar with PyPy and its `sys.pypy_version_info`.
811
* `GRAALPY_VERSION_NUM` C macro now inlcudes the release level and serial number at the end to conform to the `hexversion` format. This shouldn't break any existing comparisons.

graalpython/com.oracle.graal.python.resources/src/com/oracle/graal/python/resources/PythonResource.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ public final class PythonResource implements InternalResource {
105105
@Override
106106
public void unpackFiles(Env env, Path targetDirectory) throws IOException {
107107
OS os = env.getOS();
108-
Path osArch = Path.of(os.toString()).resolve(env.getCPUArchitecture().toString());
108+
CPUArchitecture cpuArchitecture = env.getCPUArchitecture();
109+
Path osArch = Path.of(os.toString()).resolve(cpuArchitecture.toString());
109110
ResourcesFilter filter = new ResourcesFilter();
110111
if (os.equals(OS.WINDOWS)) {
111112
env.unpackResourceFiles(BASE_PATH.resolve(LIBPYTHON_FILES), targetDirectory.resolve("Lib"), BASE_PATH.resolve(LIBPYTHON), filter);
@@ -121,7 +122,23 @@ public void unpackFiles(Env env, Path targetDirectory) throws IOException {
121122
// ni files are in the same place on all platforms
122123
env.unpackResourceFiles(BASE_PATH.resolve(NI_FILES), targetDirectory, BASE_PATH, filter);
123124
// native files already have the correct structure
124-
env.unpackResourceFiles(BASE_PATH.resolve(osArch).resolve(NATIVE_FILES), targetDirectory, BASE_PATH.resolve(osArch), filter);
125+
if (!os.equals(OS.UNSUPPORTED) && !cpuArchitecture.equals(CPUArchitecture.UNSUPPORTED)) {
126+
env.unpackResourceFiles(BASE_PATH.resolve(osArch).resolve(NATIVE_FILES), targetDirectory, BASE_PATH.resolve(osArch), filter);
127+
} else {
128+
// Native resources are not available for unsupported operating systems.
129+
if (!filter.test(Path.of(NATIVE_FILES))) {
130+
throw new IOException("Extracting native resources failed for " +
131+
osArch.toString() +
132+
". This means that GraalPy cannot use native extensions and some built-in features that rely on native code will be unavailable. " +
133+
"Currently supported platforms are linux/amd64, linux/aarch64, macos/amd64, macos/aarch64, and windows/amd64. " +
134+
"If you are running on one of these platforms and are receiving this error, that indicates a bug in this build of GraalPy. " +
135+
"If you are running on a different platform and want to keep going with this unsupported configuration, you can specify the system property \"" +
136+
ResourcesFilter.EXCLUDE_PROP +
137+
"\" with a pattern to exclude \"" +
138+
NATIVE_FILES +
139+
"\" to continue past this point. ");
140+
}
141+
}
125142

126143
if (filter.log) {
127144
System.out.println("unpacked python resources:");
@@ -153,8 +170,7 @@ private static class ResourcesFilter implements Predicate<Path> {
153170
private final List<String> included;
154171
private static final String INCLUDE_PROP = "org.graalvm.python.resources.include";
155172
private static final String EXCLUDE_PROP = "org.graalvm.python.resources.exclude";
156-
157-
private static final String LOG_PROP = "org.graalvm.python.resources.exclude";
173+
private static final String LOG_PROP = "org.graalvm.python.resources.log";
158174

159175
private ResourcesFilter() {
160176
include = getProperty(INCLUDE_PROP);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonOS.java

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,23 @@
4242

4343
import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached;
4444

45+
import java.util.Locale;
46+
47+
import org.graalvm.nativeimage.ImageInfo;
48+
49+
import com.oracle.graal.python.PythonLanguage;
50+
import com.oracle.graal.python.runtime.PythonOptions;
51+
import com.oracle.truffle.api.exception.AbstractTruffleException;
4552
import com.oracle.truffle.api.strings.TruffleString;
4653

4754
public enum PythonOS {
48-
PLATFORM_JAVA("java", "Java"),
49-
PLATFORM_CYGWIN("cygwin", "CYGWIN"),
5055
PLATFORM_LINUX("linux", "Linux"),
5156
PLATFORM_DARWIN("darwin", "Darwin"),
5257
PLATFORM_WIN32("win32", "Windows"),
53-
PLATFORM_SUNOS("sunos", "SunOS"),
54-
PLATFORM_FREEBSD("freebsd", "FreeBSD"),
5558
PLATFORM_ANY(null, null);
5659

60+
public static final String SUPPORTED_PLATFORMS = "linux/amd64, linux/aarch64, macos/amd64, macos/aarch64, and windows/amd64";
61+
5762
private final TruffleString name;
5863
private final TruffleString uname;
5964

@@ -73,28 +78,62 @@ public TruffleString getUname() {
7378
private static final PythonOS current;
7479

7580
static {
76-
String property = System.getProperty("os.name");
77-
PythonOS os = PLATFORM_JAVA;
78-
if (property != null) {
79-
property = property.toLowerCase();
80-
if (property.contains("cygwin")) {
81-
os = PLATFORM_CYGWIN;
82-
} else if (property.contains("linux")) {
83-
os = PLATFORM_LINUX;
84-
} else if (property.contains("mac")) {
85-
os = PLATFORM_DARWIN;
86-
} else if (property.contains("windows")) {
87-
os = PLATFORM_WIN32;
88-
} else if (property.contains("sunos")) {
89-
os = PLATFORM_SUNOS;
90-
} else if (property.contains("freebsd")) {
91-
os = PLATFORM_FREEBSD;
92-
}
81+
String property = System.getProperty("os.name", "").toLowerCase(Locale.ENGLISH);
82+
if (property.contains("linux")) {
83+
current = PLATFORM_LINUX;
84+
} else if (property.contains("mac") || property.contains("darwin")) {
85+
current = PLATFORM_DARWIN;
86+
} else if (property.contains("windows")) {
87+
current = PLATFORM_WIN32;
88+
} else {
89+
current = PLATFORM_ANY;
9390
}
94-
current = os;
9591
}
9692

9793
public static PythonOS getPythonOS() {
94+
if (current == PLATFORM_ANY) {
95+
if (ImageInfo.inImageBuildtimeCode()) {
96+
throw new RuntimeException("Native images with GraalPy are only supported on " + SUPPORTED_PLATFORMS + ".");
97+
}
98+
String emulated = PythonLanguage.get(null).getEngineOption(PythonOptions.UnsupportedPlatformEmulates);
99+
if (!emulated.isEmpty()) {
100+
switch (emulated) {
101+
case "linux":
102+
return PLATFORM_LINUX;
103+
case "macos":
104+
return PLATFORM_DARWIN;
105+
case "windows":
106+
return PLATFORM_WIN32;
107+
default:
108+
throw new UnsupportedPlatform("UnsupportedPlatformEmulates must be exactly one of \"linux\", \"macos\", or \"windows\"");
109+
}
110+
} else {
111+
throw new UnsupportedPlatform("This platform is not currently supported. " +
112+
"Currently supported platforms are " + SUPPORTED_PLATFORMS + ". " +
113+
"If you are running on one of these platforms and are receiving this error, that indicates a bug in this build of GraalPy. " +
114+
"If you are running on a different platform and accept that any functionality that interacts with the system may be " +
115+
"incorrect and Python native extensions will not work, you can specify the system property \"UnsupportedPlatformEmulates\" " +
116+
"with a value of either \"linux\", \"macos\", or \"windows\" to continue further and have GraalPy behave as if it were running on " +
117+
"the OS specified. Loading native libraries will not work and should be disabled using the context options. " +
118+
"See https://www.graalvm.org/python/docs/ for details on GraalPy modules with both native and Java backends, " +
119+
"and https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/Context.Builder.html to learn about disallowing native access.");
120+
}
121+
}
98122
return current;
99123
}
124+
125+
public static void throwIfUnsupported(String msg) {
126+
if (current == PLATFORM_ANY) {
127+
throw new UnsupportedPlatform(msg +
128+
"\nThis point was reached as earlier platform checks were overridden using the system property UnsupportedPlatformEmulates");
129+
}
130+
}
131+
132+
public static final class UnsupportedPlatform extends AbstractTruffleException {
133+
public UnsupportedPlatform(String msg) {
134+
super(msg);
135+
}
136+
137+
private static final long serialVersionUID = 1L;
138+
}
100139
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ private static PSimpleNamespace makeImplementation(PythonLanguage language, PTup
481481
public void initialize(Python3Core core) {
482482
PythonLanguage language = core.getLanguage();
483483
StructSequence.initType(core, VERSION_INFO_DESC);
484-
if (PythonOS.getPythonOS() == PLATFORM_WIN32) {
484+
if (getPythonOS() == PLATFORM_WIN32) {
485485
StructSequence.initType(core, WINDOWS_VER_DESC);
486486
}
487487
StructSequence.initType(core, FLAGS_DESC);
@@ -569,7 +569,7 @@ public void initialize(Python3Core core) {
569569
// tarballs, not git
570570
addBuiltinConstant("_git", PFactory.createTuple(language, new Object[]{T_GRAALPYTHON_ID, T_EMPTY_STRING, T_EMPTY_STRING}));
571571

572-
if (PythonOS.getPythonOS() == PLATFORM_WIN32) {
572+
if (os == PLATFORM_WIN32) {
573573
addBuiltinConstant("_vpath", "");
574574
}
575575

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstants.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import java.util.HashMap;
4444
import java.util.Map;
4545

46+
import com.oracle.graal.python.builtins.PythonOS;
4647
import com.oracle.truffle.api.CompilerDirectives;
4748

4849
public final class PosixConstants {
@@ -56,21 +57,19 @@ public final class PosixConstants {
5657
public static final boolean IS_WIN32;
5758

5859
static {
59-
String os = System.getProperty("os.name");
60-
if (os.contains("Linux")) {
60+
if (PythonOS.getPythonOS() == PythonOS.PLATFORM_LINUX) {
6161
IS_LINUX = true;
6262
IS_DARWIN = false;
6363
IS_WIN32 = false;
64-
} else if (os.contains("Mac")) {
64+
} else if (PythonOS.getPythonOS() == PythonOS.PLATFORM_DARWIN) {
6565
IS_LINUX = false;
6666
IS_DARWIN = true;
6767
IS_WIN32 = false;
68-
} else if (os.contains("Windows")) {
68+
} else {
69+
assert PythonOS.getPythonOS() == PythonOS.PLATFORM_WIN32;
6970
IS_LINUX = false;
7071
IS_DARWIN = false;
7172
IS_WIN32 = true;
72-
} else {
73-
throw new RuntimeException("Unsupported platform " + os);
7473
}
7574
}
7675

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixResources.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -54,11 +54,11 @@
5454
import java.util.Collections;
5555
import java.util.HashMap;
5656
import java.util.List;
57-
import java.util.Locale;
5857
import java.util.Map;
5958
import java.util.SortedMap;
6059
import java.util.TreeMap;
6160

61+
import com.oracle.graal.python.builtins.PythonOS;
6262
import com.oracle.graal.python.util.SuppressFBWarnings;
6363
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
6464
import com.oracle.truffle.api.TruffleFile;
@@ -223,12 +223,11 @@ protected PosixResources(PythonContext context) {
223223
files = Collections.synchronizedSortedMap(new TreeMap<>());
224224
filePaths = Collections.synchronizedMap(new HashMap<>());
225225
children = Collections.synchronizedList(new ArrayList<>());
226-
String osProperty = System.getProperty("os.name");
227226

228227
files.put(FD_STDIN, ChannelWrapper.createForStandardStream());
229228
files.put(FD_STDOUT, ChannelWrapper.createForStandardStream());
230229
files.put(FD_STDERR, ChannelWrapper.createForStandardStream());
231-
if (osProperty != null && osProperty.toLowerCase(Locale.ENGLISH).contains("win")) {
230+
if (PythonOS.getPythonOS() == PythonOS.PLATFORM_WIN32) {
232231
filePaths.put(FD_STDIN, "STDIN");
233232
filePaths.put(FD_STDOUT, "STDOUT");
234233
filePaths.put(FD_STDERR, "STDERR");

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import static com.oracle.graal.python.builtins.PythonOS.PLATFORM_DARWIN;
2929
import static com.oracle.graal.python.builtins.PythonOS.PLATFORM_WIN32;
3030
import static com.oracle.graal.python.builtins.PythonOS.getPythonOS;
31+
import static com.oracle.graal.python.builtins.PythonOS.throwIfUnsupported;
3132
import static com.oracle.graal.python.builtins.modules.SysModuleBuiltins.T_CACHE_TAG;
3233
import static com.oracle.graal.python.builtins.modules.SysModuleBuiltins.T__MULTIARCH;
3334
import static com.oracle.graal.python.builtins.modules.io.IONodes.T_CLOSED;
@@ -222,7 +223,7 @@ public final class PythonContext extends Python3Core {
222223
public static String getSupportLibName(PythonOS os, String libName) {
223224
// note: this should be aligned with MX's "lib" substitution
224225
return switch (os) {
225-
case PLATFORM_LINUX, PLATFORM_FREEBSD, PLATFORM_SUNOS -> J_LIB_PREFIX + libName + J_EXT_SO;
226+
case PLATFORM_LINUX -> J_LIB_PREFIX + libName + J_EXT_SO;
226227
case PLATFORM_DARWIN -> J_LIB_PREFIX + libName + J_EXT_DYLIB;
227228
case PLATFORM_WIN32 -> libName + J_EXT_DLL;
228229
default -> libName;
@@ -231,6 +232,10 @@ public static String getSupportLibName(PythonOS os, String libName) {
231232

232233
@TruffleBoundary
233234
public static String getSupportLibName(String libName) {
235+
throwIfUnsupported("Trying to load a native library on an unsupported platform. " +
236+
"This is not possible and will fail. " +
237+
"Ensure that native access is disallowed for this context and configure GraalPy to use Java backends where possible. " +
238+
"Refer to https://www.graalvm.org/python/docs/ for more information on native and Java module backends.");
234239
return getSupportLibName(getPythonOS(), libName);
235240
}
236241

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ public static void checkBytecodeDSLEnv() {
103103
"Determines default values for the CoreHome, StdLibHome, SysBasePrefix, SysPrefix.", usageSyntax = "<path>", stability = OptionStability.STABLE) //
104104
public static final OptionKey<String> PythonHome = new OptionKey<>("");
105105

106+
@EngineOption @Option(category = OptionCategory.EXPERT, help = "Allow running on unsupported platforms, making GraalPy behave as if running on macOS, Windows, or Linux. " +
107+
"This option is useful to run GraalPy on platforms with a compliant Java implementation, but without native support for GraalPy. " +
108+
"When this option is set, native libraries cannot be loaded and the Java backends must be used for common operating system libraries provided by Python.", //
109+
usageSyntax = "windows|macos|linux", stability = OptionStability.STABLE) //
110+
public static final OptionKey<String> UnsupportedPlatformEmulates = new OptionKey<>("");
111+
106112
@Option(category = OptionCategory.USER, help = "Set the location of sys.prefix. Overrides any environment variables or Java options.", usageSyntax = "<path>", stability = OptionStability.STABLE) //
107113
public static final OptionKey<TruffleString> SysPrefix = new OptionKey<>(T_EMPTY_STRING, TS_OPTION_TYPE);
108114

0 commit comments

Comments
 (0)