From 3c4938ddd2e380bd38b9e76ca9520e91b2f5ddbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Tue, 4 Jun 2024 15:41:48 +0200 Subject: [PATCH] openjdk22: add updateScript --- pkgs/development/compilers/openjdk/22.nix | 55 ++++-- .../compilers/openjdk/JavaUpdater.java | 181 ++++++++++++++++++ pkgs/development/compilers/openjdk/info.json | 12 ++ 3 files changed, 229 insertions(+), 19 deletions(-) create mode 100644 pkgs/development/compilers/openjdk/JavaUpdater.java create mode 100644 pkgs/development/compilers/openjdk/info.json diff --git a/pkgs/development/compilers/openjdk/22.nix b/pkgs/development/compilers/openjdk/22.nix index bef62b6755a2..9cdec758544c 100644 --- a/pkgs/development/compilers/openjdk/22.nix +++ b/pkgs/development/compilers/openjdk/22.nix @@ -3,7 +3,6 @@ , fetchurl , fetchpatch , fetchFromGitHub -, bash , pkg-config , autoconf , cpio @@ -44,27 +43,32 @@ , gnome_vfs , glib , GConf +, writeShellScript }: let - version = { - feature = "22"; - interim = ""; - build = "36"; - }; + + # Java version format: + # $FEATURE.$INTERIM.$UPDATE.$PATCH + # See + # https://openjdk.org/jeps/223 + # https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/Runtime.Version.html + featureVersion = "22"; + info = builtins.getAttr featureVersion (lib.importJSON ./info.json); + version = info.version; # when building a headless jdk, also bootstrap it with a headless jdk openjdk-bootstrap = openjdk22-bootstrap.override { gtkSupport = !headless; }; - openjdk = stdenv.mkDerivation { + openjdk = stdenv.mkDerivation (finalAttrs: { pname = "openjdk" + lib.optionalString headless "-headless"; - version = "${version.feature}${version.interim}+${version.build}"; + inherit version; src = fetchFromGitHub { owner = "openjdk"; - repo = "jdk${version.feature}u"; - rev = "jdk-${version.feature}${version.interim}+${version.build}"; - hash = "sha256-itjvIedPwJl/l3a2gIVpNMs1zkbrjioVqbCj1Z1nCJE="; + repo = info.repo; + rev = "jdk-${version}"; + hash = info.hash; }; nativeBuildInputs = [ pkg-config autoconf unzip ensureNewerSourcesForZipFilesHook ]; @@ -144,9 +148,8 @@ let # https://openjdk.org/groups/build/doc/building.html configureFlags = [ "--with-boot-jdk=${openjdk-bootstrap.home}" - "--with-version-build=${version.build}" - "--with-version-opt=nixos" - "--with-version-pre=" + "--with-version-string=${version}" + "--with-vendor-version-string=(nix)" "--enable-unlimited-crypto" "--with-native-debug-symbols=internal" "--with-libjpeg=system" @@ -247,14 +250,28 @@ let disallowedReferences = [ openjdk-bootstrap ]; - pos = builtins.unsafeGetAttrPos "feature" version; - meta = import ./meta.nix lib version.feature; + pos = __curPos; + meta = import ./meta.nix lib featureVersion; passthru = { - architecture = ""; - home = "${openjdk}/lib/openjdk"; + updateScript = + let + java-json = fetchurl { + url = "https://search.maven.org/remotecontent?filepath=org/json/json/20240303/json-20240303.jar"; + hash = "sha256-PPbNaJLjLitMHDng9S9SSKL1s3ZG/fu3mma0a2GEFO0="; + }; + in + writeShellScript "update-java" '' + ${finalAttrs.finalPackage}/bin/java \ + -cp ${java-json} \ + ${./JavaUpdater.java} \ + 22 pkgs/development/compilers/openjdk/info.json + ''; + + home = "${finalAttrs.finalPackage}/lib/openjdk"; + inherit gtk3; }; - }; + }); in openjdk diff --git a/pkgs/development/compilers/openjdk/JavaUpdater.java b/pkgs/development/compilers/openjdk/JavaUpdater.java new file mode 100644 index 000000000000..32dddf2fabc7 --- /dev/null +++ b/pkgs/development/compilers/openjdk/JavaUpdater.java @@ -0,0 +1,181 @@ +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URI; +import java.net.http.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class JavaUpdater { + + record GitHubResult(Optional latestVersion, Optional next) { + } + + record JsonInfo(String repo, String version, String hash) { + public JsonInfo(JSONObject json) { + this(json.getString("repo"), json.getString("version"), json.getString("hash")); + } + + public String toJsonString(String featureVersion) { + return """ + \s "%s": { + \s "version": "%s", + \s "repo": "%s", + \s "hash": "%s" + \s }\ + """.formatted(featureVersion, version, repo, hash); + } + } + + // Parses the GitHub Link header + public static Optional getNextLink(HttpHeaders headers) { + var linkHeader = headers.map().get("Link"); + if (linkHeader == null || linkHeader.isEmpty()) return null; + + var links = linkHeader.getFirst(); + var linksRegex = Pattern.compile("<(.+)>;\\s*rel=\"next\""); + return Pattern.compile(",") + .splitAsStream(links) + .map(x -> linksRegex.matcher(x).results() + .map(g -> g.group(1)) + .findFirst() + ) + .filter(Optional::isPresent) + .map(Optional::orElseThrow) + .findFirst(); + } + + // HTTP request helper, sets GITHUB_TOKEN if present + private static HttpRequest NewGithubRequest(String url) { + var token = System.getenv().get("GITHUB_TOKEN"); + var builder = HttpRequest.newBuilder() + .uri(URI.create(url)); + if (token != null) + builder.setHeader("Authorization", "Bearer " + token); + return builder.build(); + } + + private static GitHubResult getLatestTag(String url) { + var request = NewGithubRequest(url); + + var response = + HttpClient.newHttpClient().sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .join(); + + var json = new JSONArray(response.body()); + + Optional version = StreamSupport.stream(json.spliterator(), false) + .map(JSONObject.class::cast) + .map(x -> x.getString("name").replaceFirst("jdk-", "")) + .filter(x -> x.contains("-ga")) + .max(Comparator.comparing(Runtime.Version::parse)); + + return new GitHubResult(version, getNextLink(response.headers())); + } + + public String findNewerVersion() { + var url = Optional.of("https://api.github.com/repos/openjdk/" + getRepo() + "/tags?per_page=100"); + String version = getCurrentVersion(); + do { + GitHubResult response = getLatestTag(url.orElseThrow()); + if (response.latestVersion.isPresent() && response.latestVersion.orElseThrow().equals(version)) { + return null; + } + + String latestVersion = Stream.of(version, response.latestVersion.orElse(version)) + .max(Comparator.comparing(Runtime.Version::parse)).orElseThrow(); + + if (latestVersion != version) + return latestVersion; + + url = response.next; + } while (url.isPresent()); + return null; + } + + + private static String prettyPrint(JSONObject json) { + + Iterable iterable = () -> json.keys(); + + return StreamSupport + .stream(iterable.spliterator(), false) + .sorted(Comparator.reverseOrder()) + .map(majorVersion -> (new JsonInfo(json.getJSONObject(majorVersion))).toJsonString(majorVersion)) + .collect( + Collectors.joining(",\n", "{\n", "\n}") + ); + } + + public void updateJsonInfo(String newVersion) { + try { + JSONObject json = getJsonInfo(); + var info = json.getJSONObject(featureNumber); + info.put("version", newVersion); + info.put("hash", nixHash(newVersion)); + + try (PrintWriter out = new PrintWriter(infoJsonPath)) { + out.println(prettyPrint(json)); + } + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private String nixHash(String version) { + try { + var process = new ProcessBuilder("nix", "flake", "prefetch", + "--extra-experimental-features", "'nix-command flakes'", + "--json", "github:openjdk/" + getRepo() + "/jdk-" + version).start(); + + var json = new JSONObject(new String(process.getInputStream().readAllBytes())); + process.waitFor(); + return json.getString("hash"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private final String featureNumber; + private final String infoJsonPath; + private final JSONObject jsonInfo; + + public String getCurrentVersion() { + return this.jsonInfo.getJSONObject(this.featureNumber).getString("version"); + } + + public String getRepo() { + return this.jsonInfo.getJSONObject(this.featureNumber).getString("repo"); + } + + public JSONObject getJsonInfo() { + try { + String infoStr = Files.readString(Path.of(this.infoJsonPath)); + return new JSONObject(infoStr); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public JavaUpdater(String featureNumber, String infoJsonPath) { + this.featureNumber = featureNumber; + this.infoJsonPath = infoJsonPath; + this.jsonInfo = getJsonInfo(); + } + + public static void main(String[] args) { + var updater = new JavaUpdater(args[0], args[1]); + String newerVersion = updater.findNewerVersion(); + if (newerVersion != null) { + updater.updateJsonInfo(newerVersion); + } + } +} diff --git a/pkgs/development/compilers/openjdk/info.json b/pkgs/development/compilers/openjdk/info.json new file mode 100644 index 000000000000..c8f88d7280e4 --- /dev/null +++ b/pkgs/development/compilers/openjdk/info.json @@ -0,0 +1,12 @@ +{ + "22": { + "version": "22-ga", + "repo": "jdk22u", + "hash": "sha256-itjvIedPwJl/l3a2gIVpNMs1zkbrjioVqbCj1Z1nCJE=" + }, + "21": { + "version": "21.0.3-ga", + "repo": "jdk21u", + "hash": "sha256-zRN16lrc5gtDlTVIQJRRx103w/VbRkatCLeEc9AXWPE=" + } +}