From 08fbbc1eb7c0ceff8eef83f375d0beb188ff5c65 Mon Sep 17 00:00:00 2001 From: airone01 Date: Fri, 12 Jun 2026 18:21:55 +0200 Subject: [PATCH 1/9] feat(pkgs): {hamcrest,junit_4,ant_1_7} build from source --- modules/core.nix | 2 +- modules/hosts/lyra/configuration.nix | 4 + modules/pkgs/java/README.txt | 4 + modules/pkgs/java/ant_1_7/_package.nix | 69 ++++++++++++++ modules/pkgs/java/default.nix | 39 ++++++++ modules/pkgs/java/java-hamcrest/_package.nix | 67 +++++++++++++ ...Item-return-type-cast-for-hamcrest-2.patch | 10 ++ modules/pkgs/java/junit_4/_package.nix | 93 +++++++++++++++++++ 8 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 modules/pkgs/java/README.txt create mode 100644 modules/pkgs/java/ant_1_7/_package.nix create mode 100644 modules/pkgs/java/default.nix create mode 100644 modules/pkgs/java/java-hamcrest/_package.nix create mode 100644 modules/pkgs/java/junit_4/0001-fix-everyItem-return-type-cast-for-hamcrest-2.patch create mode 100644 modules/pkgs/java/junit_4/_package.nix diff --git a/modules/core.nix b/modules/core.nix index 53f63aa..2442c83 100644 --- a/modules/core.nix +++ b/modules/core.nix @@ -10,7 +10,7 @@ config, ... }: { - imports = [inputs.sops-nix.nixosModules.sops self.nixosModules.patches self.nixosModules.compat]; + imports = [inputs.sops-nix.nixosModules.sops self.nixosModules.patches self.nixosModules.compat self.nixosModules.java]; options.stars = { mainUser = lib.mkOption { diff --git a/modules/hosts/lyra/configuration.nix b/modules/hosts/lyra/configuration.nix index 7df99a7..b6b7203 100644 --- a/modules/hosts/lyra/configuration.nix +++ b/modules/hosts/lyra/configuration.nix @@ -105,6 +105,10 @@ efiSupport = true; device = "nodev"; # "nodev" for UEFI theme = pkgs.minimal-grub-theme; + # /boot is only 511M; without a limit GRUB keeps every generation's + # kernel+initrd (~100M each) and fills the ESP. Cap the menu so old + # boot files get pruned on each rebuild. + configurationLimit = 10; }; efi.canTouchEfiVariables = true; }; diff --git a/modules/pkgs/java/README.txt b/modules/pkgs/java/README.txt new file mode 100644 index 0000000..3913e83 --- /dev/null +++ b/modules/pkgs/java/README.txt @@ -0,0 +1,4 @@ +This directory serves as a testing ground for complete Java bootstrap from +source. I will probably setup an overlay which *will* make me cache miss a lot +of packages in nixpkgs. +Doing this for the love of the game. diff --git a/modules/pkgs/java/ant_1_7/_package.nix b/modules/pkgs/java/ant_1_7/_package.nix new file mode 100644 index 0000000..fd0c02f --- /dev/null +++ b/modules/pkgs/java/ant_1_7/_package.nix @@ -0,0 +1,69 @@ +{ + lib, + stdenv, + fetchurl, + jdk, + makeWrapper, +}: +stdenv.mkDerivation (finalAttrs: { + pname = "ant"; + version = "1.7.1"; + + src = fetchurl { + url = "mirror://apache/ant/source/apache-ant-${finalAttrs.version}-src.tar.bz2"; + hash = "sha256-TcSacmDvkKbcZhG36WufBH1QdYlzbUoq1u++Pt/G+6Y="; + }; + + nativeBuildInputs = [ + jdk + makeWrapper + ]; + + postPatch = '' + # The bootstrap target (-> dist-lite) depends on test-jar, which hard-fails + # with ``. We bootstrap with javac only (no + # JUnit), and a bootstrap ant doesn't need the test-utility jar, so drop + # that dependency. dist-lite just copies whatever jars were built, so the + # runnable distribution is unaffected. + substituteInPlace build.xml \ + --replace-fail 'depends="jars,test-jar"' 'depends="jars"' + ''; + + buildPhase = '' + runHook preBuild + + # build.xml pins javac source/target to 1.2/1.3, which a modern javac + # rejects (minimum is 8). bootstrap.sh forwards its arguments into the ant + # invocation that drives the build, and -D overrides the in-file + # defaults. JDK feature detection in build.xml is class-existence based + # (e.g. java.net.Proxy for jdk1.5+), so every source still compiles here. + sh bootstrap.sh -Djavac.source=8 -Djavac.target=8 + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + mkdir -p $out/share/ant + cp -r bootstrap/* $out/share/ant/ + rm -rf $out/share/ant/bin/*.bat + + makeWrapper $out/share/ant/bin/ant $out/bin/ant \ + --set ANT_HOME $out/share/ant \ + --set JAVA_HOME ${jdk.home} + runHook postInstall + ''; + + __structuredAttrs = true; + strictDeps = true; + + meta = { + homepage = "https://ant.apache.org/"; + description = "Java-based build tool"; + sourceProvenance = with lib.sourceTypes; [fromSource]; + license = lib.licenses.asl20; + teams = [lib.teams.java]; + platforms = lib.platforms.all; + mainProgram = "ant"; + }; +}) diff --git a/modules/pkgs/java/default.nix b/modules/pkgs/java/default.nix new file mode 100644 index 0000000..122256f --- /dev/null +++ b/modules/pkgs/java/default.nix @@ -0,0 +1,39 @@ +# feature: from-source Java toolchain overlay +# +# Bootstraps the Java build chain from source, heading toward a from-source +# Gradle. Each package lives as a nixpkgs-style function under +# ../pkgs/java//_package.nix (the leading underscore keeps import-tree +# from importing it as a flake-parts module); they are wired in here as a single +# overlay rather than separate flake packages so the from-source variants flow +# into every consumer of the package set. +# +# - java-hamcrest: overrides the nixpkgs (Gradle-built) package. +# - junit: new attr (nixpkgs has no top-level junit); picks up the +# overridden hamcrest via `final.callPackage`. +# - ant_1_7: new attr (does NOT override the modern `ant`); 1.7.1 is the +# last release fully bootstrappable from bootstrap.sh + javac. +{inputs, ...}: let + overlay = final: _prev: { + java-hamcrest = final.callPackage ../pkgs/java/java-hamcrest/_package.nix {}; + junit_4 = final.callPackage ../pkgs/java/junit_4/_package.nix {}; + ant_1_7 = final.callPackage ../pkgs/java/ant_1_7/_package.nix {}; + }; +in { + # Reusable overlay output (e.g. for downstream flakes or `nix build`). + flake.overlays.java = overlay; + + # Global for the flake: flake-parts provides `pkgs` only at mkOptionDefault + # priority, so this override wins and every per-system package set + # (packages, devShells, `nix build .#…`) sees the overlay without a module. + perSystem = {system, ...}: { + _module.args.pkgs = import inputs.nixpkgs { + inherit system; + overlays = [overlay]; + }; + }; + + # NixOS hosts can only receive an overlay through the module system, so this + # can't be made module-free. Kept as its own module (out of `patches`) and + # imported by core.nix so it reaches every host. + flake.nixosModules.java = {nixpkgs.overlays = [overlay];}; +} diff --git a/modules/pkgs/java/java-hamcrest/_package.nix b/modules/pkgs/java/java-hamcrest/_package.nix new file mode 100644 index 0000000..243dacd --- /dev/null +++ b/modules/pkgs/java/java-hamcrest/_package.nix @@ -0,0 +1,67 @@ +{ + lib, + stdenvNoCC, + fetchFromGitHub, + jdk, + stripJavaArchivesHook, +}: +# Built manually with javac/jar rather than Gradle: Gradle itself depends on +# Hamcrest, so bootstrapping the Java toolchain from source can't use Gradle to +# build it. Hamcrest's main module is pure Java 8 with no runtime dependencies, +# so a plain compile is enough (the hamcrest-core / -library / -integration dirs +# are legacy aggregator modules with no sources of their own since 2.0). +stdenvNoCC.mkDerivation (finalAttrs: { + pname = "java-hamcrest"; + version = "3.0"; + + src = fetchFromGitHub { + owner = "hamcrest"; + repo = "JavaHamcrest"; + tag = "v${finalAttrs.version}"; + hash = "sha256-ntae6XWpD0wEs36YoPsfTl6cSR6ULl6dAJ5oZsV+ih0="; + }; + + nativeBuildInputs = [ + jdk + stripJavaArchivesHook + ]; + + buildPhase = '' + runHook preBuild + + mkdir -p build/classes + + find hamcrest/src/main/java -name "*.java" > sources.txt + + javac \ + --release 8 \ + -encoding UTF-8 \ + -d build/classes \ + @sources.txt + + jar cf hamcrest-${finalAttrs.version}.jar -C build/classes . + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + install -Dm644 hamcrest-${finalAttrs.version}.jar $out/share/java/hamcrest-${finalAttrs.version}.jar + ln -s $out/share/java/hamcrest-${finalAttrs.version}.jar $out/share/java/hamcrest.jar + + runHook postInstall + ''; + + strictDeps = true; + __structuredAttrs = true; + + meta = { + homepage = "https://hamcrest.org/JavaHamcrest/"; + description = "Java library containing matchers that can be combined to create flexible expressions of intent"; + license = lib.licenses.bsd3; + maintainers = with lib.maintainers; [tomodachi94 airone01]; + platforms = lib.platforms.all; + sourceProvenance = with lib.sourceTypes; [fromSource]; + }; +}) diff --git a/modules/pkgs/java/junit_4/0001-fix-everyItem-return-type-cast-for-hamcrest-2.patch b/modules/pkgs/java/junit_4/0001-fix-everyItem-return-type-cast-for-hamcrest-2.patch new file mode 100644 index 0000000..b97857b --- /dev/null +++ b/modules/pkgs/java/junit_4/0001-fix-everyItem-return-type-cast-for-hamcrest-2.patch @@ -0,0 +1,10 @@ +--- a/src/main/java/org/junit/matchers/JUnitMatchers.java ++++ b/src/main/java/org/junit/matchers/JUnitMatchers.java +@@ -59,6 +59,7 @@ public class JUnitMatchers { + */ + @Deprecated ++ @SuppressWarnings("unchecked") + public static Matcher> everyItem(final Matcher elementMatcher) { +- return CoreMatchers.everyItem(elementMatcher); ++ return (Matcher>) (Object) CoreMatchers.everyItem(elementMatcher); + } diff --git a/modules/pkgs/java/junit_4/_package.nix b/modules/pkgs/java/junit_4/_package.nix new file mode 100644 index 0000000..6b9cb84 --- /dev/null +++ b/modules/pkgs/java/junit_4/_package.nix @@ -0,0 +1,93 @@ +{ + lib, + stdenvNoCC, + fetchFromGitHub, + jdk, + java-hamcrest, + stripJavaArchivesHook, +}: +stdenvNoCC.mkDerivation (finalAttrs: { + pname = "junit"; + version = "4.13.2"; + + src = fetchFromGitHub { + owner = "junit-team"; + repo = "junit4"; + rev = "r${finalAttrs.version}"; + hash = "sha256-A6ZbmsECwP/hYqmIoU3rDvEX3V9Dx3FtCgAxpv8n8+Q="; + }; + + nativeBuildInputs = [ + jdk + stripJavaArchivesHook + ]; + + propagatedBuildInputs = [java-hamcrest]; + + patches = [ + # hamcrest 2.0+ changed everyItem return type from Matcher> to + # Matcher>; cast through Object to satisfy old signature + ./0001-fix-everyItem-return-type-cast-for-hamcrest-2.patch + ]; + + postPatch = '' + # Generate Version.java from the template (Maven normally does this) + # The committed Version.java has a stale "4.13.2-SNAPSHOT" string + mv src/main/java/junit/runner/Version.java.template \ + src/main/java/junit/runner/Version.java + substituteInPlace src/main/java/junit/runner/Version.java \ + --replace-fail '@version@' '${finalAttrs.version}' + + # @Factory was removed from hamcrest 2.0+; it was a no-op marker annotation + for f in \ + src/main/java/org/junit/internal/matchers/ThrowableMessageMatcher.java \ + src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java \ + src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java; do + substituteInPlace "$f" \ + --replace-fail $'import org.hamcrest.Factory;\n' "" \ + --replace-fail $' @Factory\n ' " " + done + ''; + + buildPhase = '' + runHook preBuild + + mkdir -p build/classes + + find src/main/java -name "*.java" > sources.txt + + javac \ + --release 8 \ + -classpath "$(find ${java-hamcrest}/share/java -name '*.jar' | tr '\n' ':')" \ + -encoding ISO-8859-1 \ + -d build/classes \ + @sources.txt + + cp -r src/main/resources/. build/classes/ 2>/dev/null || true + + jar cf junit-${finalAttrs.version}.jar -C build/classes . + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + install -Dm644 junit-${finalAttrs.version}.jar $out/share/java/junit-${finalAttrs.version}.jar + ln -s $out/share/java/junit-${finalAttrs.version}.jar $out/share/java/junit.jar + + runHook postInstall + ''; + + strictDeps = true; + __structuredAttrs = true; + + meta = { + description = "JUnit 4 testing framework for Java"; + homepage = "https://junit.org/junit4/"; + license = lib.licenses.epl10; + maintainers = with lib.maintainers; [airone01]; + platforms = lib.platforms.all; + sourceProvenance = with lib.sourceTypes; [fromSource]; + }; +}) From 9170a0ea319bbc7116b44740a2604606bd62c4a0 Mon Sep 17 00:00:00 2001 From: airone01 Date: Fri, 12 Jun 2026 19:14:09 +0200 Subject: [PATCH 2/9] feat(pkgs): build ant from source --- modules/pkgs/java/ant/_package.nix | 77 ++++++++++++++++++++++++++++++ modules/pkgs/java/default.nix | 16 ++++--- 2 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 modules/pkgs/java/ant/_package.nix diff --git a/modules/pkgs/java/ant/_package.nix b/modules/pkgs/java/ant/_package.nix new file mode 100644 index 0000000..fa06d3a --- /dev/null +++ b/modules/pkgs/java/ant/_package.nix @@ -0,0 +1,77 @@ +{ + lib, + stdenv, + fetchurl, + jdk, + makeWrapper, + junit_4, + java-hamcrest, +}: +# Built from source (nixpkgs ships ant as a binary distribution). build.sh +# first bootstraps a minimal ant with javac, then drives the full build with +# that bootstrap ant. The JUnit optional tasks and the test jar are compiled +# against JUnit, which the build discovers on the classpath via lib/optional; +# we swap the pre-built binaries shipped there for our from-source junit_4 and +# java-hamcrest. JUnit 5 (junitlauncher) stays absent, so those tasks are +# skipped — junit_4 is enough for a JUnit 4 capable ant. +stdenv.mkDerivation (finalAttrs: { + pname = "ant"; + version = "1.10.15"; + + src = fetchurl { + url = "mirror://apache/ant/source/apache-ant-${finalAttrs.version}-src.tar.bz2"; + hash = "sha256-WPU+khKoAFW/FOknicf1BCBqs1uLw5dfo7cocg2A79c="; + }; + + nativeBuildInputs = [ + jdk + makeWrapper + ]; + + postPatch = '' + # Replace the binary JUnit/Hamcrest jars shipped in the source tree with our + # from-source builds. build.sh adds lib/optional to the build classpath, so + # the JUnit tasks and tests compile against these. Our java-hamcrest is a + # single merged jar (vs upstream's split core/library 1.3 jars). + rm -f lib/optional/junit-*.jar lib/optional/hamcrest-*.jar + cp ${junit_4}/share/java/junit-*.jar lib/optional/ + cp ${java-hamcrest}/share/java/hamcrest-*.jar lib/optional/ + ''; + + buildPhase = '' + runHook preBuild + + # build.sh bootstraps with javac if needed, then runs the bootstrap ant on + # the default `main` target, producing a minimal distribution in ./dist. + sh build.sh + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + mkdir -p $out/share/ant + cp -r dist/* $out/share/ant/ + rm -rf $out/share/ant/bin/*.bat $out/share/ant/bin/*.cmd + + makeWrapper $out/share/ant/bin/ant $out/bin/ant \ + --set ANT_HOME $out/share/ant \ + --set JAVA_HOME ${jdk.home} + + runHook postInstall + ''; + + __structuredAttrs = true; + strictDeps = true; + + meta = { + homepage = "https://ant.apache.org/"; + description = "Java-based build tool"; + sourceProvenance = with lib.sourceTypes; [fromSource]; + license = lib.licenses.asl20; + teams = [lib.teams.java]; + platforms = lib.platforms.all; + mainProgram = "ant"; + }; +}) diff --git a/modules/pkgs/java/default.nix b/modules/pkgs/java/default.nix index 122256f..22083e6 100644 --- a/modules/pkgs/java/default.nix +++ b/modules/pkgs/java/default.nix @@ -2,21 +2,25 @@ # # Bootstraps the Java build chain from source, heading toward a from-source # Gradle. Each package lives as a nixpkgs-style function under -# ../pkgs/java//_package.nix (the leading underscore keeps import-tree +# .//_package.nix (the leading underscore on the file keeps import-tree # from importing it as a flake-parts module); they are wired in here as a single # overlay rather than separate flake packages so the from-source variants flow # into every consumer of the package set. # # - java-hamcrest: overrides the nixpkgs (Gradle-built) package. -# - junit: new attr (nixpkgs has no top-level junit); picks up the -# overridden hamcrest via `final.callPackage`. +# - junit_4: new attr (nixpkgs has no top-level junit); picks up the +# overridden hamcrest via `final.callPackage`. Named junit_4 to +# leave room for a future `junit` (latest) package. # - ant_1_7: new attr (does NOT override the modern `ant`); 1.7.1 is the # last release fully bootstrappable from bootstrap.sh + javac. +# - ant: overrides the nixpkgs (binary) ant with a from-source 1.10.15 +# built via the bootstrap ant, compiled against our junit_4. {inputs, ...}: let overlay = final: _prev: { - java-hamcrest = final.callPackage ../pkgs/java/java-hamcrest/_package.nix {}; - junit_4 = final.callPackage ../pkgs/java/junit_4/_package.nix {}; - ant_1_7 = final.callPackage ../pkgs/java/ant_1_7/_package.nix {}; + java-hamcrest = final.callPackage ./java-hamcrest/_package.nix {}; + junit_4 = final.callPackage ./junit_4/_package.nix {}; + ant_1_7 = final.callPackage ./ant_1_7/_package.nix {}; + ant = final.callPackage ./ant/_package.nix {}; }; in { # Reusable overlay output (e.g. for downstream flakes or `nix build`). From 5f001851a6959f70abb61db46564de197667639b Mon Sep 17 00:00:00 2001 From: airone01 Date: Fri, 12 Jun 2026 20:25:05 +0200 Subject: [PATCH 3/9] docs(pkgs): java.dot dep graph --- modules/pkgs/java/java.dot | 285 +++++++++++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 modules/pkgs/java/java.dot diff --git a/modules/pkgs/java/java.dot b/modules/pkgs/java/java.dot new file mode 100644 index 0000000..a6913c2 --- /dev/null +++ b/modules/pkgs/java/java.dot @@ -0,0 +1,285 @@ +digraph JavaBootstrapJPB { + rankdir=TB; + ranksep=0.7; + nodesep=0.35; + fontname="Helvetica"; + node [shape=box, style="rounded,filled", fontname="Helvetica", fontsize=10, margin="0.14,0.07"]; + edge [fontname="Helvetica", fontsize=8, color="#64748b"]; + + // ════════════════════════════════════════════════════════════ + // BASE + // ════════════════════════════════════════════════════════════ + JC [label="javac (OpenJDK)", fillcolor="#e2e8f0", color="#475569"]; + + // ════════════════════════════════════════════════════════════ + // Tractable (javac-only, no external compile deps) + // ════════════════════════════════════════════════════════════ + subgraph cluster_tractable { + label="Phase 1 — tractable (javac bypass)"; + style="rounded,filled"; fillcolor="#ecfdf5"; color="#059669"; fontcolor="#065f46"; fontname="Helvetica-Bold"; + ANTP [label="Ant pre-1.7", fillcolor="#d1fae5", color="#059669"]; + ANT [label="Ant 1.10.x", fillcolor="#d1fae5", color="#059669"]; + HC [label="Hamcrest", fillcolor="#d1fae5", color="#059669"]; + JU4 [label="JUnit 4", fillcolor="#d1fae5", color="#059669"]; + } + + // ════════════════════════════════════════════════════════════ + // Plexus (low-level, built early) + // ════════════════════════════════════════════════════════════ + subgraph cluster_plexus { + label="Plexus"; + style="rounded,filled"; fillcolor="#fefce8"; color="#a16207"; fontcolor="#854d0e"; fontname="Helvetica-Bold"; + PL_CW [label="plexus-classworlds", fillcolor="#fef9c3", color="#a16207"]; + PL_UTIL [label="plexus-utils", fillcolor="#fef9c3", color="#a16207"]; + PL_CA [label="plexus-component-annotations",fillcolor="#fef9c3", color="#a16207"]; + PL_INTP [label="plexus-interpolation", fillcolor="#fef9c3", color="#a16207"]; + PL_CIPH [label="plexus-cipher", fillcolor="#fef9c3", color="#a16207"]; + PL_SEC [label="plexus-sec-dispatcher", fillcolor="#fef9c3", color="#a16207"]; + } + + // ════════════════════════════════════════════════════════════ + // javax / CDI / DI primitives + // ════════════════════════════════════════════════════════════ + subgraph cluster_javax { + label="javax / DI primitives"; + style="rounded,filled"; fillcolor="#f8fafc"; color="#475569"; fontname="Helvetica-Bold"; + JX_INJ [label="javax.inject", fillcolor="#f1f5f9", color="#475569"]; + JX_JSR [label="jsr250-api", fillcolor="#f1f5f9", color="#475569"]; + JX_CDI [label="cdi-api", fillcolor="#f1f5f9", color="#475569"]; + AOPAL [label="aopalliance", fillcolor="#f1f5f9", color="#475569"]; + } + + // ════════════════════════════════════════════════════════════ + // Google + Sisu (DI container) + // ════════════════════════════════════════════════════════════ + subgraph cluster_di { + label="DI container (Guice + Sisu)"; + style="rounded,filled"; fillcolor="#fdf4ff"; color="#a21caf"; fontcolor="#86198f"; fontname="Helvetica-Bold"; + GUAVA [label="guava", fillcolor="#fae8ff", color="#a21caf"]; + GUICE [label="guice", fillcolor="#fae8ff", color="#a21caf"]; + SISU_INJ [label="sisu.inject", fillcolor="#fae8ff", color="#a21caf"]; + SISU_PLEX [label="sisu.plexus", fillcolor="#fae8ff", color="#a21caf"]; + } + + // ════════════════════════════════════════════════════════════ + // Commons / misc utilities + // ════════════════════════════════════════════════════════════ + subgraph cluster_commons { + label="Commons / misc"; + style="rounded,filled"; fillcolor="#f0fdf4"; color="#15803d"; fontcolor="#14532d"; fontname="Helvetica-Bold"; + COM_CLI [label="commons-cli", fillcolor="#dcfce7", color="#15803d"]; + COM_IO [label="commons-io", fillcolor="#dcfce7", color="#15803d"]; + COM_LANG [label="commons-lang", fillcolor="#dcfce7", color="#15803d"]; + COM_LAN3 [label="commons-lang3", fillcolor="#dcfce7", color="#15803d"]; + SLF_API [label="slf4j-api", fillcolor="#dcfce7", color="#15803d"]; + SLF_SMP [label="slf4j-simple", fillcolor="#dcfce7", color="#15803d"]; + JSOUP [label="jsoup", fillcolor="#dcfce7", color="#15803d"]; + } + + // ════════════════════════════════════════════════════════════ + // Wagon + // ════════════════════════════════════════════════════════════ + subgraph cluster_wagon { + label="Wagon 2.10"; + style="rounded,filled"; fillcolor="#eff6ff"; color="#2563eb"; fontcolor="#1e3a8a"; fontname="Helvetica-Bold"; + WAG_API [label="wagon-provider-api", fillcolor="#dbeafe", color="#2563eb"]; + WAG_HTSH [label="wagon-http-shared", fillcolor="#dbeafe", color="#2563eb"]; + WAG_FILE [label="wagon-file", fillcolor="#dbeafe", color="#2563eb"]; + WAG_HTTP [label="wagon-http", fillcolor="#dbeafe", color="#2563eb"]; + } + + // ════════════════════════════════════════════════════════════ + // Eclipse Aether + // ════════════════════════════════════════════════════════════ + subgraph cluster_aether { + label="Eclipse Aether 1.0.2"; + style="rounded,filled"; fillcolor="#eef2ff"; color="#4338ca"; fontcolor="#3730a3"; fontname="Helvetica-Bold"; + AE_API [label="aether-api", fillcolor="#e0e7ff", color="#4338ca"]; + AE_SPI [label="aether-spi", fillcolor="#e0e7ff", color="#4338ca"]; + AE_UTIL [label="aether-util", fillcolor="#e0e7ff", color="#4338ca"]; + AE_IMPL [label="aether-impl", fillcolor="#e0e7ff", color="#4338ca"]; + AE_CONN [label="aether-connector-basic", fillcolor="#e0e7ff", color="#4338ca"]; + AE_TRAN [label="aether-transport-wagon", fillcolor="#e0e7ff", color="#4338ca"]; + } + + // ════════════════════════════════════════════════════════════ + // Modello (code generator) + // ════════════════════════════════════════════════════════════ + MODELLO [label="modello", fillcolor="#ffedd5", color="#c2410c", fontcolor="#7c2d12"]; + + // ════════════════════════════════════════════════════════════ + // Maven internal modules + // ════════════════════════════════════════════════════════════ + subgraph cluster_maven { + label="Maven 3.3.9 modules"; + style="rounded,filled"; fillcolor="#fff7ed"; color="#c2410c"; fontcolor="#7c2d12"; fontname="Helvetica-Bold"; + M_ART [label="maven-artifact", fillcolor="#fed7aa", color="#c2410c"]; + M_BUILD [label="maven-builder-support", fillcolor="#fed7aa", color="#c2410c"]; + M_MODEL [label="maven-model", fillcolor="#fed7aa", color="#c2410c"]; + M_MODELB [label="maven-model-builder", fillcolor="#fed7aa", color="#c2410c"]; + M_SET [label="maven-settings", fillcolor="#fed7aa", color="#c2410c"]; + M_SETB [label="maven-settings-builder", fillcolor="#fed7aa", color="#c2410c"]; + M_PLUG [label="maven-plugin-api", fillcolor="#fed7aa", color="#c2410c"]; + M_REPOM [label="maven-repository-metadata", fillcolor="#fed7aa", color="#c2410c"]; + M_AETHP [label="maven-aether-provider", fillcolor="#fed7aa", color="#c2410c"]; + M_CORE [label="maven-core", fillcolor="#fdba74", color="#c2410c"]; + M_EMB [label="maven-embedder", fillcolor="#fdba74", color="#c2410c"]; + M_COMPAT [label="maven-compat", fillcolor="#fdba74", color="#c2410c"]; + } + + // ════════════════════════════════════════════════════════════ + // Goals + // ════════════════════════════════════════════════════════════ + MVN339 [label="Maven 3.3.9", fillcolor="#fecaca", color="#dc2626", fontcolor="#991b1b"]; + MVN35 [label="Maven 3.5+", fillcolor="#fecaca", color="#dc2626", fontcolor="#991b1b"]; + GDEPS [label="Gradle deps", fillcolor="#fecaca", color="#dc2626", fontcolor="#991b1b", style="rounded,filled,dashed"]; + GRADLE [label="Gradle", fillcolor="#ede9fe", color="#7c3aed", fontcolor="#4c1d95"]; + + // ════════════════════════════════════════════════════════════ + // EDGES — every node is built with javac; package edges = compile classpath order + // ════════════════════════════════════════════════════════════ + + // ── javac builds EVERYTHING (JPB: only OpenJDK + C compiler at the base) ── + JC -> ANTP; + JC -> HC; + JC -> JU4; + JC -> PL_CW; + JC -> PL_UTIL; + JC -> PL_CA; + JC -> PL_INTP; + JC -> PL_CIPH; + JC -> JX_INJ; + JC -> JX_JSR; + JC -> JX_CDI; + JC -> AOPAL; + JC -> GUAVA; + JC -> GUICE; + JC -> SISU_INJ; + JC -> SISU_PLEX; + JC -> COM_CLI; + JC -> COM_IO; + JC -> COM_LANG; + JC -> COM_LAN3; + JC -> SLF_API; + JC -> SLF_SMP; + JC -> JSOUP; + JC -> WAG_API; + JC -> WAG_HTSH; + JC -> WAG_FILE; + JC -> WAG_HTTP; + JC -> AE_API; + JC -> AE_SPI; + JC -> AE_UTIL; + JC -> AE_IMPL; + JC -> AE_CONN; + JC -> AE_TRAN; + JC -> MODELLO; + JC -> M_ART; + JC -> M_BUILD; + JC -> M_MODEL; + JC -> M_MODELB; + JC -> M_SET; + JC -> M_SETB; + JC -> M_PLUG; + JC -> M_REPOM; + JC -> M_AETHP; + JC -> M_CORE; + JC -> M_EMB; + JC -> M_COMPAT; + + // ── Tractable chain ── + ANTP -> ANT; + HC -> JU4; + HC -> ANT; + JU4 -> ANT; + + // ── Plexus internal ── + PL_CIPH -> PL_SEC; + JC -> PL_SEC; + + // ── DI primitives ── + JX_INJ -> GUICE; + AOPAL -> GUICE; + JX_JSR -> JX_CDI; + GUICE -> SISU_INJ; + SISU_INJ -> SISU_PLEX; + JX_CDI -> SISU_PLEX; + PL_CA -> SISU_PLEX; + + // ── Wagon classpath order ── + WAG_API -> WAG_FILE; + WAG_API -> WAG_HTTP; + WAG_HTSH -> WAG_HTTP; + + // ── Aether classpath order ── + AE_API -> AE_SPI; + AE_API -> AE_UTIL; + AE_API -> AE_IMPL; + AE_SPI -> AE_IMPL; + AE_UTIL -> AE_IMPL; + AE_API -> AE_CONN; + AE_SPI -> AE_CONN; + AE_UTIL -> AE_CONN; + AE_API -> AE_TRAN; + AE_SPI -> AE_TRAN; + WAG_API -> AE_TRAN; + + // ── Modello generates Maven model sources ── + MODELLO -> M_MODEL; + MODELLO -> M_SET; + MODELLO -> M_REPOM; + + // ── Maven module compile order ── + M_MODEL -> M_MODELB; + M_BUILD -> M_MODELB; + PL_INTP -> M_MODELB; + GUAVA -> M_MODELB; + M_SET -> M_SETB; + PL_SEC -> M_SETB; + M_ART -> M_CORE; + M_MODEL -> M_CORE; + M_MODELB -> M_CORE; + M_SETB -> M_CORE; + M_REPOM -> M_CORE; + M_PLUG -> M_CORE; + M_AETHP -> M_CORE; + AE_API -> M_AETHP; + AE_UTIL -> M_AETHP; + AE_CONN -> M_AETHP; + AE_TRAN -> M_AETHP; + AE_IMPL -> M_CORE; + SISU_PLEX -> M_CORE; + GUICE -> M_CORE; + PL_CA -> M_CORE; + PL_INTP -> M_CORE; + M_MODELB -> M_EMB; + M_SET -> M_EMB; + M_PLUG -> M_EMB; + M_CORE -> M_EMB; + PL_CW -> M_EMB; + PL_UTIL -> M_EMB; + SLF_API -> M_EMB; + SLF_SMP -> M_EMB; + COM_LAN3 -> M_EMB; + COM_CLI -> M_EMB; + JSOUP -> M_EMB; + WAG_API -> M_COMPAT; + COM_LANG -> M_COMPAT; + COM_IO -> M_COMPAT; + M_CORE -> M_COMPAT; + + // ── Modules assemble into the product ── + M_EMB -> MVN339; + M_CORE -> MVN339; + M_COMPAT -> MVN339; + ANT -> MVN339 [style=dashed, label="built with"]; + + // ── Onward ── + MVN339 -> MVN35; + MVN35 -> GDEPS; + GDEPS -> GRADLE; + + // ── Circular back-edges (upstream build-tool reality, broken by javac bypass) ── + GRADLE -> HC [style=dashed, color="#b45309", fontcolor="#b45309", label="upstream: Gradle", constraint=false]; + MVN339 -> JU4 [style=dashed, color="#b45309", fontcolor="#b45309", label="upstream: Maven", constraint=false]; +} From 6390eac45a75a05b8da25d4c5d34ddbf4385a618 Mon Sep 17 00:00:00 2001 From: airone01 Date: Sat, 13 Jun 2026 00:49:26 +0200 Subject: [PATCH 4/9] feat(pkgs): {jsoup,specify} build from source --- modules/pkgs/java/default.nix | 4 ++ modules/pkgs/java/java.dot | 6 ++- modules/pkgs/java/jsoup/_package.nix | 68 +++++++++++++++++++++++++ modules/pkgs/java/jspecify/_package.nix | 68 +++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 modules/pkgs/java/jsoup/_package.nix create mode 100644 modules/pkgs/java/jspecify/_package.nix diff --git a/modules/pkgs/java/default.nix b/modules/pkgs/java/default.nix index 22083e6..aa82e4d 100644 --- a/modules/pkgs/java/default.nix +++ b/modules/pkgs/java/default.nix @@ -15,12 +15,16 @@ # last release fully bootstrappable from bootstrap.sh + javac. # - ant: overrides the nixpkgs (binary) ant with a from-source 1.10.15 # built via the bootstrap ant, compiled against our junit_4. +# - jspecify: new attr; pure nullness annotations, javac-only leaf. +# - jsoup: new attr; HTML parser, javac build, compiled against jspecify. {inputs, ...}: let overlay = final: _prev: { java-hamcrest = final.callPackage ./java-hamcrest/_package.nix {}; junit_4 = final.callPackage ./junit_4/_package.nix {}; ant_1_7 = final.callPackage ./ant_1_7/_package.nix {}; ant = final.callPackage ./ant/_package.nix {}; + jspecify = final.callPackage ./jspecify/_package.nix {}; + jsoup = final.callPackage ./jsoup/_package.nix {}; }; in { # Reusable overlay output (e.g. for downstream flakes or `nix build`). diff --git a/modules/pkgs/java/java.dot b/modules/pkgs/java/java.dot index a6913c2..1fa0705 100644 --- a/modules/pkgs/java/java.dot +++ b/modules/pkgs/java/java.dot @@ -73,6 +73,7 @@ digraph JavaBootstrapJPB { COM_LAN3 [label="commons-lang3", fillcolor="#dcfce7", color="#15803d"]; SLF_API [label="slf4j-api", fillcolor="#dcfce7", color="#15803d"]; SLF_SMP [label="slf4j-simple", fillcolor="#dcfce7", color="#15803d"]; + JSPECIFY [label="jspecify", fillcolor="#dcfce7", color="#15803d"]; JSOUP [label="jsoup", fillcolor="#dcfce7", color="#15803d"]; } @@ -162,7 +163,7 @@ digraph JavaBootstrapJPB { JC -> COM_LAN3; JC -> SLF_API; JC -> SLF_SMP; - JC -> JSOUP; + JC -> JSPECIFY; JC -> WAG_API; JC -> WAG_HTSH; JC -> WAG_FILE; @@ -206,6 +207,9 @@ digraph JavaBootstrapJPB { JX_CDI -> SISU_PLEX; PL_CA -> SISU_PLEX; + // ── Commons / misc internal ── + JSPECIFY -> JSOUP; + // ── Wagon classpath order ── WAG_API -> WAG_FILE; WAG_API -> WAG_HTTP; diff --git a/modules/pkgs/java/jsoup/_package.nix b/modules/pkgs/java/jsoup/_package.nix new file mode 100644 index 0000000..86416be --- /dev/null +++ b/modules/pkgs/java/jsoup/_package.nix @@ -0,0 +1,68 @@ +{ + lib, + stdenvNoCC, + fetchFromGitHub, + jdk, + stripJavaArchivesHook, + jspecify, +}: +# Pure-Java HTML parser, built with javac. Its only compile-time dependency is +# jspecify (nullness annotations, provided scope upstream); not propagated, as +# it isn't needed at runtime. The src/main/java9 module-info is for the modular +# multi-release jar and is intentionally skipped in this plain build. +stdenvNoCC.mkDerivation (finalAttrs: { + pname = "jsoup"; + version = "1.17.2"; + + src = fetchFromGitHub { + owner = "jhy"; + repo = "jsoup"; + rev = "jsoup-${finalAttrs.version}"; + hash = "sha256-Zkq2W9p8AAgeuWxze1QJfmmzwLPz4iNm6UggW5sZmJ0="; + }; + + nativeBuildInputs = [ + jdk + stripJavaArchivesHook + ]; + + buildPhase = '' + runHook preBuild + + mkdir -p build/classes + + find src/main/java -name "*.java" > sources.txt + + javac \ + --release 8 \ + -encoding UTF-8 \ + -classpath ${jspecify}/share/java/jspecify.jar \ + -d build/classes \ + @sources.txt + + jar cf jsoup-${finalAttrs.version}.jar -C build/classes . + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + install -Dm644 jsoup-${finalAttrs.version}.jar $out/share/java/jsoup-${finalAttrs.version}.jar + ln -s $out/share/java/jsoup-${finalAttrs.version}.jar $out/share/java/jsoup.jar + + runHook postInstall + ''; + + strictDeps = true; + __structuredAttrs = true; + + meta = { + homepage = "https://jsoup.org/"; + description = "Java HTML parser for real-world HTML: editing, cleaning, scraping, and XSS safety"; + license = lib.licenses.mit; + maintainers = with lib.maintainers; [airone01]; + platforms = lib.platforms.all; + sourceProvenance = with lib.sourceTypes; [fromSource]; + }; +}) diff --git a/modules/pkgs/java/jspecify/_package.nix b/modules/pkgs/java/jspecify/_package.nix new file mode 100644 index 0000000..18c4449 --- /dev/null +++ b/modules/pkgs/java/jspecify/_package.nix @@ -0,0 +1,68 @@ +{ + lib, + stdenvNoCC, + fetchFromGitHub, + jdk, + stripJavaArchivesHook, +}: +# Pure-annotations library (nullness markers for static analysis), built with +# javac alone — no dependencies. jsoup compiles against these (provided scope +# upstream: needed at compile time, not bundled at runtime). +stdenvNoCC.mkDerivation (finalAttrs: { + pname = "jspecify"; + version = "0.3.0"; + + src = fetchFromGitHub { + owner = "jspecify"; + repo = "jspecify"; + rev = "v${finalAttrs.version}"; + hash = "sha256-kPeRTncvY5iIQQ4AOM6prOFxMRjvM8Dc8/wPuJFk5go="; + }; + + nativeBuildInputs = [ + jdk + stripJavaArchivesHook + ]; + + buildPhase = '' + runHook preBuild + + mkdir -p build/classes + + find src/main/java -name "*.java" > sources.txt + + # NullMarked targets ElementType.MODULE (Java 9+), so use -source/-target 8 + # (which keeps the full running-JDK API) rather than --release 8, whose + # Java-8 API surface lacks ElementType.MODULE. + javac \ + -source 8 -target 8 \ + -encoding UTF-8 \ + -d build/classes \ + @sources.txt + + jar cf jspecify-${finalAttrs.version}.jar -C build/classes . + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + install -Dm644 jspecify-${finalAttrs.version}.jar $out/share/java/jspecify-${finalAttrs.version}.jar + ln -s $out/share/java/jspecify-${finalAttrs.version}.jar $out/share/java/jspecify.jar + + runHook postInstall + ''; + + strictDeps = true; + __structuredAttrs = true; + + meta = { + homepage = "https://jspecify.dev/"; + description = "Standard annotations for expressing nullness in Java for static analysis"; + license = lib.licenses.asl20; + maintainers = with lib.maintainers; [airone01]; + platforms = lib.platforms.all; + sourceProvenance = with lib.sourceTypes; [fromSource]; + }; +}) From 939488555ba760de2aa47655ca2040fe309dc6d1 Mon Sep 17 00:00:00 2001 From: airone01 Date: Sun, 14 Jun 2026 00:43:28 +0200 Subject: [PATCH 5/9] feat(pkgs): common-{cli,io,lang{,3}} build from source --- modules/pkgs/java/commons-cli/_package.nix | 64 ++++++++++++++++++ modules/pkgs/java/commons-io/_package.nix | 64 ++++++++++++++++++ modules/pkgs/java/commons-lang/_package.nix | 69 ++++++++++++++++++++ modules/pkgs/java/commons-lang3/_package.nix | 64 ++++++++++++++++++ modules/pkgs/java/default.nix | 8 +++ 5 files changed, 269 insertions(+) create mode 100644 modules/pkgs/java/commons-cli/_package.nix create mode 100644 modules/pkgs/java/commons-io/_package.nix create mode 100644 modules/pkgs/java/commons-lang/_package.nix create mode 100644 modules/pkgs/java/commons-lang3/_package.nix diff --git a/modules/pkgs/java/commons-cli/_package.nix b/modules/pkgs/java/commons-cli/_package.nix new file mode 100644 index 0000000..91d5909 --- /dev/null +++ b/modules/pkgs/java/commons-cli/_package.nix @@ -0,0 +1,64 @@ +{ + lib, + stdenvNoCC, + fetchFromGitHub, + jdk, + stripJavaArchivesHook, +}: +stdenvNoCC.mkDerivation (finalAttrs: { + pname = "commons-cli"; + version = "1.10.0"; + + src = fetchFromGitHub { + owner = "apache"; + repo = "commons-cli"; + rev = "rel/commons-cli-${finalAttrs.version}"; + hash = "sha256-xYefS5iict9yspWEZrhgGfWbqT9uzfilj3ehXjgQBOE="; + }; + + nativeBuildInputs = [ + jdk + stripJavaArchivesHook + ]; + + buildPhase = '' + runHook preBuild + + mkdir -p build/classes + + find src/main/java -name "*.java" > sources.txt + + javac \ + --release 8 \ + -encoding UTF-8 \ + -d build/classes \ + @sources.txt + + jar cf commons-cli-${finalAttrs.version}.jar -C build/classes . + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + install -Dm644 commons-cli-${finalAttrs.version}.jar \ + $out/share/java/commons-cli-${finalAttrs.version}.jar + ln -s $out/share/java/commons-cli-${finalAttrs.version}.jar \ + $out/share/java/commons-cli.jar + + runHook postInstall + ''; + + strictDeps = true; + __structuredAttrs = true; + + meta = { + homepage = "https://commons.apache.org/proper/commons-cli/"; + description = "Apache Commons CLI provides a simple API for presenting, processing and validating a command line interface"; + license = lib.licenses.asl20; + maintainers = with lib.maintainers; [airone01]; + platforms = lib.platforms.all; + sourceProvenance = with lib.sourceTypes; [fromSource]; + }; +}) diff --git a/modules/pkgs/java/commons-io/_package.nix b/modules/pkgs/java/commons-io/_package.nix new file mode 100644 index 0000000..e185d4b --- /dev/null +++ b/modules/pkgs/java/commons-io/_package.nix @@ -0,0 +1,64 @@ +{ + lib, + stdenvNoCC, + fetchFromGitHub, + jdk, + stripJavaArchivesHook, +}: +stdenvNoCC.mkDerivation (finalAttrs: { + pname = "commons-io"; + version = "2.20.0"; + + src = fetchFromGitHub { + owner = "apache"; + repo = "commons-io"; + rev = "rel/commons-io-${finalAttrs.version}"; + hash = "sha256-fzfrmR0LhFihXe0TdEO3M4EIR0MxJw0AwhiSWRC6PVs="; + }; + + nativeBuildInputs = [ + jdk + stripJavaArchivesHook + ]; + + buildPhase = '' + runHook preBuild + + mkdir -p build/classes + + find src/main/java -name "*.java" > sources.txt + + javac \ + --release 8 \ + -encoding UTF-8 \ + -d build/classes \ + @sources.txt + + jar cf commons-io-${finalAttrs.version}.jar -C build/classes . + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + install -Dm644 commons-io-${finalAttrs.version}.jar \ + $out/share/java/commons-io-${finalAttrs.version}.jar + ln -s $out/share/java/commons-io-${finalAttrs.version}.jar \ + $out/share/java/commons-io.jar + + runHook postInstall + ''; + + strictDeps = true; + __structuredAttrs = true; + + meta = { + homepage = "https://commons.apache.org/proper/commons-io/"; + description = "Apache Commons IO is a library of utilities to assist with developing IO functionality"; + license = lib.licenses.asl20; + maintainers = with lib.maintainers; [airone01]; + platforms = lib.platforms.all; + sourceProvenance = with lib.sourceTypes; [fromSource]; + }; +}) diff --git a/modules/pkgs/java/commons-lang/_package.nix b/modules/pkgs/java/commons-lang/_package.nix new file mode 100644 index 0000000..0013475 --- /dev/null +++ b/modules/pkgs/java/commons-lang/_package.nix @@ -0,0 +1,69 @@ +{ + lib, + stdenvNoCC, + fetchFromGitHub, + jdk, + stripJavaArchivesHook, +}: +stdenvNoCC.mkDerivation (finalAttrs: { + pname = "commons-lang"; + version = "2.6"; + + src = fetchFromGitHub { + owner = "apache"; + repo = "commons-lang"; + rev = "LANG_2_6"; + hash = "sha256-7fjU0OKkHeyPZvZZ0lmWrxym8huTGEPvNwiUMpOcquk="; + }; + + nativeBuildInputs = [ + jdk + stripJavaArchivesHook + ]; + + buildPhase = '' + runHook preBuild + + mkdir -p build/classes + + # The lang/enum/ package uses 'enum' as a directory/package name, which + # became a reserved keyword in Java 5. Exclude it; the lang/enums/ package + # (with 's') provides the same functionality and compiles cleanly. + # Sources are Latin-1 encoded (pre-Maven UTF-8 convention). + find src/main/java -name "*.java" \ + ! -path "*/lang/enum/*" > sources.txt + + javac \ + --release 8 \ + -encoding ISO-8859-1 \ + -d build/classes \ + @sources.txt + + jar cf commons-lang-${finalAttrs.version}.jar -C build/classes . + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + install -Dm644 commons-lang-${finalAttrs.version}.jar \ + $out/share/java/commons-lang-${finalAttrs.version}.jar + ln -s $out/share/java/commons-lang-${finalAttrs.version}.jar \ + $out/share/java/commons-lang.jar + + runHook postInstall + ''; + + strictDeps = true; + __structuredAttrs = true; + + meta = { + homepage = "https://commons.apache.org/proper/commons-lang/"; + description = "Apache Commons Lang provides extra functionality for classes in java.lang"; + license = lib.licenses.asl20; + maintainers = with lib.maintainers; [airone01]; + platforms = lib.platforms.all; + sourceProvenance = with lib.sourceTypes; [fromSource]; + }; +}) diff --git a/modules/pkgs/java/commons-lang3/_package.nix b/modules/pkgs/java/commons-lang3/_package.nix new file mode 100644 index 0000000..1d0fc9e --- /dev/null +++ b/modules/pkgs/java/commons-lang3/_package.nix @@ -0,0 +1,64 @@ +{ + lib, + stdenvNoCC, + fetchFromGitHub, + jdk, + stripJavaArchivesHook, +}: +stdenvNoCC.mkDerivation (finalAttrs: { + pname = "commons-lang3"; + version = "3.19.0"; + + src = fetchFromGitHub { + owner = "apache"; + repo = "commons-lang"; + rev = "rel/commons-lang-${finalAttrs.version}"; + hash = "sha256-RB9PCDTCd8iAWOibrR9zL0xE6OMTmiJY0ZgLqgxBe2Q="; + }; + + nativeBuildInputs = [ + jdk + stripJavaArchivesHook + ]; + + buildPhase = '' + runHook preBuild + + mkdir -p build/classes + + find src/main/java -name "*.java" > sources.txt + + javac \ + --release 8 \ + -encoding UTF-8 \ + -d build/classes \ + @sources.txt + + jar cf commons-lang3-${finalAttrs.version}.jar -C build/classes . + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + install -Dm644 commons-lang3-${finalAttrs.version}.jar \ + $out/share/java/commons-lang3-${finalAttrs.version}.jar + ln -s $out/share/java/commons-lang3-${finalAttrs.version}.jar \ + $out/share/java/commons-lang3.jar + + runHook postInstall + ''; + + strictDeps = true; + __structuredAttrs = true; + + meta = { + homepage = "https://commons.apache.org/proper/commons-lang/"; + description = "Apache Commons Lang3 provides extra functionality for classes in java.lang"; + license = lib.licenses.asl20; + maintainers = with lib.maintainers; [airone01]; + platforms = lib.platforms.all; + sourceProvenance = with lib.sourceTypes; [fromSource]; + }; +}) diff --git a/modules/pkgs/java/default.nix b/modules/pkgs/java/default.nix index aa82e4d..8a0e0bd 100644 --- a/modules/pkgs/java/default.nix +++ b/modules/pkgs/java/default.nix @@ -17,6 +17,10 @@ # built via the bootstrap ant, compiled against our junit_4. # - jspecify: new attr; pure nullness annotations, javac-only leaf. # - jsoup: new attr; HTML parser, javac build, compiled against jspecify. +# - commons-cli: new attr; CLI argument parsing, javac-only leaf. +# - commons-io: new attr; IO utilities, javac-only leaf. +# - commons-lang: new attr; java.lang extras (2.x API), javac-only leaf. +# - commons-lang3: new attr; java.lang extras (3.x API), javac-only leaf. {inputs, ...}: let overlay = final: _prev: { java-hamcrest = final.callPackage ./java-hamcrest/_package.nix {}; @@ -25,6 +29,10 @@ ant = final.callPackage ./ant/_package.nix {}; jspecify = final.callPackage ./jspecify/_package.nix {}; jsoup = final.callPackage ./jsoup/_package.nix {}; + commons-cli = final.callPackage ./commons-cli/_package.nix {}; + commons-io = final.callPackage ./commons-io/_package.nix {}; + commons-lang = final.callPackage ./commons-lang/_package.nix {}; + commons-lang3 = final.callPackage ./commons-lang3/_package.nix {}; }; in { # Reusable overlay output (e.g. for downstream flakes or `nix build`). From 8ce3c1980f8d809eaf8f1593b5c634040d78a50a Mon Sep 17 00:00:00 2001 From: airone01 Date: Sun, 14 Jun 2026 00:49:49 +0200 Subject: [PATCH 6/9] docs(pkgs): document blobs in java packages --- modules/pkgs/java/README.txt | 108 +++++++++++++++++++ modules/pkgs/java/java.dot | 204 +++++++++++++++++++++++++---------- 2 files changed, 258 insertions(+), 54 deletions(-) diff --git a/modules/pkgs/java/README.txt b/modules/pkgs/java/README.txt index 3913e83..99ee0d6 100644 --- a/modules/pkgs/java/README.txt +++ b/modules/pkgs/java/README.txt @@ -2,3 +2,111 @@ This directory serves as a testing ground for complete Java bootstrap from source. I will probably setup an overlay which *will* make me cache miss a lot of packages in nixpkgs. Doing this for the love of the game. + +BLOB AUDIT (per-package) +------------------------ +Packages are checked at the git tag used in the bootstrap build. "blob" means +a precompiled binary in the source tree; "test-only" means it is not on any +production compile classpath. + + commons-cli 1.10.0 — clean + commons-io 2.20.0 — test-only .bin fixtures; not touched by our build + commons-lang 2.6 — clean + commons-lang3 3.19.0 — clean + jspecify 0.3.0 — gradle-wrapper.jar (wrapper, never invoked) + jsoup 1.17.2 — gradle-wrapper.jar (wrapper, never invoked) + java-hamcrest 3.0 — gradle-wrapper.jar (wrapper, never invoked) + junit4 4.13.2 — maven-wrapper.jar + lib/hamcrest-core-1.3.jar; + neither is used: our build passes -classpath + explicitly and never invokes the Maven wrapper + plexus-classworlds 2.8.0 — test-data/*.jar (test fixtures only) + plexus-utils 3.5.1 — clean + plexus-containers 2.2.0 — clean + plexus-interpolation 1.27 — clean + plexus-cipher 2.0 — clean + plexus-sec-dispatcher 2.0 — clean + javax.inject 1.0.5 — clean + jsr250-api / common-annotations-api 1.3.5 — clean + cdi-api 2.0.2 — clean + guava 32.1.3 — gradle-wrapper.jar in integration-tests/ subdir + guice 5.1.0 — clean + slf4j 1.7.36 — integration/lib/*.jar; "integration" is a + separate reactor module not needed for + slf4j-api or slf4j-simple + sisu 0.3.2 — test resources only (*.jar / *.class in + src/test/resources) + maven-wagon 3.5.3 — clean + modello 2.4.0 — clean + maven-resolver 1.9.22 — demo.jar in demo-snippets (not a build dep) + Maven 3.3.9 — all blobs are in src/test/resources except one: + maven-ant-tasks-2.1.1.jar at the repo root, + referenced only by build.xml (the Ant-based + build path). The Maven-based bootstrap never + invokes build.xml so this blob is inert. + commons-logging 1.1.3 — clean + commons-codec 1.6 — clean + httpcore 4.3.2 — clean + httpclient 4.3.5 — clean + +KNOWN BLOB ISSUES +----------------- +ant_1_7: RESOLVED. bootstrap.sh hardcoded lib/xercesImpl.jar, lib/xml-apis.jar, + and lib/optional/*.jar (ant-antunit-1.0.jar, junit-3.8.2.jar) on the javac + classpath. postPatch now removes them all. The build.xml check_for_optional_packages + target probes the classpath via ; without the jars, junit.present + and antunit.present are unset and those source files are excluded via + conditional selectors. The build succeeds; postInstall verifies JUnitTask and + AntUnitTask are absent from the output jars. + +ant 1.10.15: postPatch evicts junit/hamcrest from lib/optional, but + ant-antunit-1.4.1.jar is left behind. build.sh passes -lib lib/optional to + bootstrap ant, so antunit lands on the compile classpath for AntUnit tasks. + +SOURCE PROVENANCE ISSUES +------------------------ +aopalliance 1.0: No public git repository exists. Fedora's javapackages- + bootstrap sources it directly from Maven Central as a -sources.jar archive. + This is the only package in the graph without a proper VCS origin. + +Eclipse Aether 1.0.2: The original source lived at eclipse.org Gerrit (now + retired). The apache/maven-resolver GitHub repo begins at 2.0.0 and does + not contain the 1.0.x history. Building Aether 1.0.2 from source requires + fetching the eclipse.org source tarball (available on Maven Central as + -sources.jar) or switching to a newer maven-resolver release for Maven 3.5+. + +BOOTSTRAP CHAIN NOTES +--------------------- +wagon-http 2.10: pom lists httpclient:4.3.5, httpcore:4.3.2, and + commons-logging:1.1.3 as direct compile deps. httpclient additionally + requires httpcore, commons-logging, and commons-codec:1.6 (all compile + scope). All four HttpComponents nodes are now in the graph and are leaves + (no compile deps beyond javac itself). + +logback-core / logback-classic 1.0.7: all non-core deps (janino, jansi, + groovy, servlet-api, JavaMail, JMS spec) are declared true + in pom. A minimal javac build that excludes those source files compiles + cleanly with only SLF4J-API and logback-core — no new nodes needed. + +commons-lang 2.6 / commons-io 2.20.0: NOT compile deps of any Maven 3.3.9 + core module, but ARE needed by the Wagon 2.10 transport layer: + commons-lang → wagon-file, wagon-http-shared + commons-io → wagon-http-shared + wagon-http-shared in turn feeds wagon-http, which is bundled in the Maven + distribution. So both are necessary for a complete Maven 3.3.9 bootstrap. + +jsoup: wagon-http-shared 2.10 pom specifies jsoup:1.7.2 (compile scope); + jsoup 1.7.2 is a pure leaf (no compile deps). Our overlay ships 1.17.2, + which adds jspecify as a provided (annotation-only) compile dep but is + otherwise API-compatible for wagon's use. JSOUP → WAG_HTSH edge is now + in the graph. + +MAVEN VERSION NOTES +------------------- +Maven 3.3.9 uses guice:4.0:no_aop (the classifier that strips bytecode +weaving), so ASM and cglib are NOT required for the bootstrap despite being +listed as compile deps in the full guice pom. + +Maven 3.3.9 uses plexus-cipher:1.7 and plexus-sec-dispatcher:1.3 under the +org.sonatype.plexus groupId (now moved to codehaus-plexus). The newer +codehaus releases (cipher:2.0, sec-dispatcher:2.0) are API-compatible +drop-in replacements and are what javapackages-bootstrap targets. diff --git a/modules/pkgs/java/java.dot b/modules/pkgs/java/java.dot index 1fa0705..d1e14e5 100644 --- a/modules/pkgs/java/java.dot +++ b/modules/pkgs/java/java.dot @@ -71,12 +71,34 @@ digraph JavaBootstrapJPB { COM_IO [label="commons-io", fillcolor="#dcfce7", color="#15803d"]; COM_LANG [label="commons-lang", fillcolor="#dcfce7", color="#15803d"]; COM_LAN3 [label="commons-lang3", fillcolor="#dcfce7", color="#15803d"]; - SLF_API [label="slf4j-api", fillcolor="#dcfce7", color="#15803d"]; - SLF_SMP [label="slf4j-simple", fillcolor="#dcfce7", color="#15803d"]; JSPECIFY [label="jspecify", fillcolor="#dcfce7", color="#15803d"]; JSOUP [label="jsoup", fillcolor="#dcfce7", color="#15803d"]; } + // ════════════════════════════════════════════════════════════ + // Logging (SLF4J + Logback) + // ════════════════════════════════════════════════════════════ + subgraph cluster_logging { + label="Logging"; + style="rounded,filled"; fillcolor="#f0fdf4"; color="#166534"; fontcolor="#14532d"; fontname="Helvetica-Bold"; + SLF_API [label="slf4j-api", fillcolor="#bbf7d0", color="#166534"]; + SLF_SMP [label="slf4j-simple", fillcolor="#bbf7d0", color="#166534"]; + LOGBACK_CORE [label="logback-core", fillcolor="#bbf7d0", color="#166534"]; + LOGBACK [label="logback-classic", fillcolor="#bbf7d0", color="#166534"]; + } + + // ════════════════════════════════════════════════════════════ + // HttpComponents (compile deps of wagon-http and httpclient) + // ════════════════════════════════════════════════════════════ + subgraph cluster_httpcomp { + label="HttpComponents 4.3.x"; + style="rounded,filled"; fillcolor="#f0f9ff"; color="#0369a1"; fontcolor="#0c4a6e"; fontname="Helvetica-Bold"; + COM_LOG [label="commons-logging 1.1.3", fillcolor="#e0f2fe", color="#0369a1"]; + COM_CODEC [label="commons-codec 1.6", fillcolor="#e0f2fe", color="#0369a1"]; + HTTPCORE [label="httpcore 4.3.2", fillcolor="#e0f2fe", color="#0369a1"]; + HTTPCLIENT [label="httpclient 4.3.5", fillcolor="#bae6fd", color="#0369a1"]; + } + // ════════════════════════════════════════════════════════════ // Wagon // ════════════════════════════════════════════════════════════ @@ -164,6 +186,12 @@ digraph JavaBootstrapJPB { JC -> SLF_API; JC -> SLF_SMP; JC -> JSPECIFY; + JC -> LOGBACK_CORE; + JC -> LOGBACK; + JC -> COM_LOG; + JC -> COM_CODEC; + JC -> HTTPCORE; + JC -> HTTPCLIENT; JC -> WAG_API; JC -> WAG_HTSH; JC -> WAG_FILE; @@ -195,37 +223,64 @@ digraph JavaBootstrapJPB { JU4 -> ANT; // ── Plexus internal ── + PL_UTIL -> PL_SEC; PL_CIPH -> PL_SEC; JC -> PL_SEC; // ── DI primitives ── JX_INJ -> GUICE; - AOPAL -> GUICE; + AOPAL -> GUICE; + GUAVA -> GUICE; JX_JSR -> JX_CDI; - GUICE -> SISU_INJ; - SISU_INJ -> SISU_PLEX; - JX_CDI -> SISU_PLEX; - PL_CA -> SISU_PLEX; + GUICE -> SISU_INJ; + SISU_INJ -> SISU_PLEX; + JX_CDI -> SISU_PLEX; + PL_CA -> SISU_PLEX; + PL_CW -> SISU_PLEX; + PL_UTIL -> SISU_PLEX; + GUICE -> SISU_PLEX; // ── Commons / misc internal ── + // jspecify is a provided (compile-only annotation) dep of jsoup 1.17.2 JSPECIFY -> JSOUP; + // wagon-http-shared 2.10 specifies jsoup:1.7.2; our overlay supplies 1.17.2 + JSOUP -> WAG_HTSH; + + // ── Logging ── + SLF_API -> LOGBACK; + LOGBACK_CORE -> LOGBACK; + // logback-core and logback-classic optional deps (janino, jansi, groovy, + // servlet-api, JavaMail, JMS) are all true in pom; + // a minimal javac build excludes those source files and needs nothing more. + + // ── HttpComponents internal ── + HTTPCORE -> HTTPCLIENT; + COM_LOG -> HTTPCLIENT; + COM_CODEC -> HTTPCLIENT; // ── Wagon classpath order ── - WAG_API -> WAG_FILE; - WAG_API -> WAG_HTTP; - WAG_HTSH -> WAG_HTTP; + WAG_API -> WAG_FILE; + WAG_API -> WAG_HTTP; + WAG_HTSH -> WAG_HTTP; + COM_LANG -> WAG_FILE; + COM_LANG -> WAG_HTSH; + COM_IO -> WAG_HTSH; + // wagon-http directly lists httpclient, httpcore, commons-logging in its pom + HTTPCLIENT -> WAG_HTTP; + HTTPCORE -> WAG_HTTP; + COM_LOG -> WAG_HTTP; // ── Aether classpath order ── - AE_API -> AE_SPI; - AE_API -> AE_UTIL; - AE_API -> AE_IMPL; - AE_SPI -> AE_IMPL; + AE_API -> AE_SPI; + AE_API -> AE_UTIL; + AE_API -> AE_IMPL; + AE_SPI -> AE_IMPL; AE_UTIL -> AE_IMPL; - AE_API -> AE_CONN; - AE_SPI -> AE_CONN; + AE_API -> AE_CONN; + AE_SPI -> AE_CONN; AE_UTIL -> AE_CONN; - AE_API -> AE_TRAN; - AE_SPI -> AE_TRAN; + AE_API -> AE_TRAN; + AE_SPI -> AE_TRAN; WAG_API -> AE_TRAN; // ── Modello generates Maven model sources ── @@ -234,54 +289,95 @@ digraph JavaBootstrapJPB { MODELLO -> M_REPOM; // ── Maven module compile order ── - M_MODEL -> M_MODELB; - M_BUILD -> M_MODELB; - PL_INTP -> M_MODELB; - GUAVA -> M_MODELB; - M_SET -> M_SETB; - PL_SEC -> M_SETB; - M_ART -> M_CORE; - M_MODEL -> M_CORE; - M_MODELB -> M_CORE; - M_SETB -> M_CORE; - M_REPOM -> M_CORE; - M_PLUG -> M_CORE; - M_AETHP -> M_CORE; - AE_API -> M_AETHP; + // maven-model-builder + M_MODEL -> M_MODELB; + M_BUILD -> M_MODELB; + M_ART -> M_MODELB; + PL_INTP -> M_MODELB; + PL_CA -> M_MODELB; + PL_UTIL -> M_MODELB; + GUAVA -> M_MODELB; + COM_LAN3 -> M_MODELB; + + // maven-settings-builder + M_SET -> M_SETB; + M_BUILD -> M_SETB; + PL_SEC -> M_SETB; + PL_CA -> M_SETB; + PL_INTP -> M_SETB; + PL_UTIL -> M_SETB; + COM_LAN3 -> M_SETB; + + // maven-aether-provider + AE_API -> M_AETHP; AE_UTIL -> M_AETHP; AE_CONN -> M_AETHP; AE_TRAN -> M_AETHP; - AE_IMPL -> M_CORE; + + // maven-core + M_ART -> M_CORE; + M_MODEL -> M_CORE; + M_MODELB -> M_CORE; + M_SET -> M_CORE; + M_SETB -> M_CORE; + M_REPOM -> M_CORE; + M_PLUG -> M_CORE; + M_AETHP -> M_CORE; + AE_API -> M_CORE; + AE_IMPL -> M_CORE; + AE_UTIL -> M_CORE; SISU_PLEX -> M_CORE; - GUICE -> M_CORE; - PL_CA -> M_CORE; - PL_INTP -> M_CORE; + GUICE -> M_CORE; + PL_CA -> M_CORE; + PL_CW -> M_CORE; + PL_INTP -> M_CORE; + PL_UTIL -> M_CORE; + PL_SEC -> M_CORE; + COM_LAN3 -> M_CORE; + + // maven-compat + M_ART -> M_COMPAT; + M_MODEL -> M_COMPAT; + M_MODELB -> M_COMPAT; + M_SET -> M_COMPAT; + M_CORE -> M_COMPAT; + WAG_API -> M_COMPAT; + PL_CA -> M_COMPAT; + PL_INTP -> M_COMPAT; + PL_UTIL -> M_COMPAT; + SISU_PLEX -> M_COMPAT; + + // maven-embedder + M_CORE -> M_EMB; M_MODELB -> M_EMB; - M_SET -> M_EMB; - M_PLUG -> M_EMB; - M_CORE -> M_EMB; - PL_CW -> M_EMB; - PL_UTIL -> M_EMB; - SLF_API -> M_EMB; - SLF_SMP -> M_EMB; + M_SET -> M_EMB; + M_PLUG -> M_EMB; + PL_CW -> M_EMB; + PL_CA -> M_EMB; + PL_UTIL -> M_EMB; + PL_CIPH -> M_EMB; + PL_SEC -> M_EMB; + SISU_PLEX -> M_EMB; + SLF_API -> M_EMB; + SLF_SMP -> M_EMB; + LOGBACK -> M_EMB; COM_LAN3 -> M_EMB; - COM_CLI -> M_EMB; - JSOUP -> M_EMB; - WAG_API -> M_COMPAT; - COM_LANG -> M_COMPAT; - COM_IO -> M_COMPAT; - M_CORE -> M_COMPAT; + COM_CLI -> M_EMB; // ── Modules assemble into the product ── - M_EMB -> MVN339; - M_CORE -> MVN339; + M_EMB -> MVN339; + M_CORE -> MVN339; M_COMPAT -> MVN339; - ANT -> MVN339 [style=dashed, label="built with"]; + ANT -> MVN339 [style=dashed, label="built with"]; + WAG_FILE -> MVN339 [style=dashed, label="bundled in dist"]; + WAG_HTTP -> MVN339 [style=dashed, label="bundled in dist"]; + // NOTE: maven-compat is a runtime (not compile) dep of maven-embedder; + // build order still requires M_COMPAT before M_EMB. // ── Onward ── MVN339 -> MVN35; - MVN35 -> GDEPS; - GDEPS -> GRADLE; + MVN35 -> GDEPS; + GDEPS -> GRADLE; // ── Circular back-edges (upstream build-tool reality, broken by javac bypass) ── GRADLE -> HC [style=dashed, color="#b45309", fontcolor="#b45309", label="upstream: Gradle", constraint=false]; From 5fbd7064baa8830439da76120c598ee404d6fb50 Mon Sep 17 00:00:00 2001 From: airone01 Date: Sun, 14 Jun 2026 23:41:46 +0200 Subject: [PATCH 7/9] fix(pkgs): ant_1_7 evict binary blobs --- modules/pkgs/java/ant_1_7/_package.nix | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/modules/pkgs/java/ant_1_7/_package.nix b/modules/pkgs/java/ant_1_7/_package.nix index fd0c02f..3373b33 100644 --- a/modules/pkgs/java/ant_1_7/_package.nix +++ b/modules/pkgs/java/ant_1_7/_package.nix @@ -20,13 +20,29 @@ stdenv.mkDerivation (finalAttrs: { ]; postPatch = '' - # The bootstrap target (-> dist-lite) depends on test-jar, which hard-fails - # with ``. We bootstrap with javac only (no - # JUnit), and a bootstrap ant doesn't need the test-utility jar, so drop - # that dependency. dist-lite just copies whatever jars were built, so the - # runnable distribution is unaffected. + # dist-lite depends on test-jar, which has an unconditional + # — drop it; we don't need the test jar. substituteInPlace build.xml \ --replace-fail 'depends="jars,test-jar"' 'depends="jars"' + + # Evict all binary blobs. bootstrap.sh hardcodes lib/xercesImpl.jar and + # lib/xml-apis.jar on the classpath and glob-adds lib/optional/*.jar. + # The ant-driven build phase runs check_for_optional_packages, which + # probes the classpath via ; without the jars, junit.present + # and antunit.present are not set and those source files are excluded + # from compilation by the conditional selectors in build.xml. + rm -f lib/xercesImpl.jar lib/xml-apis.jar \ + lib/optional/junit-*.jar lib/optional/ant-antunit-*.jar + ''; + + postInstall = '' + # Verify the optional tasks dependent on the removed blobs are absent. + for j in $out/share/ant/lib/*.jar; do + if jar tf "$j" 2>/dev/null | grep -q "JUnitTask\|AntUnitTask"; then + echo "ant_1_7: JUnitTask or AntUnitTask found in $j — blob eviction failed" >&2 + exit 1 + fi + done ''; buildPhase = '' From 584468b1426acc1329ad4ced9e90439e8281e0b3 Mon Sep 17 00:00:00 2001 From: airone01 Date: Mon, 15 Jun 2026 00:10:14 +0200 Subject: [PATCH 8/9] feat(pkgs): junit_3 build from source, ant_1_7 add junit capabilities --- modules/pkgs/java/README.txt | 14 +++--- modules/pkgs/java/ant_1_7/_package.nix | 30 ++++++++---- modules/pkgs/java/default.nix | 3 ++ modules/pkgs/java/junit_3/_package.nix | 66 ++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 16 deletions(-) create mode 100644 modules/pkgs/java/junit_3/_package.nix diff --git a/modules/pkgs/java/README.txt b/modules/pkgs/java/README.txt index 99ee0d6..a026f42 100644 --- a/modules/pkgs/java/README.txt +++ b/modules/pkgs/java/README.txt @@ -43,6 +43,8 @@ production compile classpath. referenced only by build.xml (the Ant-based build path). The Maven-based bootstrap never invokes build.xml so this blob is inert. + junit 3.8.2 — no git repo; sourced from Maven Central -sources.jar + (same as aopalliance). No blobs in the sources.jar. commons-logging 1.1.3 — clean commons-codec 1.6 — clean httpcore 4.3.2 — clean @@ -51,12 +53,12 @@ production compile classpath. KNOWN BLOB ISSUES ----------------- ant_1_7: RESOLVED. bootstrap.sh hardcoded lib/xercesImpl.jar, lib/xml-apis.jar, - and lib/optional/*.jar (ant-antunit-1.0.jar, junit-3.8.2.jar) on the javac - classpath. postPatch now removes them all. The build.xml check_for_optional_packages - target probes the classpath via ; without the jars, junit.present - and antunit.present are unset and those source files are excluded via - conditional selectors. The build succeeds; postInstall verifies JUnitTask and - AntUnitTask are absent from the output jars. + ant-antunit-1.0.jar, and junit-3.8.2.jar on the javac classpath. + postPatch removes xercesImpl.jar, xml-apis.jar, and the antunit blob. + The junit blob is replaced by junit_3 (built from source): our from-source + jar is copied to lib/optional/ before the build, so check_for_optional_packages + finds junit.present=true and compiles JUnitTask against it. + postInstall verifies JUnitTask IS present in ant-junit.jar. ant 1.10.15: postPatch evicts junit/hamcrest from lib/optional, but ant-antunit-1.4.1.jar is left behind. build.sh passes -lib lib/optional to diff --git a/modules/pkgs/java/ant_1_7/_package.nix b/modules/pkgs/java/ant_1_7/_package.nix index 3373b33..b414443 100644 --- a/modules/pkgs/java/ant_1_7/_package.nix +++ b/modules/pkgs/java/ant_1_7/_package.nix @@ -4,6 +4,7 @@ fetchurl, jdk, makeWrapper, + junit_3, }: stdenv.mkDerivation (finalAttrs: { pname = "ant"; @@ -19,30 +20,39 @@ stdenv.mkDerivation (finalAttrs: { makeWrapper ]; + depsVersions.junit = "3.8.2"; + postPatch = '' # dist-lite depends on test-jar, which has an unconditional # — drop it; we don't need the test jar. substituteInPlace build.xml \ --replace-fail 'depends="jars,test-jar"' 'depends="jars"' - # Evict all binary blobs. bootstrap.sh hardcodes lib/xercesImpl.jar and - # lib/xml-apis.jar on the classpath and glob-adds lib/optional/*.jar. - # The ant-driven build phase runs check_for_optional_packages, which - # probes the classpath via ; without the jars, junit.present - # and antunit.present are not set and those source files are excluded - # from compilation by the conditional selectors in build.xml. + # Evict binary blobs and replace the junit blob with our from-source build. + # bootstrap.sh hardcodes lib/xercesImpl.jar and lib/xml-apis.jar on the + # classpath and glob-adds lib/optional/*.jar. Removing xercesImpl/xml-apis + # leaves trax.present true (JDK has javax.xml.transform.*) so the XSLT tasks + # still compile. Replacing the junit blob with junit_3 sets junit.present and + # compiles JUnitTask against our from-source jar instead of the blob. rm -f lib/xercesImpl.jar lib/xml-apis.jar \ lib/optional/junit-*.jar lib/optional/ant-antunit-*.jar + mkdir -p lib/optional + cp ${junit_3}/share/java/junit-${finalAttrs.depsVersions.junit}.jar lib/optional/ ''; postInstall = '' - # Verify the optional tasks dependent on the removed blobs are absent. + # Verify JUnitTask was compiled (junit_3 was found by check_for_optional_packages). + found=0 for j in $out/share/ant/lib/*.jar; do - if jar tf "$j" 2>/dev/null | grep -q "JUnitTask\|AntUnitTask"; then - echo "ant_1_7: JUnitTask or AntUnitTask found in $j — blob eviction failed" >&2 - exit 1 + if jar tf "$j" 2>/dev/null | grep -q "JUnitTask"; then + found=1 + break fi done + if [ $found -eq 0 ]; then + echo "ant_1_7: JUnitTask not found in output — junit_3 was not picked up" >&2 + exit 1 + fi ''; buildPhase = '' diff --git a/modules/pkgs/java/default.nix b/modules/pkgs/java/default.nix index 8a0e0bd..70a86d6 100644 --- a/modules/pkgs/java/default.nix +++ b/modules/pkgs/java/default.nix @@ -21,9 +21,12 @@ # - commons-io: new attr; IO utilities, javac-only leaf. # - commons-lang: new attr; java.lang extras (2.x API), javac-only leaf. # - commons-lang3: new attr; java.lang extras (3.x API), javac-only leaf. +# - junit_3: new attr; JUnit 3.8.2 built from Maven Central sources.jar +# (no public git repo); wired into ant_1_7 so JUnitTask compiles. {inputs, ...}: let overlay = final: _prev: { java-hamcrest = final.callPackage ./java-hamcrest/_package.nix {}; + junit_3 = final.callPackage ./junit_3/_package.nix {}; junit_4 = final.callPackage ./junit_4/_package.nix {}; ant_1_7 = final.callPackage ./ant_1_7/_package.nix {}; ant = final.callPackage ./ant/_package.nix {}; diff --git a/modules/pkgs/java/junit_3/_package.nix b/modules/pkgs/java/junit_3/_package.nix new file mode 100644 index 0000000..e637b13 --- /dev/null +++ b/modules/pkgs/java/junit_3/_package.nix @@ -0,0 +1,66 @@ +{ + lib, + stdenvNoCC, + fetchurl, + jdk, + stripJavaArchivesHook, +}: +stdenvNoCC.mkDerivation (finalAttrs: { + pname = "junit"; + version = "3.8.2"; + + # No public git repository for JUnit 3.x; sourced from Maven Central sources.jar + # (same situation as aopalliance). fetchzip can't unpack .jar extensions, so + # we use fetchurl and extract manually with `jar xf` in buildPhase. + src = fetchurl { + url = "https://repo1.maven.org/maven2/junit/junit/${finalAttrs.version}/junit-${finalAttrs.version}-sources.jar"; + hash = "sha256-eQSHmRRBcRItEPj1e7r1QjieVFKnIQwmNgAFSOmEB4o="; + }; + + nativeBuildInputs = [ + jdk + stripJavaArchivesHook + ]; + + unpackPhase = "true"; + + buildPhase = '' + runHook preBuild + + jar xf $src + + mkdir -p build/classes + find . -name "*.java" \ + ! -path "./junit/tests/*" \ + ! -path "./junit/samples/*" \ + > sources.txt + + javac --release 8 -encoding UTF-8 -d build/classes @sources.txt + jar cf junit-${finalAttrs.version}.jar -C build/classes . + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + install -Dm644 junit-${finalAttrs.version}.jar \ + $out/share/java/junit-${finalAttrs.version}.jar + ln -s $out/share/java/junit-${finalAttrs.version}.jar \ + $out/share/java/junit.jar + + runHook postInstall + ''; + + strictDeps = true; + __structuredAttrs = true; + + meta = { + description = "JUnit 3 testing framework for Java"; + homepage = "https://junit.org/junit4/"; + license = lib.licenses.cpl10; + maintainers = with lib.maintainers; [airone01]; + platforms = lib.platforms.all; + sourceProvenance = with lib.sourceTypes; [fromSource]; + }; +}) From 1579a74cce391ed3c0500fc677ae5dc2429f0732 Mon Sep 17 00:00:00 2001 From: airone01 Date: Mon, 15 Jun 2026 00:36:34 +0200 Subject: [PATCH 9/9] feat(pkgs): more ant_1_7 capabilities --- modules/pkgs/java/README.txt | 16 ++++-- modules/pkgs/java/ant_1_7/_package.nix | 37 ++++++++---- modules/pkgs/java/default.nix | 6 ++ modules/pkgs/java/xerces_j/_package.nix | 76 +++++++++++++++++++++++++ modules/pkgs/java/xml-apis/_package.nix | 63 ++++++++++++++++++++ 5 files changed, 183 insertions(+), 15 deletions(-) create mode 100644 modules/pkgs/java/xerces_j/_package.nix create mode 100644 modules/pkgs/java/xml-apis/_package.nix diff --git a/modules/pkgs/java/README.txt b/modules/pkgs/java/README.txt index a026f42..75cd445 100644 --- a/modules/pkgs/java/README.txt +++ b/modules/pkgs/java/README.txt @@ -45,6 +45,9 @@ production compile classpath. invokes build.xml so this blob is inert. junit 3.8.2 — no git repo; sourced from Maven Central -sources.jar (same as aopalliance). No blobs in the sources.jar. + xml-apis 1.3.04 — no git repo; sourced from Maven Central -sources.jar. + No blobs in the sources.jar. + xercesImpl 2.9.1 — clean (2.9.0 had no source tarball; 2.9.1 used instead) commons-logging 1.1.3 — clean commons-codec 1.6 — clean httpcore 4.3.2 — clean @@ -54,11 +57,14 @@ KNOWN BLOB ISSUES ----------------- ant_1_7: RESOLVED. bootstrap.sh hardcoded lib/xercesImpl.jar, lib/xml-apis.jar, ant-antunit-1.0.jar, and junit-3.8.2.jar on the javac classpath. - postPatch removes xercesImpl.jar, xml-apis.jar, and the antunit blob. - The junit blob is replaced by junit_3 (built from source): our from-source - jar is copied to lib/optional/ before the build, so check_for_optional_packages - finds junit.present=true and compiles JUnitTask against it. - postInstall verifies JUnitTask IS present in ant-junit.jar. + postPatch removes all four blobs. xercesImpl and xml-apis are replaced by + from-source builds (xerces_j 2.9.1 and xml-apis 1.3.04) copied to lib/; dist-lite + then ships them in the output distribution. The junit blob is replaced by junit_3 + (built from source) copied to lib/optional/; check_for_optional_packages finds + junit.present=true and compiles JUnitTask against it. ant-antunit is not replaced + (not needed for the bootstrap). + postInstall verifies: JUnitTask present in ant-junit.jar, xercesImpl-*.jar and + xml-apis-*.jar present in $out/share/ant/lib/. ant 1.10.15: postPatch evicts junit/hamcrest from lib/optional, but ant-antunit-1.4.1.jar is left behind. build.sh passes -lib lib/optional to diff --git a/modules/pkgs/java/ant_1_7/_package.nix b/modules/pkgs/java/ant_1_7/_package.nix index b414443..a2533c2 100644 --- a/modules/pkgs/java/ant_1_7/_package.nix +++ b/modules/pkgs/java/ant_1_7/_package.nix @@ -5,6 +5,8 @@ jdk, makeWrapper, junit_3, + xml-apis, + xerces_j, }: stdenv.mkDerivation (finalAttrs: { pname = "ant"; @@ -21,6 +23,8 @@ stdenv.mkDerivation (finalAttrs: { ]; depsVersions.junit = "3.8.2"; + depsVersions.xercesImpl = "2.9.1"; + depsVersions.xmlApis = "1.3.04"; postPatch = '' # dist-lite depends on test-jar, which has an unconditional @@ -28,30 +32,40 @@ stdenv.mkDerivation (finalAttrs: { substituteInPlace build.xml \ --replace-fail 'depends="jars,test-jar"' 'depends="jars"' - # Evict binary blobs and replace the junit blob with our from-source build. + # Evict all binary blobs and replace with from-source builds. # bootstrap.sh hardcodes lib/xercesImpl.jar and lib/xml-apis.jar on the - # classpath and glob-adds lib/optional/*.jar. Removing xercesImpl/xml-apis - # leaves trax.present true (JDK has javax.xml.transform.*) so the XSLT tasks - # still compile. Replacing the junit blob with junit_3 sets junit.present and - # compiles JUnitTask against our from-source jar instead of the blob. + # classpath; we replace them so the compile classpath is blob-free. + # dist-lite copies lib/*.jar to the output distribution, so replacing the + # blobs also makes the shipped ant blob-free. + # junit_3 replaces the optional junit blob; check_for_optional_packages then + # finds junit.present and compiles JUnitTask. rm -f lib/xercesImpl.jar lib/xml-apis.jar \ lib/optional/junit-*.jar lib/optional/ant-antunit-*.jar + cp ${xml-apis}/share/java/xml-apis-${finalAttrs.depsVersions.xmlApis}.jar lib/ + cp ${xerces_j}/share/java/xercesImpl-${finalAttrs.depsVersions.xercesImpl}.jar lib/ mkdir -p lib/optional cp ${junit_3}/share/java/junit-${finalAttrs.depsVersions.junit}.jar lib/optional/ ''; postInstall = '' - # Verify JUnitTask was compiled (junit_3 was found by check_for_optional_packages). + # Verify JUnitTask compiled and all expected jars are in the distribution. found=0 for j in $out/share/ant/lib/*.jar; do if jar tf "$j" 2>/dev/null | grep -q "JUnitTask"; then - found=1 - break + found=1; break fi done if [ $found -eq 0 ]; then - echo "ant_1_7: JUnitTask not found in output — junit_3 was not picked up" >&2 - exit 1 + echo "ant_1_7: JUnitTask not found — junit_3 was not picked up" >&2; exit 1 + fi + if ! ls $out/share/ant/lib/xercesImpl-*.jar &>/dev/null; then + echo "ant_1_7: xercesImpl jar missing from output" >&2; exit 1 + fi + if ! ls $out/share/ant/lib/xml-apis-*.jar &>/dev/null; then + echo "ant_1_7: xml-apis jar missing from output" >&2; exit 1 + fi + if ! ls $out/share/ant/lib/optional/junit-*.jar &>/dev/null; then + echo "ant_1_7: junit jar missing from lib/optional" >&2; exit 1 fi ''; @@ -73,6 +87,9 @@ stdenv.mkDerivation (finalAttrs: { mkdir -p $out/share/ant cp -r bootstrap/* $out/share/ant/ rm -rf $out/share/ant/bin/*.bat + mkdir -p $out/share/ant/lib/optional + cp ${junit_3}/share/java/junit-${finalAttrs.depsVersions.junit}.jar \ + $out/share/ant/lib/optional/ makeWrapper $out/share/ant/bin/ant $out/bin/ant \ --set ANT_HOME $out/share/ant \ diff --git a/modules/pkgs/java/default.nix b/modules/pkgs/java/default.nix index 70a86d6..304a07d 100644 --- a/modules/pkgs/java/default.nix +++ b/modules/pkgs/java/default.nix @@ -23,10 +23,16 @@ # - commons-lang3: new attr; java.lang extras (3.x API), javac-only leaf. # - junit_3: new attr; JUnit 3.8.2 built from Maven Central sources.jar # (no public git repo); wired into ant_1_7 so JUnitTask compiles. +# - xml-apis: new attr; W3C DOM/SAX/JAXP stubs 1.3.04 from Maven Central +# sources.jar; compile dep of xerces_j and ant_1_7 bootstrap. +# - xerces_j: new attr; Xerces-J 2.9.1 from GitHub (2.9.0 has no source +# tarball); wired into ant_1_7 lib/ so dist-lite ships it. {inputs, ...}: let overlay = final: _prev: { java-hamcrest = final.callPackage ./java-hamcrest/_package.nix {}; junit_3 = final.callPackage ./junit_3/_package.nix {}; + "xml-apis" = final.callPackage ./xml-apis/_package.nix {}; + xerces_j = final.callPackage ./xerces_j/_package.nix {}; junit_4 = final.callPackage ./junit_4/_package.nix {}; ant_1_7 = final.callPackage ./ant_1_7/_package.nix {}; ant = final.callPackage ./ant/_package.nix {}; diff --git a/modules/pkgs/java/xerces_j/_package.nix b/modules/pkgs/java/xerces_j/_package.nix new file mode 100644 index 0000000..b63dd39 --- /dev/null +++ b/modules/pkgs/java/xerces_j/_package.nix @@ -0,0 +1,76 @@ +{ + lib, + stdenvNoCC, + fetchFromGitHub, + jdk, + stripJavaArchivesHook, + xml-apis, +}: +stdenvNoCC.mkDerivation (finalAttrs: { + pname = "xercesImpl"; + version = "2.9.1"; + + # Xerces-J 2.9.0 (the version bundled in ant_1_7) has no source tarball. + # 2.9.1 is the next point release; API-compatible and available on GitHub. + src = fetchFromGitHub { + owner = "apache"; + repo = "xerces2-j"; + rev = "Xerces-J_${lib.replaceStrings ["."] ["_"] finalAttrs.version}"; + hash = "sha256-uhtVwm8SCSyEkUo9uTM07x+BrMqSSZP42GBMFLM8VF4="; + }; + + nativeBuildInputs = [ + jdk + stripJavaArchivesHook + ]; + + buildPhase = '' + runHook preBuild + + mkdir -p build/classes + + # XMLCatalogResolver uses the optional xml-resolver package; exclude it. + find src -name "*.java" \ + ! -path "*/xerces/util/XMLCatalogResolver.java" \ + > sources.txt + + javac --release 8 -encoding UTF-8 \ + -classpath "${xml-apis}/share/java/xml-apis.jar" \ + -d build/classes \ + @sources.txt + + # Bundle non-Java resources (message bundles, .properties, .res files) + find src -not -name "*.java" -type f | while read f; do + rel="''${f#src/}" + mkdir -p "build/classes/''${rel%/*}" + cp "$f" "build/classes/$rel" + done + + jar cf xercesImpl-${finalAttrs.version}.jar -C build/classes . + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + install -Dm644 xercesImpl-${finalAttrs.version}.jar \ + $out/share/java/xercesImpl-${finalAttrs.version}.jar + ln -s $out/share/java/xercesImpl-${finalAttrs.version}.jar \ + $out/share/java/xercesImpl.jar + + runHook postInstall + ''; + + strictDeps = true; + __structuredAttrs = true; + + meta = { + description = "Apache Xerces-J XML parser"; + homepage = "https://xerces.apache.org/xerces2-j/"; + license = lib.licenses.asl20; + maintainers = with lib.maintainers; [airone01]; + platforms = lib.platforms.all; + sourceProvenance = with lib.sourceTypes; [fromSource]; + }; +}) diff --git a/modules/pkgs/java/xml-apis/_package.nix b/modules/pkgs/java/xml-apis/_package.nix new file mode 100644 index 0000000..81db0db --- /dev/null +++ b/modules/pkgs/java/xml-apis/_package.nix @@ -0,0 +1,63 @@ +{ + lib, + stdenvNoCC, + fetchurl, + jdk, + stripJavaArchivesHook, +}: +stdenvNoCC.mkDerivation (finalAttrs: { + pname = "xml-apis"; + version = "1.3.04"; + + # No public git repository; sourced from Maven Central sources.jar. + # fetchzip doesn't handle .jar extensions; extract manually with jar xf. + src = fetchurl { + url = "https://repo1.maven.org/maven2/xml-apis/xml-apis/${finalAttrs.version}/xml-apis-${finalAttrs.version}-sources.jar"; + hash = "sha256-0xAhmlcn1UJMS3qFmRs8CCGI6kITOFHNL6wG+W2DN8o="; + }; + + nativeBuildInputs = [ + jdk + stripJavaArchivesHook + ]; + + unpackPhase = "true"; + + buildPhase = '' + runHook preBuild + + jar xf $src + + mkdir -p build/classes + find . -name "*.java" ! -path "./META-INF/*" > sources.txt + + javac --release 8 -encoding UTF-8 -d build/classes @sources.txt + + jar cf xml-apis-${finalAttrs.version}.jar -C build/classes . + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + install -Dm644 xml-apis-${finalAttrs.version}.jar \ + $out/share/java/xml-apis-${finalAttrs.version}.jar + ln -s $out/share/java/xml-apis-${finalAttrs.version}.jar \ + $out/share/java/xml-apis.jar + + runHook postInstall + ''; + + strictDeps = true; + __structuredAttrs = true; + + meta = { + description = "W3C DOM, SAX and JAXP API definitions"; + homepage = "https://xml.apache.org/commons/"; + license = lib.licenses.asl20; + maintainers = with lib.maintainers; [airone01]; + platforms = lib.platforms.all; + sourceProvenance = with lib.sourceTypes; [fromSource]; + }; +})