Skip to content

Commit 09f7c17

Browse files
authored
Merge pull request #44 from cachedjdk/semeru-versions
Clean up ibm-semeru-openj9 and graalvm version string quirk handling
2 parents 4f50cfd + f4a09e5 commit 09f7c17

File tree

8 files changed

+84
-151
lines changed

8 files changed

+84
-151
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ repos:
1919
- id: ruff
2020
args: ["--fix"]
2121
- id: ruff-format
22-
- repo: https://github.com/executablebooks/mdformat
23-
rev: 0.7.22
22+
- repo: https://github.com/hukkin/mdformat
23+
rev: 1.0.0
2424
hooks:
2525
- id: mdformat
2626
additional_dependencies:

docs/changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ See also the section on [versioning](versioning-scheme).
1515
- `list_vendors()` and `ls-vendors` now filter vendors by OS and architecture,
1616
defaulting to the current platform.
1717

18+
### Removed
19+
20+
- The synthesized `ibm-semeru-openj9` vendor is no longer generated. Use
21+
`ibm-semeru` instead (now available directly in the upstream index).
22+
1823
## [0.5.0] - 2026-01-07
1924

2025
### Added

docs/example-graal-hello.md

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -47,31 +47,11 @@ with open("Hello.java", "w") as fp:
4747
fp.write(java_source)
4848
```
4949

50-
Let's store the keyword arguments to `cjdk.java_env()` so that we can call it
51-
several times with the same configuration.
52-
53-
```{code-cell} ipython3
54-
cjdk_config = dict(vendor="graalvm-java17", version="22.1.0")
55-
```
56-
57-
The GraalVM `native-image` command is not included in the default install, so
58-
we need to use `gu` (the GraalVM updater) to install it.
59-
60-
(On macOS, you may see warnings related to `setrlimit` in this and following
61-
steps. They can be ignored.)
62-
63-
```{code-cell} ipython3
64-
with cjdk.java_env(**cjdk_config):
65-
subprocess.run(
66-
["gu", "install", "--no-progress", "native-image"], check=True
67-
)
68-
```
69-
7050
Now let's compile the source, first with `javac` to byte code, then to a native
7151
image.
7252

7353
```{code-cell} ipython3
74-
with cjdk.java_env(**cjdk_config):
54+
with cjdk.java_env(vendor="graalvm-community", version="25.0.1"):
7555
subprocess.run(["javac", "Hello.java"], check=True)
7656
subprocess.run(["native-image", "Hello"], check=True)
7757
```

docs/vendors.md

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ SPDX-License-Identifier: MIT
77
# JDK vendors
88

99
**cjdk** allows you to choose among JDKs and JREs released from different
10-
sources. Names such as `adoptium`, `zulu-jre`, or `graalvm-java17` are used to
11-
select a particular series of JDKs. These names are referred to as "vendors",
12-
even though they do not map 1:1 to companies.
10+
sources. Names such as `adoptium`, `zulu-jre`, or `graalvm-community` are used
11+
to select a particular series of JDKs. These names are referred to as
12+
"vendors", even though they do not map 1:1 to companies.
1313

1414
If no vendor is specified, `adoptium` is used unless the environment variable
1515
[`CJDK_VENDOR`](environ-cjdk-vendor) is set to an alternative default.
@@ -19,18 +19,31 @@ If no vendor is specified, `adoptium` is used unless the environment variable
1919
The available set of vendors is determined by the [JDK index](./jdk-index.md)
2020
and is not built into **cjdk** itself.
2121

22-
Common vendors include `adopt`, `adoptium`, `temurin`, `liberica`, `zulu`, and
23-
their JRE counterparts `adopt-jre`, `adoptium-jre`, `temurin-jre`,
24-
`liberica-jre`, `zulu-jre`.
25-
26-
AdoptOpenJDK was
27-
[succeeded](https://blog.adoptium.net/2021/08/adoptium-celebrates-first-release/)
28-
by Eclipse Temurin by Adoptium in 2021. To specifically get AdoptOpenJDK
29-
releases, use `adopt`; to specifically get Temurin releases, use `temurin`;
30-
`adoptium` will get a Temurin release if available, falling back to
31-
AdoptOpenJDK for older versions. (Again, this behavior is defined by the index,
32-
not **cjdk** itself.)
33-
34-
For GraalVM, `graalvm-java11`, `graalvm-java16`, and `graalvm-java17` are
35-
available at the time of writing (these each have [versions](./versions.md)
36-
that are numbered independently of the regular JDK version).
22+
Common vendor names for full JDKs include `temurin`, `zulu`, `liberica`,
23+
`corretto`, `ibm-semeru`, and `graalvm-community`. Common vendor names for JREs
24+
include `temurin-jre`, `zulu-jre`, and `liberica-jre`.
25+
26+
```{note}
27+
**Eclipse Temurin** was
28+
[previously known](https://blog.adoptium.net/2021/08/adoptium-celebrates-first-release/)
29+
as **AdoptOpenJDK**. To specifically get AdoptOpenJDK releases, use `adopt`; to
30+
specifically get Temurin releases, use `temurin`; `adoptium` will get a Temurin
31+
release if available, falling back to AdoptOpenJDK for older versions. (This
32+
behavior is defined by the index, not **cjdk** itself.)
33+
```
34+
35+
```{note}
36+
For **GraalVM**, the recommended vendor name is `graalvm-community`, which uses
37+
Java-version-aligned numbering (e.g., version `21.0.2` is for Java 21). Legacy
38+
vendors `graalvm` (Java 8), `graalvm-java11`, `graalvm-java17`, etc., are also
39+
available; these use GraalVM release version numbers (e.g., `22.3.3`) which are
40+
independent of the Java version. The `-javaN` suffix indicates the Java
41+
version.
42+
```
43+
44+
```{note}
45+
For **IBM Semeru**, use `ibm-semeru`. There are also vendor names
46+
like `ibm-semeru-openj9-java11` (per Java major version), containing JDK
47+
versions that include an OpenJ9 VM version suffix (e.g.,
48+
`11.0.29+7_openj9-0.56.0`).
49+
```

docs/versions.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ The available versions for a given vendor (and OS, architecture) are defined by
1414
the [JDK index](./jdk-index.md). Different vendors use different numbering
1515
schemes.
1616

17+
```{attention}
18+
For legacy GraalVM vendors (`graalvm`, `graalvm-java11`, etc.), version numbers
19+
are GraalVM release versions, not Java/JDK versions. See
20+
[vendors](./vendors.md) for details.
21+
```
22+
1723
If you want to reproducibly install an exact JDK build, you should consult the
1824
index and specify an exact version in full.
1925

src/cjdk/_index.py

Lines changed: 30 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
"""
66
JDK index handling.
77
8-
Fetches and caches the Coursier JDK index, parses JSON, normalizes vendor names
9-
(e.g., merges ibm-semeru-*-java## variants), and performs version
8+
Fetches and caches the Coursier JDK index, parses JSON, and performs version
109
matching/resolution with support for version expressions like "17+".
1110
1211
No actual operations except for caching the index itself. _index should be
@@ -143,44 +142,6 @@ def _read_index(path: Path) -> Index:
143142
except json.JSONDecodeError as e:
144143
raise InstallError(f"Invalid JSON in index file {path}: {e}") from e
145144

146-
return _postprocess_index(index)
147-
148-
149-
def _postprocess_index(index: Index) -> Index:
150-
"""
151-
Post-process the index to normalize the data.
152-
153-
Some "vendors" include the major Java version,
154-
so let's merge such entries. In particular:
155-
156-
* ibm-semuru-openj9-java<##>
157-
* graalvm-java<##>
158-
159-
However: while the graalvm vendors follow this pattern, the version
160-
numbers for graalvm are *not* JDK versions, but rather GraalVM versions,
161-
which merely strongly resemble JDK version strings. For example,
162-
graalvm-java17 version 22.3.3 bundles OpenJDK 17.0.8, but
163-
unfortunately there is no way to know this from the index alone.
164-
"""
165-
166-
pattern = re.compile("^(jdk@ibm-semeru.*)-java\\d+$")
167-
if not hasattr(index, "items"):
168-
return index
169-
for os, arches in index.items():
170-
if not hasattr(arches, "items"):
171-
continue
172-
for arch, vendors in arches.items():
173-
if not hasattr(vendors, "items"):
174-
continue
175-
for vendor, versions in vendors.copy().items():
176-
if not vendor.startswith("jdk@graalvm") and (
177-
m := pattern.match(vendor)
178-
):
179-
true_vendor = m.group(1)
180-
if true_vendor not in index[os][arch]:
181-
index[os][arch][true_vendor] = {}
182-
index[os][arch][true_vendor].update(versions)
183-
184145
return index
185146

186147

@@ -235,9 +196,10 @@ def _normalize_version(
235196
ver: str, *, remove_prefix_1: bool = False
236197
) -> tuple[int | str, ...]:
237198
# Normalize requested version and candidates:
238-
# - Split at dots and dashes (so we don't distinguish between '.' and '-')
239-
# - Try to convert elements to integers (so that we can compare elements
240-
# numerically where feasible)
199+
# - Handle _openj9- as a plain separator (for ibm-semeru-openj9-java*
200+
# versions); also handle -m2 suffix
201+
# - Split at dots, dashes, plus signs, and underscores
202+
# - Convert elements to integers (raise ValueError if not possible)
241203
# - If remove_prefix_1 and first element is 1, remove it (so JDK 1.8 == 8)
242204
# - Return as a tuple (so that we compare element by element)
243205
# - Trailing zero elements are NOT removed, so, e.g., 11 < 11.0 (for the
@@ -247,24 +209,38 @@ def _normalize_version(
247209
is_plus = ver.endswith("+")
248210
if is_plus:
249211
ver = ver[:-1]
212+
plus = ("+",) if is_plus else ()
213+
214+
if "_openj9-" in ver:
215+
# ibm-semeru-openj9-java* version numbers have a variable number of
216+
# '.'-separated numbers before the '+'. Pad so that comparisons work.
217+
first, second = ver.split("+", 1)
218+
nfirst = _normalize_version(first, remove_prefix_1=remove_prefix_1)
219+
while len(nfirst) < 4:
220+
nfirst = nfirst + (0,)
221+
nsecond = _normalize_version(
222+
second.replace("-m", "-").replace("_openj9-", "-")
223+
)
224+
return nfirst + nsecond + plus
225+
250226
if ver:
251-
norm = tuple(re.split(_VER_SEPS, ver))
252-
norm = tuple(_intify(e) for e in norm)
227+
parts = re.split(_VER_SEPS, ver)
228+
norm = []
229+
for part in parts:
230+
try:
231+
norm.append(int(part))
232+
except ValueError:
233+
raise ValueError(
234+
f"Non-integer element '{part}' in version"
235+
) from None
236+
norm = tuple(norm)
253237
else:
254238
norm = ()
255-
plus = ("+",) if is_plus else ()
256239
if remove_prefix_1 and len(norm) and norm[0] == 1:
257240
return norm[1:] + plus
258241
return norm + plus
259242

260243

261-
def _intify(s: str) -> int | str:
262-
try:
263-
return int(s)
264-
except ValueError:
265-
return s
266-
267-
268244
def _is_version_compatible_with_spec(
269245
version: tuple[int | str, ...], spec: tuple[int | str, ...]
270246
) -> bool:
@@ -282,25 +258,6 @@ def _is_version_compatible_with_spec(
282258
return len(version) >= len(spec) and version[: len(spec)] == spec
283259

284260

285-
class _VersionElement:
286-
"""Wrapper for version tuple elements enabling mixed int/str comparison."""
287-
288-
def __init__(self, value: int | str) -> None:
289-
self.value = value
290-
291-
def __eq__(self, other: object) -> bool:
292-
if not isinstance(other, _VersionElement):
293-
return NotImplemented
294-
if isinstance(self.value, int) and isinstance(other.value, int):
295-
return self.value == other.value
296-
return str(self.value) == str(other.value)
297-
298-
def __lt__(self, other: _VersionElement) -> bool:
299-
if isinstance(self.value, int) and isinstance(other.value, int):
300-
return self.value < other.value
301-
return str(self.value) < str(other.value)
302-
303-
304261
def matching_jdk_versions(index: Index, conf: Configuration) -> list[str]:
305262
"""
306263
Return all version strings matching the configuration, sorted by version.
@@ -313,10 +270,4 @@ def matching_jdk_versions(index: Index, conf: Configuration) -> list[str]:
313270
if not versions:
314271
return []
315272
matched = _match_versions(conf.vendor, versions, conf.version)
316-
317-
def version_sort_key(
318-
item: tuple[tuple[int | str, ...], str],
319-
) -> tuple[_VersionElement, ...]:
320-
return tuple(_VersionElement(e) for e in item[0])
321-
322-
return [v for _, v in sorted(matched.items(), key=version_sort_key)]
273+
return [v for _, v in sorted(matched.items())]

tests/test_index.py

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -162,34 +162,6 @@ def test_read_index(tmp_path):
162162
assert _index._read_index(path) == data
163163

164164

165-
def test_postprocess_index():
166-
index = {
167-
"linux": {
168-
"amd64": {
169-
"jdk@ibm-semeru-openj9-java11": {
170-
"11.0.21+9_openj9-0.41.0": "a",
171-
"11.0.22+7_openj9-0.43.0": "b",
172-
"11.0.23+9_openj9-0.44.0": "c",
173-
},
174-
"jdk@ibm-semeru-openj9-java17": {
175-
"17.0.1+12_openj9-0.29.1": "d",
176-
"17.0.2+8_openj9-0.30.0": "e",
177-
"17.0.3+7_openj9-0.32.0": "f",
178-
},
179-
"jdk@ibm-semeru-openj9-java21": {
180-
"21.0.1+12_openj9-0.42.0": "g",
181-
"21.0.2+13_openj9-0.43.0": "h",
182-
},
183-
"jdk@not-semeru": {"8.0.252": "i"},
184-
}
185-
}
186-
}
187-
pp_index = _index._postprocess_index(index)
188-
assert pp_index is index
189-
assert "jdk@ibm-semeru-openj9" in index["linux"]["amd64"]
190-
assert len(index["linux"]["amd64"]["jdk@ibm-semeru-openj9"]) == 8
191-
192-
193165
def test_match_versions():
194166
f = _index._match_versions
195167
assert f("adoptium", ["10", "11.0", "11.1", "1.12.0"], "11") == {
@@ -238,8 +210,14 @@ def test_normalize_version():
238210
assert f("1", remove_prefix_1=True) == ()
239211
assert f("1.8", remove_prefix_1=True) == (8,)
240212
assert f("1.8.0", remove_prefix_1=True) == (8, 0)
241-
assert f("1.8u300", remove_prefix_1=True) == ("8u300",)
242-
assert f("21.0.1+12_openj9-0.42.0") == (21, 0, 1, 12, "openj9", 0, 42, 0)
213+
assert f("17.0.4.1+1_openj9-0.33.1") == (17, 0, 4, 1, 1, 0, 33, 1)
214+
assert f("21.0.1+12_openj9-0.42.0") == (21, 0, 1, 0, 12, 0, 42, 0)
215+
assert f("23+37_openj9-0.47.0.0.0") == (23, 0, 0, 0, 37, 0, 47, 0, 0, 0)
216+
assert f("23.0.1+11_openj9-0.49.0-m2") == (23, 0, 1, 0, 11, 0, 49, 0, 2)
217+
with pytest.raises(ValueError):
218+
f("23.4.5_openj9-42") # No '+' despite having _openj9-
219+
with pytest.raises(ValueError):
220+
f("1.8u300") # No longer seen in index
243221

244222

245223
def test_is_version_compatible_with_spec():

tests/test_integration.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def test_list_vendors():
1111
assert "adoptium" in vendors
1212
assert "corretto" in vendors
1313
assert "graalvm" in vendors
14-
assert "ibm-semeru-openj9" in vendors
14+
assert "ibm-semeru" in vendors
1515
assert "java-oracle" in vendors
1616
assert "liberica" in vendors
1717
assert "temurin" in vendors

0 commit comments

Comments
 (0)