Skip to content

Commit 123e52d

Browse files
committed
[GR-76801] Fix building a GraalVM enterprise JDK when the sibling suite is not called graal-enterprise.
PullRequest: graalpython/4675
2 parents 82a1db4 + 893d382 commit 123e52d

3 files changed

Lines changed: 156 additions & 87 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"runtime": "graalos/graalos_prod_pkeyson_sandboxon-runtime-2026_06_21_v1.0.0_13143_gdf587994b631-1.el8.x86_64.tar.gz",
3+
"toolchain": "graal/graalvm-graalos-java25-linux-amd64-25.1.3-dev-g03c51fe.tar.gz"
4+
}

mx.graalpython/mx_graalpython.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import os
3535
import pathlib
3636
import re
37+
import signal
3738
import shlex
3839
import shutil
3940
import subprocess
@@ -1280,7 +1281,7 @@ def graalvm_jdk(enterprise=False):
12801281

12811282
jdk_major_version = mx.get_jdk().version.parts[0]
12821283
if enterprise:
1283-
mx_args = ['-p', os.path.join(mx.suite('truffle').dir, '..', '..', 'graal-enterprise', 'vm-enterprise'), '--env', 'ee']
1284+
mx_args = ['-p', str(next(Path(mx.suite('truffle').dir).resolve().parent.parent.glob('*/vm-enterprise'))), '--env', 'ee']
12841285
edition = ""
12851286
else:
12861287
mx_args = ['-p', os.path.join(mx.suite('truffle').dir, '..', 'vm'), '--env', 'ce']
@@ -1989,7 +1990,17 @@ def graalpython_gate_runner(_, tasks):
19891990

19901991
with Task('GraalPython GraalOS standalone build on SVM', tasks, tags=[GraalPythonTags.svm_graalos_standalone_build]) as task:
19911992
if task:
1992-
mx_graalpython_graalos.graalpy_graalos_standalone_build_and_test(report=report())
1993+
branch = _normalize_branch_name(os.environ.get("TO_BRANCH") or SUITE.vc.active_branch(SUITE.dir, abortOnError=False) or 'master')
1994+
if branch == 'master':
1995+
on_fail = mx.abort
1996+
else:
1997+
def on_fail(codeOrMessage, context=None, killsig=signal.SIGTERM):
1998+
mx.warn(codeOrMessage)
1999+
assert False, "GraalOS build and test failed"
2000+
try:
2001+
mx_graalpython_graalos.graalpy_graalos_standalone_build_and_test(report=report(), on_fail=on_fail)
2002+
except AssertionError:
2003+
pass
19932004

19942005
with Task('GraalPython tests on SVM', tasks, tags=[GraalPythonTags.svmunit]) as task:
19952006
if task:
@@ -2601,6 +2612,8 @@ def update_import_cmd(args):
26012612
shutil.copy(truffle_repo / "common.json", repo / "ci" / "graal" / "common.json")
26022613
shutil.copytree(truffle_repo / "ci", repo / "ci" / "graal" / "ci", dirs_exist_ok=True)
26032614

2615+
mx_graalpython_graalos.update_graalos_versions()
2616+
26042617
if args.rota:
26052618
import_updated = _commit_if_dirty(vc, repo, "Update imports")
26062619
_apply_github_unittest_tags(no_commit=True)

mx.graalpython/mx_graalpython_graalos.py

Lines changed: 137 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -45,156 +45,206 @@
4545
import gzip
4646
import json
4747
import os
48+
import re
4849
import shutil
4950
import sys
5051
import tarfile
5152
import urllib.parse
5253
import urllib.request
54+
from pathlib import Path
5355

5456
import mx
5557
import mx_urlrewrites
56-
import mx_util
5758

5859

5960
SUITE = mx.suite('graalpython')
61+
GRAALOS_VERSIONS_PATH = Path(__file__).parent / "graalos_versions.json"
6062

6163

6264
def run(*args, **kwargs):
6365
from mx_graalpython import run
6466
return run(*args, **kwargs)
6567

6668

