Move Gradle build logic to a setup hook

This commit is contained in:
Tad Fisher
2024-06-13 15:21:58 -07:00
parent a4ef499401
commit b32bf21a6c
26 changed files with 768 additions and 470 deletions

View File

@@ -2,7 +2,8 @@
[[./assets/gradle2nix.png]]
Generate [[https://nixos.org/nix/][Nix]] expressions which build [[https://gradle.org/][Gradle]]-based projects.
Generate [[https://nixos.org/nix/][Nix]] expressions which build
[[https://gradle.org/][Gradle]]-based projects.
** Table of contents
@@ -13,6 +14,10 @@ Generate [[https://nixos.org/nix/][Nix]] expressions which build [[https://gradl
- [[#usage][Usage]]
- [[#for-packagers][For packagers]]
- [[#specifying-the-gradle-installation][Specifying the Gradle installation]]
- [[#reference][Reference]]
- [[#buildGradlePackage][=buildGradlePackage=]]
- [[#buildMavenRepo][=buildMavenRepo=]]
- [[#gradleSetupHook][=gradleSetupHook=]]
- [[#contributing][Contributing]]
- [[#license][License]]
#+END_QUOTE
@@ -25,7 +30,8 @@ is that it is purely functional; a "package" is a function which
accepts inputs (source code, configuration, etc) and produces an
output (binaries, a Java JAR, documentation, really anything).
One benefit of a functional build system is [[https://reproducible-builds.org/][reproducibility]]. If you
One benefit of a functional build system is
[[https://reproducible-builds.org/][reproducibility]]. If you
specify your inputs precisely, and take care not to introduce
impurities—such as files retrieved over a network without tracking
their content—you will receive, byte-for-byte, the exact output as
@@ -42,31 +48,33 @@ inputs, including:
- Environment variables and command-line options
- Artifacts cached on the system hosting the build
=gradle2nix= helps to solve this problem by leveraging Nix to control
the most common inputs to a Gradle build. When run on a project, it
will record all dependencies for both the build environment (including
=plugins= and =buildscript= blocks) and the project, and provide a Nix
expression to run the build given these dependencies. The build itself
is then run in a sandbox, where only content-tracked network requests
are allowed to fetch dependencies, and a local Maven repository is
created on-the-fly to host the dependency artifacts somewhere Gradle
can resolve them without a network.
=gradle2nix= helps to solve this problem by leveraging Nix to
control the most common inputs to a Gradle build. When run on a
project, it will record all dependencies for both the build
environment (including =plugins= and =buildscript= blocks) and the
project, and provide a Nix expression to run the build given these
dependencies. The build itself is then run in a sandbox, where only
content-tracked network requests are allowed to fetch dependencies,
and a local Maven repository is created on-the-fly to host the
dependency artifacts somewhere Gradle can resolve them without a
network.
This tool is useful for both development and packaging. You can use
=gradle2nix= to:
- Create isolated and reproducible development environments that work
anywhere Nix itself can run;
- Create isolated and reproducible development environments that
work anywhere Nix itself can run.
- Reduce or eliminate flakiness and maintenance headaches from CI/CD
pipelines
pipelines.
- Distribute a recipe which can reliably build a Gradle project in
repositories such as the [[https://nixos.org/nixpkgs/][Nix Package Collection]].
repositories such as the [[https://nixos.org/nixpkgs/][Nix Package
Collection]].
** Installation
A [[./gradle.nix][Nix expression]] (generated by =gradle2nix= itself) is provided for
convenience. The following expression will fetch and build the latest
version of this package:
A [[./gradle.nix][Nix expression]] (generated by =gradle2nix=
itself) is provided for convenience. The following expression will
fetch and build the latest version of this package:
#+begin_src nix
import (fetchTarball "https://github.com/tadfisher/gradle2nix/archive/master.tar.gz") {}
@@ -93,8 +101,9 @@ nix-env -if "https://github.com/tadfisher/gradle2nix/archive/master.tar.gz"
=gradle2nix= is not yet packaged in =nixpkgs= itself, but work is
[[https://github.com/NixOS/nixpkgs/pull/77422][in progress]].
The [[./gradle.nix][buildGradlePackage]] function is provided via the
=gradle2nix.passthru.buildGradlePackage= attribute.
The [[./nix/build-gradle-package.nix][buildGradlePackage]] function
is provided via the =gradle2nix.passthru.buildGradlePackage=
attribute.
#+begin_src nix
{ pkgs ? import <nixpkgs> {} }:
@@ -106,23 +115,24 @@ gradle2nix.buildGradlePackage {
pname = "my-package";
version = "1.0";
lockFile = ./gradle.lock;
gradleFlags = [ "installDist" ];
gradleInstallFlags = [ "installDist" ];
# ...
}
#+end_src
*** Flake
A [[./flake.nix][flake.nix]] is provided for those using [[https://nixos.wiki/wiki/Flakes][Nix flakes]]. For example, the
following will build and run =gradle2nix= with the arguments provided
after =--=:
A [[./flake.nix][flake.nix]] is provided for those using
[[https://nixos.wiki/wiki/Flakes][Nix flakes]]. For example, the
following will build and run =gradle2nix= with the arguments
provided after =--=:
#+begin_example
nix run github:tadfisher/gradle2nix -- --help
#+end_example
The [[./gradle.nix][buildGradlePackage]] function is provided via the
=builders= output.
The [[./nix/build-gradle-package.nix][buildGradlePackage]] function
is provided via the =builders= output.
#+begin_src nix
{
@@ -134,7 +144,7 @@ The [[./gradle.nix][buildGradlePackage]] function is provided via the
pname = "my-package";
version = "1.0";
lockFile = ./gradle.lock;
gradleFlags = [ "installDist" ];
gradleInstallFlags = [ "installDist" ];
# ...
};
};
@@ -169,12 +179,12 @@ Arguments:
<args> Extra arguments to pass to Gradle
#+end_example
Simply running =gradle2nix= in the root directory of a project should
be enough for most projects. This will produce two files, by default
called =gradle.lock= and =gradle.nix=, which contain the
pinned dependencies for the project and a standard build expression
which can be imported or called by other Nix expressions. An example
of such an expression can be found in this project's [[./gradle2nix.nix][gradle2nix.nix]].
Simply running =gradle2nix= in the root directory of a project
should be enough for most projects. This will produce a lock file,
by default called =gradle.lock=, which contains the pinned
dependencies for the project. An example of a build expression using
this lock file can be found in this project's
[[./default.nix][default.nix]].
*** For packagers
@@ -211,16 +221,203 @@ gradle2nix --gradle-home=`nix eval nixpkgs#gradle.outPath`/lib/gradle
gradle2nix --gradle-wrapper=8.7
#+end_example
** Reference
*** =buildGradlePackage=
This function is a convenience wrapper around =stdenv.mkDerivation=
that simplifies building Gradle projects with the lock files
produced by =gradle2nix=. It performs the following:
1. Applies [[#gradleSetupHook][=gradleSetupHook=]], overriding the
required =gradle= package if specified.
2. Builds the offline Maven repository with
[[#buildMavenRepo][=buildMavenRepo=]].
3. Sets the JDK used to run Gradle if specified.
4. Applies the offline repo to the Gradle build using an
initialization script.
- Source:
[[./nix/build-gradle-package.nix][build-gradle-package.nix]]
- Location:
- Nix :: =gradle2nix.passthru.buildGradlePackage=
- Flake :: =builders.${system}.buildGradlePackage=
**** Arguments to =buildGradlePackage=
- =lockFile= :: Path to the lock file generated by =gradle2nix=
(e.g. =gradle.lock=).
- =gradlePackage= :: The Gradle package to use. Default is
=pkgs.gradle=.
- =buildJdk= :: Override the default JDK used to run Gradle itself.
- =fetchers= :: Override functions which fetch dependency
artifacts.
See [[#fetchers][detailed documentation]] below.
- =overrides= :: Override artifacts in the offline Maven repository.
See [[#override][detailed documentation]] below.
In addition, this function accepts:
- All arguments to =stdenv.mkDerivation=.
- Arguments specific to =gradleSetupHook= (see
[[#gradleSetupHook][below]]).
*** =buildMavenRepo=
This function takes a lock file and produces a derivation which
downloads all dependencies into a Maven local repository. The
derivation provides a passthru =gradleInitScript= attribute, which
is a Gradle initialization script that can be applied using =gradle
--init-script== or placed in =$GRADLE_USER_HOME/init.d=. The init
script replaces all repositories referenced in the project with the
local repository.
- Source: [[./nix/build-maven-repo.nix][build-maven-repo.nix]]
- Location:
- Nix :: =gradle2nix.passthru.buildMavenRepo=
- Flake :: =builders.${system}.buildMavenRepo=
**** Arguments to =buildMavenRepo=
- =lockFile= :: Path to the lock file generated by gradle2nix (e.g.
=gradle.lock=).
- =fetchers= :: Override functions which fetch dependency
artifacts.
See [[#fetchers][detailed documentation]] below.
- =overrides= :: Override artifacts in the offline Maven repository.
See [[#override][detailed documentation]] below.
*** =gradleSetupHook=
A
[[https://nixos.org/manual/nixpkgs/unstable/#ssec-setup-hooks][setup
hook]] to simplify building Gradle packages. Overrides the default
configure, build, check, and install phases.
To use, add =gradleSetupHook= to a derivation's =nativeBuildInputs=.
- Source: [[./nix/setup-hook.sh][setup-hook.sh]]
- Location:
- Nix :: =gradle2nix.passthru.gradleSetupHook=
- Flake :: =packages.${system}.gradleSetupHook=
**** Variables controlling =gradleSetupHook=
- =gradleInitScript= :: Path to an
[[https://docs.gradle.org/current/userguide/init_scripts.html][initialization
script]] used by =gradle= during all phases.
- =gradleFlags= :: Controls the arguments passed to =gradle= during
all phases.
- =gradleBuildFlags= :: Controls the arguments passed to =gradle=
during the build phase. The build phase is skipped if this is
unset.
- =gradleCheckFlags= :: Controls the arguments passed to =gradle=
during the check phase. The check phase is skipped if this is
unset.
- =gradleInstallFlags= :: Controls the arguments passed to =gradle=
during the install phase. This install phase is skipped if this is
unset.
- =dontUseGradleConfigure= :: When set to true, don't use the
predefined =gradleConfigurePhase=. This will also disable the use
of =gradleInitScript=.
- =dontUseGradleCheck= :: When set to true, don't use the predefined
=gradleCheckPhase=.
- =dontUseGradleInstall= :: When set to true, don't use the
predefined =gradleInstallPhase=.
**** Honored variables
The following variables commonly used by =stdenv.mkDerivation= are
honored by =gradleSetupHook=.
- =enableParallelBuilding=
- =enableParallelChecking=
- =enableParallelInstalling=
*** Common arguments
**** =fetchers=
Names in this set are URL schemes such as "https" or "s3". Values
are functions which take an artifact in the form ={ url, hash }=
and fetches it into the Nix store. For example:
#+begin_src nix
{
s3 = { name, url, hash }: fetchs3 {
s3url = url;
# TODO This doesn't work without patching fetchs3 to accept SRI hashes
inherit name hash;
region = "us-west-2";
credentials = {
access_key_id = "foo";
secret_access_key = "bar";
};
};
}
#+end_src
**** =overrides=
This is an attrset of the form:
#+begin_src nix
{
"${group}:${module}:${version}" = {
"${filename}" = <override function>;
}
}
#+end_src
The override function takes the original derivation from 'fetchers'
(e.g. the result of 'fetchurl') and produces a new derivation to
replace it.
- Replace a dependency's JAR artifact:
#+begin_src nix
{
"com.squareup.okio:okio:3.9.0"."okio-3.9.0.jar" = _: fetchurl {
url = "https://repo.maven.apache.org/maven2/com/squareup/okio/okio/3.9.0/okio-3.9.0.jar";
hash = "...";
downloadToTemp = true;
postFetch = "install -Dt $out/com/squareup/okio/okio/3.9.0/ $downloadedFile"
};
}
#+end_src
- Patch a JAR containing native binaries:
#+begin_src nix
{
"com.android.tools.build:aapt2:8.5.0-rc02-11315950" = {
"aapt2-8.5.0-rc02-11315950-linux.jar" = src: runCommandCC src.name {
nativeBuildInputs = [ jdk autoPatchelfHook ];
dontAutoPatchelf = true;
} ''
cp ${src} aapt2.jar
jar xf aapt2.jar aapt2
chmod +x aapt2
autoPatchelf aapt2
jar uf aapt2.jar aapt2
cp aapt2.jar $out
'';
}
}
#+end_src
** Contributing
Bug reports and feature requests are encouraged.
[[https://github.com/tadfisher/gradle2nix/issues/new][Create an issue]]
[[https://github.com/tadfisher/gradle2nix/issues/new][Create an
issue]]
Code contributions are also encouraged. Please review the test cases
in the [[./fixtures][fixtures]] directory and create a new one to reproduce any fixes
or test new features. See the [[./app/src/test/kotlin/org/nixos/gradle2nix/GoldenTest.kt][existing tests]]
for examples of testing with these fixtures.
in the [[./fixtures][fixtures]] directory and create a new one to
reproduce any fixes or test new features. See the
[[./app/src/test/kotlin/org/nixos/gradle2nix/GoldenTest.kt][existing
tests]] for examples of testing with these fixtures.
** License