1# Java {#sec-language-java} 2 3Ant-based Java packages are typically built from source as follows: 4 5```nix 6stdenv.mkDerivation { 7 pname = "..."; 8 version = "..."; 9 10 src = fetchurl { /* ... */ }; 11 12 nativeBuildInputs = [ 13 ant 14 jdk 15 stripJavaArchivesHook # removes timestamp metadata from jar files 16 ]; 17 18 buildPhase = '' 19 runHook preBuild 20 ant # build the project using ant 21 runHook postBuild 22 ''; 23 24 installPhase = '' 25 runHook preInstall 26 27 # copy generated jar file(s) to an appropriate location in $out 28 install -Dm644 build/foo.jar $out/share/java/foo.jar 29 30 runHook postInstall 31 ''; 32} 33``` 34 35Note that `jdk` is an alias for the OpenJDK (self-built where available, 36or pre-built via Zulu). Platforms with OpenJDK not (yet) in Nixpkgs 37(`Aarch32`, `Aarch64`) point to the (unfree) `oraclejdk`. 38 39Also note that not using `stripJavaArchivesHook` will likely cause the 40generated `.jar` files to be non-deterministic, which is not optimal. 41Using it, however, does not always guarantee reproducibility. 42 43JAR files that are intended to be used by other packages should be 44installed in `$out/share/java`. JDKs have a stdenv setup hook that add 45any JARs in the `share/java` directories of the build inputs to the 46`CLASSPATH` environment variable. For instance, if the package `libfoo` 47installs a JAR named `foo.jar` in its `share/java` directory, and 48another package declares the attribute 49 50```nix 51{ 52 buildInputs = [ libfoo ]; 53 nativeBuildInputs = [ jdk ]; 54} 55``` 56 57then `CLASSPATH` will be set to 58`/nix/store/...-libfoo/share/java/foo.jar`. 59 60Private JARs should be installed in a location like 61`$out/share/package-name`. 62 63If your Java package provides a program, you need to generate a wrapper 64script to run it using a JRE. You can use `makeWrapper` for this: 65 66```nix 67{ 68 nativeBuildInputs = [ makeWrapper ]; 69 70 installPhase = '' 71 mkdir -p $out/bin 72 makeWrapper ${jre}/bin/java $out/bin/foo \ 73 --add-flags "-cp $out/share/java/foo.jar org.foo.Main" 74 ''; 75} 76``` 77 78Since the introduction of the Java Platform Module System in Java 9, 79Java distributions typically no longer ship with a general-purpose JRE: 80instead, they allow generating a JRE with only the modules required for 81your application(s). Because we can't predict what modules will be 82needed on a general-purpose system, the default jre package is the full 83JDK. When building a minimal system/image, you can override the 84`modules` parameter on `jre_minimal` to build a JRE with only the 85modules relevant for you: 86 87```nix 88let 89 my_jre = pkgs.jre_minimal.override { 90 modules = [ 91 # The modules used by 'something' and 'other' combined: 92 "java.base" 93 "java.logging" 94 ]; 95 }; 96 something = (pkgs.something.override { jre = my_jre; }); 97 other = (pkgs.other.override { jre = my_jre; }); 98in 99 <...> 100``` 101 102You can also specify what JDK your JRE should be based on, for example 103selecting a 'headless' build to avoid including a link to GTK+: 104 105```nix 106{ 107 my_jre = pkgs.jre_minimal.override { 108 jdk = jdk11_headless; 109 }; 110} 111``` 112 113Note all JDKs passthru `home`, so if your application requires 114environment variables like `JAVA_HOME` being set, that can be done in a 115generic fashion with the `--set` argument of `makeWrapper`: 116 117```bash 118--set JAVA_HOME ${jdk.home} 119``` 120 121It is possible to use a different Java compiler than `javac` from the 122OpenJDK. For instance, to use the GNU Java Compiler: 123 124```nix 125{ 126 nativeBuildInputs = [ gcj ant ]; 127} 128``` 129 130Here, Ant will automatically use `gij` (the GNU Java Runtime) instead of 131the OpenJRE.