67-
def _download_graalos_standalone_artifact(source, target):
69+
def _load_graalos_overlay_urls(ci_overlays_dir: Path):
70+
overlay_path = ci_overlays_dir / "graalpython.jsonnet"
71+
if not overlay_path.is_file():
72+
mx.abort(f"Expected sibling ci-overlays checkout at {overlay_path}")
73+
74+
content = overlay_path.read_text(encoding="utf-8")
75+
urls = {}
76+
for env_name, version_name in (
77+
("GRAALPY_GRAALOS_TOOLCHAIN_URL", "toolchain"),
78+
("GRAALPY_GRAALOS_RUNTIME_URL", "runtime"),
79+
):
80+
match = re.search(rf'{env_name}:\s*"([^"]+)"', content)
81+
if not match:
82+
mx.abort(f"Could not find {env_name} in {overlay_path}")
83+
urls[version_name] = match.group(1)
84+
return urls
85+
86+
87+
def update_graalos_versions():
88+
ci_overlays_dir = Path(SUITE.dir).parent / "ci-overlays"
89+
overlay_urls = _load_graalos_overlay_urls(ci_overlays_dir)
90+
versions = {
91+
name: resolve_latest_graalos_artifact_name(url)
92+
for name, url in overlay_urls.items()
93+
}
94+
content = json.dumps(versions, indent=2, sort_keys=True)
95+
content += "\n"
96+
mx.update_file(GRAALOS_VERSIONS_PATH.as_posix(), content, showDiff=True)
97+
SUITE.vc.git_command(SUITE.dir, ["add", GRAALOS_VERSIONS_PATH.relative_to(SUITE.dir)], abortOnError=True)
98+
99+
100+
def resolve_latest_graalos_artifact_name(source, on_fail=mx.abort):
68101
source = mx_urlrewrites.rewriteurl(source)
69-
if "artifact/latest" in source:
70-
artifact_base_url = os.environ.get("GRAALPY_GRAALOS_ARTIFACT_BASE_URL")
71-
if not artifact_base_url:
72-
mx.abort("GRAALPY_GRAALOS_ARTIFACT_BASE_URL must be set to resolve GraalOS artifact metadata")
73-
with urllib.request.urlopen(urllib.request.Request(source, headers={"Accept": "application/json"})) as response:
74-
metadata = json.loads(response.read().decode("utf-8"))
75-
artifact_name = metadata.get("artifactName")
76-
if not artifact_name:
77-
mx.abort(f"GraalOS artifact metadata does not contain artifactName: {source}")
78-
if script := os.environ.get("ARTIFACT_DOWNLOAD_SCRIPT"):
79-
run([sys.executable, script, artifact_name, target])
80-
return
81-
source = urllib.parse.urljoin(artifact_base_url, artifact_name)
102+
with urllib.request.urlopen(urllib.request.Request(source, headers={"Accept": "application/json"})) as response:
103+
metadata = json.loads(response.read().decode("utf-8"))
104+
artifact_name = metadata.get("artifactName")
105+
if not artifact_name:
106+
on_fail(f"GraalOS artifact metadata does not contain artifactName: {source}")
107+
return artifact_name
108+
109+
110+
def load_graalos_versions(on_fail=mx.abort):
111+
try:
112+
with open(GRAALOS_VERSIONS_PATH, "r", encoding="utf-8") as fp:
113+
versions = json.load(fp)
114+
except FileNotFoundError:
115+
on_fail(f"GraalOS versions file not found: {GRAALOS_VERSIONS_PATH}")
116+
except json.JSONDecodeError as exc:
117+
on_fail(f"Could not parse GraalOS versions file {GRAALOS_VERSIONS_PATH}: {exc}")
118+
119+
for key in ("toolchain", "runtime"):
120+
value = versions.get(key)
121+
if not isinstance(value, str) or not value:
122+
on_fail(f"GraalOS versions file must contain a non-empty string for {key!r}: {GRAALOS_VERSIONS_PATH}")
123+
return versions
124+
125+
126+
def _download_graalos_standalone_artifact(artifact_name, target: Path, on_fail=mx.abort):
127+
if script := os.environ.get("ARTIFACT_DOWNLOAD_SCRIPT"):
128+
run([sys.executable, script, artifact_name, str(target)])
129+
return
130+
131+
artifact_base_url = os.environ.get("GRAALPY_GRAALOS_ARTIFACT_BASE_URL")
132+
if not artifact_base_url:
133+
on_fail("GRAALPY_GRAALOS_ARTIFACT_BASE_URL must be set to download GraalOS artifacts")
134+
135+
source = urllib.parse.urljoin(mx_urlrewrites.rewriteurl(artifact_base_url), artifact_name)
82136

