Skip to content

Commit be97e4f

Browse files
committed
[GR-68157] Allow users to opt-out of early failure on unsupported platforms.
PullRequest: graalpython/3940
2 parents 5d7649a + 4dbd6f4 commit be97e4f

File tree

13 files changed

+216
-64
lines changed

13 files changed

+216
-64
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
name: Test on exotic platforms
2+
on:
3+
schedule:
4+
- cron: '0 0 * * 1'
5+
workflow_dispatch:
6+
7+
jobs:
8+
centos-ppc64le-test:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
13+
- name: Build maven artifacts
14+
run: |
15+
sudo apt-get update
16+
sudo apt-get install -y git cmake build-essential python3 maven
17+
git clone --depth=1 https://github.com/graalvm/mx.git
18+
export PATH=$PWD/mx:$PATH
19+
export NATIVE_IMAGES=''
20+
mx sforceimport
21+
mx -p ../graal/vm fetch-jdk -A --jdk-id labsjdk-ce-latest
22+
export JAVA_HOME="$HOME/.mx/jdks/labsjdk-ce-latest/"
23+
mx deploy-local-maven-repo
24+
mv mxbuild/jdk*/mx.graalpython/public-maven-repo m2repo
25+
26+
- name: Test on OpenJ9
27+
run: |
28+
# Install IBM Semeru OpenJ9 Java 21
29+
wget https://github.com/ibmruntimes/semeru21-binaries/releases/download/jdk-21.0.8%2B9_openj9-0.53.0/ibm-semeru-open-jdk_x64_linux_21.0.8_9_openj9-0.53.0.tar.gz
30+
mkdir -p /opt/java/openjdk-21-openj9
31+
tar -C /opt/java/openjdk-21-openj9 --strip-components=1 -xzf ibm-semeru*.tar.gz
32+
export JAVA_HOME=/opt/java/openjdk-21-openj9
33+
34+
export PATH=$JAVA_HOME/bin:$PATH
35+
mkdir $(pwd)/user_resource_cache
36+
mvn -f graalpython/com.oracle.graal.python.test.integration/pom.xml -Dcom.oracle.graal.python.test.polyglot.version=26.0.0 -Dcom.oracle.graal.python.test.polyglot_repo=file:///$(pwd)/m2repo --batch-mode -U -Dtruffle.UseFallbackRuntime=true -Dpolyglot.engine.allowUnsupportedPlatform=true -Dpolyglot.engine.userResourceCache=/$(pwd)/user_resource_cache -Dpolyglot.python.UnsupportedPlatformEmulates=linux -Dorg.graalvm.python.resources.exclude=native.files test -Dtest=HelloWorldTests,AttributeTests,BuiltinSubclassTest,ComplexTexts,CreateClassTest,AsyncActionThreadingTest,JavaInteropTest
37+
rm -rf $(pwd)/user_resource_cache
38+
39+
- name: Test on ppc64le
40+
uses: uraimo/run-on-arch-action@v3
41+
with:
42+
arch: ppc64le
43+
distro: ubuntu_latest
44+
dockerRunArgs: |
45+
--volume "${GITHUB_WORKSPACE}:/workspace"
46+
--memory-reservation 6G
47+
run: |
48+
apt-get update
49+
apt-get install -y python3 git wget tar gzip cmake build-essential maven openjdk-21-jdk
50+
51+
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-ppc64el
52+
export PATH=$JAVA_HOME/bin:$PATH
53+
54+
echo "Using $JAVA_HOME"
55+
$JAVA_HOME/bin/java -version
56+
57+
# Run some basic smoke tests
58+
cd /workspace
59+
mkdir $(pwd)/user_resource_cache
60+
mvn -f graalpython/com.oracle.graal.python.test.integration/pom.xml -Dcom.oracle.graal.python.test.polyglot.version=26.0.0 -Dcom.oracle.graal.python.test.polyglot_repo=file:///$(pwd)/m2repo --batch-mode -U -Dtruffle.UseFallbackRuntime=true -Dpolyglot.engine.allowUnsupportedPlatform=true -Dpolyglot.engine.userResourceCache=/$(pwd)/user_resource_cache -Dpolyglot.python.UnsupportedPlatformEmulates=linux -Dorg.graalvm.python.resources.exclude=native.files test -Dtest=HelloWorldTests,AttributeTests,BuiltinSubclassTest,ComplexTexts,CreateClassTest,AsyncActionThreadingTest,JavaInteropTest
61+
rm -rf $(pwd)/user_resource_cache

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: 28 additions & 5 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);
@@ -211,7 +227,14 @@ private static String getProperty(String prop) {
211227
@Override
212228
public String versionHash(Env env) {
213229
StringBuilder sb = new StringBuilder();
214-
for (var s : List.of(LIBGRAALPY_SHA256, LIBPYTHON_SHA256, NI_SHA256, INCLUDE_SHA256, Path.of(env.getOS().toString()).resolve(env.getCPUArchitecture().toString()).resolve(NATIVE_SHA256))) {
230+
List<Path> al = List.of(LIBGRAALPY_SHA256, LIBPYTHON_SHA256, NI_SHA256, INCLUDE_SHA256);
231+
OS os = env.getOS();
232+
CPUArchitecture cpuArchitecture = env.getCPUArchitecture();
233+
if (!os.equals(OS.UNSUPPORTED) && !cpuArchitecture.equals(CPUArchitecture.UNSUPPORTED)) {
234+
al = new ArrayList<>(al);
235+
al.add(Path.of(os.toString()).resolve(cpuArchitecture.toString()).resolve(NATIVE_SHA256));
236+
}
237+
for (var s : al) {
215238
try {
216239
sb.append(env.readResourceLines(BASE_PATH.resolve(s)).get(0).substring(0, 8));
217240
} catch (IOException | IndexOutOfBoundsException | InvalidPathException e) {

graalpython/com.oracle.graal.python.test/src/tests/test_posix.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -912,14 +912,18 @@ def test_sysconf(self):
912912
else:
913913
assert False
914914

915+
def sysconf_max(name):
916+
value = os.sysconf(name)
917+
return sys.maxsize - value if value < 0 else value
918+
915919
# constants taken from POSIX where defined
916-
self.assertGreaterEqual(os.sysconf('SC_ARG_MAX'), 4096)
917-
self.assertGreaterEqual(os.sysconf('SC_CHILD_MAX'), 25)
918-
self.assertGreaterEqual(os.sysconf('SC_LOGIN_NAME_MAX'), 9)
920+
self.assertGreaterEqual(sysconf_max('SC_ARG_MAX'), 4096)
921+
self.assertGreaterEqual(sysconf_max('SC_CHILD_MAX'), 25)
922+
self.assertGreaterEqual(sysconf_max('SC_LOGIN_NAME_MAX'), 9)
919923
self.assertGreaterEqual(os.sysconf('SC_CLK_TCK'), 0)
920-
self.assertGreaterEqual(os.sysconf('SC_OPEN_MAX'), 20)
924+
self.assertGreaterEqual(sysconf_max('SC_OPEN_MAX'), 20)
921925
self.assertGreaterEqual(os.sysconf('SC_PAGESIZE'), 1)
922-
os.sysconf('SC_SEM_NSEMS_MAX') # returns -1 on my linux box, just check it's there
926+
self.assertGreaterEqual(sysconf_max('SC_SEM_NSEMS_MAX'), 32)
923927
self.assertGreaterEqual(os.sysconf('SC_PHYS_PAGES'), 1)
924928
self.assertGreaterEqual(os.sysconf('SC_NPROCESSORS_CONF'), 1)
925929

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959

6060
import com.oracle.graal.python.builtins.Python3Core;
6161
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
62+
import com.oracle.graal.python.builtins.PythonOS;
6263
import com.oracle.graal.python.builtins.modules.MarshalModuleBuiltins;
6364
import com.oracle.graal.python.builtins.modules.SignalModuleBuiltins;
6465
import com.oracle.graal.python.builtins.objects.PNone;
@@ -460,7 +461,6 @@ protected boolean patchContext(PythonContext context, Env newEnv) {
460461
@Override
461462
protected PythonContext createContext(Env env) {
462463
final PythonContext context = new PythonContext(this, env);
463-
context.initializeHomeAndPrefixPaths(env, getLanguageHome());
464464

465465
Object[] engineOptionsUnroll = this.engineOptionsStorage;
466466
if (engineOptionsUnroll == null) {
@@ -476,13 +476,6 @@ protected PythonContext createContext(Env env) {
476476
assert areOptionsCompatible(options, PythonOptions.createEngineOptions(env)) : "invalid engine options";
477477
}
478478

479-
if (allocationReporter == null) {
480-
allocationReporter = env.lookup(AllocationReporter.class);
481-
} else {
482-
// GR-61960
483-
// assert allocationReporter == env.lookup(AllocationReporter.class);
484-
}
485-
486479
return context;
487480
}
488481

@@ -509,8 +502,22 @@ protected OptionDescriptors getOptionDescriptors() {
509502
@Override
510503
protected void initializeContext(PythonContext context) {
511504
if (!isLanguageInitialized) {
505+
if (allocationReporter == null) {
506+
allocationReporter = context.getEnv().lookup(AllocationReporter.class);
507+
} else {
508+
// GR-61960
509+
// assert allocationReporter == env.lookup(AllocationReporter.class);
510+
}
512511
initializeLanguage();
513512
}
513+
if (PythonOS.isUnsupported() && context.getEnv().isNativeAccessAllowed()) {
514+
LOGGER.log(Level.WARNING, "Loading native libraries into GraalPy on unsupported platforms. " +
515+
"You can ensure that native access is disallowed for this context and configure GraalPy to use Java backends where possible. " +
516+
"Refer to https://www.graalvm.org/python/docs/ for more information on native and Java module backends. " +
517+
"This is not fatal, because other languages may allow native libraries to run on this platform in the same context, " +
518+
"but attempting to load a native library on GraalPy will fail.");
519+
}
520+
context.initializeHomeAndPrefixPaths(context.getEnv(), getLanguageHome());
514521
context.initialize();
515522
}
516523

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -430,9 +430,8 @@ public abstract class Python3Core {
430430
private static final TruffleString T__FROZEN_IMPORTLIB_EXTERNAL = tsLiteral("_frozen_importlib_external");
431431
private static final TruffleString T__FROZEN_IMPORTLIB = tsLiteral("_frozen_importlib");
432432
private static final TruffleString T_IMPORTLIB_BOOTSTRAP = tsLiteral("importlib._bootstrap");
433-
private final TruffleString[] coreFiles;
434433

435-
private static TruffleString[] initializeCoreFiles() {
434+
private static TruffleString[] getCoreFiles() {
436435
// Order matters!
437436
List<TruffleString> coreFiles = List.of(
438437
T___GRAALPYTHON__,
@@ -450,7 +449,7 @@ private static TruffleString[] initializeCoreFiles() {
450449
return coreFiles.toArray(new TruffleString[0]);
451450
}
452451

453-
private final PythonBuiltins[] builtins;
452+
private PythonBuiltins[] builtins;
454453

455454
public static final boolean HAS_PROFILER_TOOL;
456455
static {
@@ -865,8 +864,6 @@ private static PythonBuiltins[] initializeBuiltins(TruffleLanguage.Env env) {
865864

866865
public Python3Core(PythonLanguage language, TruffleLanguage.Env env) {
867866
this.language = language;
868-
this.builtins = initializeBuiltins(env);
869-
this.coreFiles = initializeCoreFiles();
870867
}
871868

872869
@CompilerDirectives.ValueType
@@ -944,6 +941,8 @@ public final boolean isCoreInitialized() {
944941
* Load the core library and prepare all builtin classes and modules.
945942
*/
946943
public final void initialize(PythonContext context) {
944+
assert this.builtins == null;
945+
this.builtins = initializeBuiltins(context.getEnv());
947946
initializeJavaCore();
948947
initializeImportlib();
949948
context.applyModuleOptions();
@@ -1042,7 +1041,7 @@ private void initializeImportlib() {
10421041
}
10431042

10441043
private void initializePython3Core(TruffleString coreHome) {
1045-
for (TruffleString s : coreFiles) {
1044+
for (TruffleString s : getCoreFiles()) {
10461045
loadFile(s, coreHome);
10471046
}
10481047
initialized = true;

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

Lines changed: 65 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,66 @@ 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 must 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 (isUnsupported()) {
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 boolean isUnsupported() {
133+
return current == PLATFORM_ANY;
134+
}
135+
136+
public static final class UnsupportedPlatform extends AbstractTruffleException {
137+
public UnsupportedPlatform(String msg) {
138+
super(msg);
139+
}
140+
141+
private static final long serialVersionUID = 1L;
142+
}
100143
}

0 commit comments

Comments
 (0)