diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml
index 6147061..ad1d1f9 100644
--- a/.github/workflows/benchmarks.yml
+++ b/.github/workflows/benchmarks.yml
@@ -96,8 +96,10 @@ jobs:
run: |
set -euo pipefail
ver="$(awk -F'"' '/^DEFAULT_VERSION = /{print $2}' elide/private/versions.bzl)"
- echo "Elide version: $ver"
- base="https://github.com/elide-dev/elide/releases/download/$ver"
+ tag="$(awk -F'"' -v v="$ver" '$2 == v && $4 != "" {print $4}' elide/private/versions.bzl)"
+ tag="${tag:-$ver}"
+ echo "Elide version: $ver (tag $tag)"
+ base="https://github.com/elide-dev/elide/releases/download/$tag"
curl -fsSL -o elide.tgz "$base/elide.linux-amd64.tgz"
curl -fsSL -o elide.tgz.sha256 "$base/elide.linux-amd64.tgz.sha256"
echo "$(awk '{print $1}' elide.tgz.sha256) elide.tgz" | sha256sum -c -
diff --git a/docs/extensions.md b/docs/extensions.md
index a56f52d..98eb4d3 100644
--- a/docs/extensions.md
+++ b/docs/extensions.md
@@ -26,7 +26,7 @@ elide.use(channel, channel | Release channel: nightly, preview, or release. | String | optional | `"nightly"` |
| repo_prefix | Prefix used to name per-platform download repos. | String | optional | `"elide"` |
| url_template | Override release URL template. Tokens: {channel}, {version}, {os}, {cpu}, {ext}. | String | optional | `""` |
-| version | Elide release version tag, e.g. `1.2.0+20260602`. Must match an entry in elide/private/versions.bzl. Defaults to the most-recently verified release. | String | optional | `"1.3.6+20260628"` |
+| version | Elide release version, e.g. `1.6.0-rc.2` or `1.3.6+20260628`. Must match an entry in elide/private/versions.bzl. Defaults to the most-recently verified release. | String | optional | `"1.6.0-rc.2"` |
diff --git a/elide/extensions.bzl b/elide/extensions.bzl
index cdfdc22..b0e67d0 100644
--- a/elide/extensions.bzl
+++ b/elide/extensions.bzl
@@ -60,7 +60,7 @@ _install = tag_class(
),
"version": attr.string(
default = DEFAULT_VERSION,
- doc = "Elide release version tag, e.g. `1.2.0+20260602`. " +
+ doc = "Elide release version, e.g. `1.6.0-rc.2` or `1.3.6+20260628`. " +
"Must match an entry in elide/private/versions.bzl. " +
"Defaults to the most-recently verified release.",
),
diff --git a/elide/kotlin/builder/Fallback.kt b/elide/kotlin/builder/Fallback.kt
index 43626ba..03a1e5a 100644
--- a/elide/kotlin/builder/Fallback.kt
+++ b/elide/kotlin/builder/Fallback.kt
@@ -12,18 +12,27 @@ object Fallback {
// launch flag (no env var, no `Picked up …` notice). See WHIPLASH#1131.
//
// The option only exists on JDK 23+; passing it to an older `java` launcher is
- // a fatal "Unrecognized option", so gate on the running feature version (the
- // shim and the fallback builder share the toolchain JDK).
+ // a fatal "Unrecognized option", so gate on the JDK the fallback builder runs
+ // on: the launcher's JAVA_HOME. The shim itself runs on `elide java`, whose
+ // version says nothing about that JDK.
private const val UNSAFE_SILENCER =
"--wrapper_script_flag=--jvm_flag=--sun-misc-unsafe-memory-access=allow"
internal fun unsafeSilencer(jdkFeature: Int): List =
if (jdkFeature >= 23) listOf(UNSAFE_SILENCER) else emptyList()
+ /** Feature version of the JDK at [javaHome], from its `release` file; 0 when unknown. */
+ fun jdkFeature(javaHome: String?): Int {
+ if (javaHome.isNullOrEmpty()) return 0
+ val release = File(javaHome, "release").takeIf { it.isFile } ?: return 0
+ val line = release.readLines().firstOrNull { it.startsWith("JAVA_VERSION=") } ?: return 0
+ return line.substringAfter('=').trim('"').takeWhile { it.isDigit() }.toIntOrNull() ?: 0
+ }
+
fun command(
stockBuilder: String,
flagfile: String,
- jdkFeature: Int = Runtime.version().feature(),
+ jdkFeature: Int = jdkFeature(System.getenv("JAVA_HOME")),
): List =
listOf(stockBuilder) + unsafeSilencer(jdkFeature) + "--flagfile=$flagfile"
diff --git a/elide/private/download.bzl b/elide/private/download.bzl
index 09aac4a..7446a18 100644
--- a/elide/private/download.bzl
+++ b/elide/private/download.bzl
@@ -2,7 +2,7 @@
"""Repository rule that downloads one elide release artifact for one platform."""
-load(":versions.bzl", "DEFAULT_CHANNEL", "DEFAULT_URL_TEMPLATE", "archive_ext", "binary_ext")
+load(":versions.bzl", "DEFAULT_CHANNEL", "DEFAULT_URL_TEMPLATE", "archive_ext", "binary_ext", "release_tag")
visibility(["//elide/...", "//tests/..."])
@@ -60,7 +60,7 @@ def _elide_download_impl(ctx):
cpu = ctx.attr.cpu
url = (ctx.attr.url_template or DEFAULT_URL_TEMPLATE).format(
channel = ctx.attr.channel or DEFAULT_CHANNEL,
- version = ctx.attr.version,
+ version = release_tag(ctx.attr.version),
os = os,
cpu = cpu,
ext = archive_ext(os),
@@ -94,6 +94,10 @@ def _elide_download_impl(ctx):
ctx.execute(["chmod", "+x", "bin/" + name])
for name in ["native-image", "native-image-configure"]:
ctx.execute(["chmod", "+x", "lib/svm/bin/" + name])
+
+ # The JDK's process launcher helper also lacks it; without it every
+ # subprocess started from `elide java` fails with posix_spawn EACCES.
+ ctx.execute(["chmod", "+x", "lib/jspawnhelper"])
bin_ext = binary_ext(os)
binary_path = ctx.attr.binary_path or ("bin/elide" + bin_ext)
ctx.file("BUILD.bazel", _BUILD_TEMPLATE.format(
diff --git a/elide/private/versions.bzl b/elide/private/versions.bzl
index 7623a61..61c2d18 100644
--- a/elide/private/versions.bzl
+++ b/elide/private/versions.bzl
@@ -51,6 +51,7 @@ DEFAULT_CHANNEL = "nightly"
# Hashes verified 2026-06-22 against GitHub release tag 1.3.4+20260622.
# Hashes verified 2026-06-23 against GitHub release tag 1.3.4+20260623.
# Hashes verified 2026-06-28 against GitHub release tag 1.3.6+20260628.
+# Hashes verified 2026-10-12 against GitHub release tag v1.6.0-rc.2.
ELIDE_VERSIONS = {
"1.2.0+20260602": {
("linux", "amd64"): "sha256-yIQZwU4nM0SN19Zj2rdikdWBpj9d+fVlJK0d10p3DRA=",
@@ -106,11 +107,35 @@ ELIDE_VERSIONS = {
("macos", "arm64"): "sha256-IPUIc9kYzc1mxpva0PX88yGA7UsWS+dneXTfhJxUxRA=",
("windows", "amd64"): "sha256-mKvMV80P8OFl0Qw1fTjH33qRw239sIPqJ0m18VAhlt8=",
},
+ "1.6.0-rc.2": {
+ ("linux", "amd64"): "sha256-hlwps26EHnSj+mZtqTGylHzACQAKcUrZIPWW3Q8OV5c=",
+ ("linux", "arm64"): "sha256-rVHHv97BWesWwT2s9eMW/BNYYtjivxps8xiZOeKJSSw=",
+ ("macos", "arm64"): "sha256-eWiPIObRBd6ZxAtCpSR1gtAb2S9IRufTP7KxddFrOug=",
+ ("windows", "amd64"): "sha256-KGilECOjaOLFVMy1jhiHx2dcWUbXVW9n9Hp1kOF06uw=",
+ },
+}
+
+# Versions whose GitHub release tag differs from the version string. Nightly tags
+# (`1.3.6+20260628`) are the version itself; release candidates and releases are
+# tagged with a leading `v`.
+RELEASE_TAGS = {
+ "1.6.0-rc.2": "v1.6.0-rc.2",
}
+def release_tag(version):
+ """Returns the GitHub release tag that publishes `version`.
+
+ Args:
+ version: str. An Elide version, e.g. `1.6.0-rc.2` or `1.3.6+20260628`.
+
+ Returns:
+ str. The release tag, which the `{version}` URL token expands to.
+ """
+ return RELEASE_TAGS.get(version, version)
+
# Default version used when consumers call elide.install() without a version.
# Points at the most-recently verified entry in ELIDE_VERSIONS above.
-DEFAULT_VERSION = "1.3.6+20260628"
+DEFAULT_VERSION = "1.6.0-rc.2"
# GitHub releases serve each tag as an immutable artifact. The {channel} token
# is accepted but unused here; it remains available for consumers who override
diff --git a/tests/kotlin_builder/FallbackTest.kt b/tests/kotlin_builder/FallbackTest.kt
index f56a13e..5e9436e 100644
--- a/tests/kotlin_builder/FallbackTest.kt
+++ b/tests/kotlin_builder/FallbackTest.kt
@@ -1,5 +1,6 @@
package elide.kotlin.builder
+import java.nio.file.Files
import kotlin.test.Test
import kotlin.test.assertEquals
@@ -24,4 +25,18 @@ class FallbackTest {
Fallback.command(stockBuilder = "/bin/stock", flagfile = "/tmp/f", jdkFeature = 23),
)
}
+
+ @Test fun readsFeatureVersionFromJavaHome() {
+ val home = Files.createTempDirectory("jdk").toFile()
+ try {
+ assertEquals(0, Fallback.jdkFeature(home.path))
+ home.resolve("release").writeText("IMPLEMENTOR=\"x\"\nJAVA_VERSION=\"21.0.5\"\n")
+ assertEquals(21, Fallback.jdkFeature(home.path))
+ home.resolve("release").writeText("JAVA_VERSION=\"25\"\n")
+ assertEquals(25, Fallback.jdkFeature(home.path))
+ assertEquals(0, Fallback.jdkFeature(null))
+ } finally {
+ home.deleteRecursively()
+ }
+ }
}
diff --git a/tests/versions_test.bzl b/tests/versions_test.bzl
index 5198413..8cf90e2 100644
--- a/tests/versions_test.bzl
+++ b/tests/versions_test.bzl
@@ -5,7 +5,7 @@
load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest")
# buildifier: disable=bzl-visibility
-load("//elide/private:versions.bzl", "PLATFORMS", "archive_ext", "binary_ext")
+load("//elide/private:versions.bzl", "DEFAULT_VERSION", "ELIDE_VERSIONS", "PLATFORMS", "archive_ext", "binary_ext", "release_tag")
def _archive_ext_test_impl(ctx):
env = unittest.begin(ctx)
@@ -36,6 +36,15 @@ def _platforms_complete_test_impl(ctx):
_platforms_complete_test = unittest.make(_platforms_complete_test_impl)
+def _release_tag_test_impl(ctx):
+ env = unittest.begin(ctx)
+ asserts.equals(env, "1.3.6+20260628", release_tag("1.3.6+20260628"))
+ asserts.equals(env, "v1.6.0-rc.2", release_tag("1.6.0-rc.2"))
+ asserts.true(env, DEFAULT_VERSION in ELIDE_VERSIONS, "DEFAULT_VERSION has no integrity row")
+ return unittest.end(env)
+
+_release_tag_test = unittest.make(_release_tag_test_impl)
+
def versions_test_suite(name):
"""Wires unit tests for versions.bzl helpers.
@@ -45,11 +54,13 @@ def versions_test_suite(name):
_archive_ext_test(name = "archive_ext_test")
_binary_ext_test(name = "binary_ext_test")
_platforms_complete_test(name = "platforms_complete_test")
+ _release_tag_test(name = "release_tag_test")
native.test_suite(
name = name,
tests = [
":archive_ext_test",
":binary_ext_test",
":platforms_complete_test",
+ ":release_tag_test",
],
)