83137
mx.log(f"Downloading {source} to {target}")
84138
with urllib.request.urlopen(source) as response, open(target, "wb") as fp:
85139
shutil.copyfileobj(response, fp)
86140

87141

88-
def _extract_tarball(tarball, destination, strip_components=0):
89-
if os.path.isdir(destination):
142+
def _extract_tarball(tarball: Path, destination: Path, strip_components=0, on_fail=mx.abort):
143+
if destination.is_dir():
90144
shutil.rmtree(destination)
91-
mx_util.ensure_dir_exists(destination)
145+
destination.mkdir(parents=True, exist_ok=True)
92146
if strip_components == 0:
93-
mx.Extractor.create(tarball).extract(destination)
147+
mx.Extractor.create(str(tarball)).extract(str(destination))
94148
return
95149
if strip_components < 0:
96150
mx.abort(f"strip_components must not be negative: {strip_components}")
97151

98-
with tarfile.open(tarball) as archive:
152+
with tarfile.open(str(tarball)) as archive:
99153
for member in archive:
100154
original_name = member.name
101155
stripped_name = "/".join(original_name.split("/")[strip_components:])
102156
if not stripped_name:
103157
continue
104158
if not mx.Extractor._is_sane_name(stripped_name): # pylint: disable=protected-access
105-
mx.abort(f"Refusing to extract unsafe archive entry after stripping: {original_name}")
159+
on_fail(f"Refusing to extract unsafe archive entry after stripping: {original_name}")
106160
member.name = stripped_name
107-
archive.extract(member, destination)
161+
archive.extract(member, str(destination))
108162
member.name = original_name
109163
os.utime(destination, None)
110164

111165

112-
def _find_graalos_runtime_home(runtime_root):
113-
default = os.path.join(runtime_root, "opt", "graalos")
114-
if os.path.isdir(default):
166+
def _find_graalos_runtime_home(runtime_root: Path, on_fail=mx.abort):
167+
default = runtime_root / "opt" / "graalos"
168+
if default.is_dir():
115169
return default
116170
for root, dirs, _ in os.walk(runtime_root):
117-
if os.path.basename(root) == "opt" and "graalos" in dirs:
118-
return os.path.join(root, "graalos")
119-
mx.abort(f"Could not find opt/graalos in extracted GraalOS runtime artifact: {runtime_root}")
171+
root_path = Path(root)
172+
if root_path.name == "opt" and "graalos" in dirs:
173+
return root_path / "graalos"
174+
on_fail(f"Could not find opt/graalos in extracted GraalOS runtime artifact: {runtime_root}")
120175

121176

122-
def _ensure_graalos_runtime_inputs(runtime_home):
123-
graalhost_dir = os.path.join(runtime_home, "graalhost")
177+
def _ensure_graalos_runtime_inputs(runtime_home: Path, on_fail=mx.abort):
178+
graalhost_dir = runtime_home / "graalhost"
124179
required = [
125-
os.path.join(graalhost_dir, "graalhost"),
126-
os.path.join(graalhost_dir, "libc.so"),
180+
graalhost_dir / "graalhost",
181+
graalhost_dir / "libc.so",
127182
]
128-
libbinsweep = os.path.join(graalhost_dir, "libbinsweep.so")
129-
if not os.path.exists(libbinsweep):
130-
optional_libbinsweep = os.path.join(graalhost_dir, "optional", "libbinsweep.so.gz")
131-
if os.path.exists(optional_libbinsweep):
183+
libbinsweep = graalhost_dir / "libbinsweep.so"
184+
if not libbinsweep.exists():
185+
optional_libbinsweep = graalhost_dir / "optional" / "libbinsweep.so.gz"
186+
if optional_libbinsweep.exists():
132187
with gzip.open(optional_libbinsweep, "rb") as src, open(libbinsweep, "wb") as dst:
133188
shutil.copyfileobj(src, dst)
134189
required.append(libbinsweep)
135-
missing = [path for path in required if not os.path.exists(path)]
190+
missing = [path for path in required if not path.exists()]
136191
if missing:
137-
mx.abort("Extracted GraalOS runtime artifact is missing required files:\n" + "\n".join(missing))
192+
on_fail("Extracted GraalOS runtime artifact is missing required files:\n" + "\n".join([str(p) for p in missing]))
138193

139194

140-
def graalpy_graalos_standalone_build_and_test(report=None):
195+
def graalpy_graalos_standalone_build_and_test(report=None, on_fail=mx.abort):
141196
del report # This gate executes an in-sandbox smoke test directly instead of using the source-tree test runner.
142-
toolchain_url = os.environ.get("GRAALPY_GRAALOS_TOOLCHAIN_URL")
143-
runtime_url = os.environ.get("GRAALPY_GRAALOS_RUNTIME_URL")
144-
if not toolchain_url or not runtime_url:
145-
mx.log("Skipping GRAALPY_NATIVE_GRAALOS_STANDALONE build: GraalOS artifact URLs are not configured")
197+
artifact_base_url = os.environ.get("GRAALPY_GRAALOS_ARTIFACT_BASE_URL")
198+
if not artifact_base_url:
199+
mx.log("Skipping GRAALPY_NATIVE_GRAALOS_STANDALONE build: GRAALPY_GRAALOS_ARTIFACT_BASE_URL is not configured")
146200
return
201+
versions = load_graalos_versions(on_fail=on_fail)
147202

148-
work_dir = os.path.join(SUITE.dir, "mxbuild", "graalos-standalone-ci")
149-
if os.path.isdir(work_dir):
203+
work_dir = Path(SUITE.dir) / "mxbuild" / "graalos-standalone-ci"
204+
if work_dir.is_dir():
150205
shutil.rmtree(work_dir)
151-
mx_util.ensure_dir_exists(work_dir)
152-
153-
graalvm_tarball = os.path.join(work_dir, "graalvm.tar.gz")
154-
runtime_tarball = os.path.join(work_dir, "graalos-runtime.tar.gz")
155-
graalvm_home = os.path.join(work_dir, "graalvm")
156-
runtime_root = os.path.join(work_dir, "runtime")
157-
158-
_download_graalos_standalone_artifact(toolchain_url, graalvm_tarball)
159-
_extract_tarball(graalvm_tarball, graalvm_home, strip_components=1)
160-
musl_toolchain = os.path.join(graalvm_home, "lib", "toolchains", "musl-swcfi")
161-
if not os.path.exists(os.path.join(graalvm_home, "bin", mx.exe_suffix("java"))):
162-
mx.abort(f"Extracted GraalOS toolchain artifact does not contain bin/java: {graalvm_home}")
163-
if not os.path.isdir(musl_toolchain):
164-
mx.abort(f"Extracted GraalOS toolchain artifact does not contain musl-swcfi toolchain: {musl_toolchain}")
165-
166-
_download_graalos_standalone_artifact(runtime_url, runtime_tarball)
167-
_extract_tarball(runtime_tarball, runtime_root)
168-
graalos_runtime_home = _find_graalos_runtime_home(runtime_root)
169-
_ensure_graalos_runtime_inputs(graalos_runtime_home)
206+
work_dir.mkdir(parents=True, exist_ok=True)
207+
208+
graalvm_tarball = work_dir / "graalvm.tar.gz"
209+
runtime_tarball = work_dir / "graalos-runtime.tar.gz"
210+
graalvm_home = work_dir / "graalvm"
211+
runtime_root = work_dir / "runtime"
212+
213+
_download_graalos_standalone_artifact(versions["toolchain"], graalvm_tarball, on_fail=on_fail)
214+
_extract_tarball(graalvm_tarball, graalvm_home, strip_components=1, on_fail=on_fail)
215+
musl_toolchain = graalvm_home / "lib" / "toolchains" / "musl-swcfi"
216+
if not (graalvm_home / "bin" / mx.exe_suffix("java")).is_file():
217+
on_fail(f"Extracted GraalOS toolchain artifact does not contain bin/java: {graalvm_home}")
218+
if not musl_toolchain.is_dir():
219+
on_fail(f"Extracted GraalOS toolchain artifact does not contain musl-swcfi toolchain: {musl_toolchain}")
220+
221+
_download_graalos_standalone_artifact(versions["runtime"], runtime_tarball, on_fail=on_fail)
222+
_extract_tarball(runtime_tarball, runtime_root, on_fail=on_fail)
223+
graalos_runtime_home = _find_graalos_runtime_home(runtime_root, on_fail=on_fail)
224+
_ensure_graalos_runtime_inputs(graalos_runtime_home, on_fail=on_fail)
170225

171226
from mx_graalpython import extend_os_env, run_mx, _graalpy_launcher
172227
env = extend_os_env(
173-
JAVA_HOME=graalvm_home,
174-
MUSL_TOOLCHAIN=musl_toolchain,
175-
GRAALOS_TOOLCHAIN_PATH=musl_toolchain,
176-
GRAALOS_RUNTIME_HOME=graalos_runtime_home,
228+
JAVA_HOME=str(graalvm_home),
229+
MUSL_TOOLCHAIN=str(musl_toolchain),
230+
GRAALOS_TOOLCHAIN_PATH=str(musl_toolchain),
231+
GRAALOS_RUNTIME_HOME=str(graalos_runtime_home),
177232
NATIVE_IMAGE_EXPERIMENTAL_OPTIONS_ARE_FATAL="false",
178233
)
179-
run_mx([
234+
result = run_mx([
180235
"--multitarget=linux-amd64-musl-swcfi",
181236
"build",
182237
"--target", "GRAALPY_NATIVE_GRAALOS_STANDALONE",
183-
], env=env)
184-
185-
standalone_home = os.path.join(SUITE.dir, "mxbuild", "linux-amd64", "GRAALPY_NATIVE_GRAALOS_STANDALONE")
186-
launcher = os.path.join(standalone_home, "bin", _graalpy_launcher())
187-
if not os.path.exists(launcher):
188-
mx.abort(f"GRAALPY_NATIVE_GRAALOS_STANDALONE launcher was not built: {launcher}")
189-
190-
test_path = os.path.join(
191-
SUITE.dir,
192-
"graalpython",
193-
"com.oracle.graal.python.test",
194-
"src",
195-
"tests",
196-
"test_graalos_standalone.py",
197-
)
238+
], env=env, nonZeroIsFatal=(on_fail == mx.abort)) # pylint: disable=comparison-with-callable
239+
if result != 0:
240+
on_fail("Building GRAALPY_NATIVE_GRAALOS_STANDALONE failed")
241+
242+
standalone_home = Path(SUITE.dir) / "mxbuild" / "linux-amd64" / "GRAALPY_NATIVE_GRAALOS_STANDALONE"
243+
launcher = standalone_home / "bin" / _graalpy_launcher()
244+
if not launcher.exists():
245+
on_fail(f"GRAALPY_NATIVE_GRAALOS_STANDALONE launcher was not built: {launcher}")
246+
247+
test_path = Path(SUITE.dir) / "graalpython" / "com.oracle.graal.python.test" / "src" / "tests" / "test_graalos_standalone.py"
198248
with open(test_path, "r", encoding="utf-8") as f:
199249
smoke_test = f.read()
200250
smoke_test += """
@@ -205,4 +255,6 @@ def graalpy_graalos_standalone_build_and_test(report=None):
205255
"""
206256
smoke_test_arg = base64.b64encode(smoke_test.encode("utf-8")).decode("ascii")
207257
smoke_test_command = f"import base64; exec(base64.b64decode({smoke_test_arg!r}).decode('utf-8'))"
208-
run([launcher, "-c", smoke_test_command], env=env)
258+
result = run([str(launcher), "-c", smoke_test_command], env=env, nonZeroIsFatal=(on_fail == mx.abort)) # pylint: disable=comparison-with-callable
259+
if result != 0:
260+
on_fail("Testing GraalOS standalone failed")

0 commit comments

Comments
 (0)