2024-06-04 13:15:20 -07:00
2024-05-29 18:16:15 -07:00
2019-03-31 20:18:55 -07:00
2024-05-29 17:51:45 -07:00
2024-05-29 17:51:45 -07:00
2024-05-29 18:24:28 -07:00

./assets/gradle2nix.png

Generate Nix expressions which build Gradle-based projects.

Why?

Nix is an OS-agnostic package manager, a language-agnostic build system, and a bespoke programming language. One of its unique features 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 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 someone else running the same function over the same inputs.

Gradle is not a functional build system. Most Gradle-based projects will produce highly variable outputs depending on a host of impure inputs, including:

  • The JVM hosting the build
  • The Gradle installation running the build
  • Any usage of dynamic version constraints for dependencies
  • SNAPSHOT dependencies
  • 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.

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;
  • Reduce or eliminate flakiness and maintenance headaches from CI/CD pipelines
  • Distribute a recipe which can reliably build a Gradle project in repositories such as the Nix Package Collection.

Installation

A Nix expression (generated by gradle2nix itself) is provided for convenience. The following expression will fetch and build the latest version of this package:

import (fetchTarball "https://github.com/tadfisher/gradle2nix/archive/master.tar.gz")  {}

If this expression is in, say, gradle2nix.nix, gradle2nix can be built and placed in ./result with the following:

nix build -f gradle2nix.nix

You can also use the following one-liners to build or install gradle2nix in your user profile:

# Build and place in ./result/
nix build -f "https://github.com/tadfisher/gradle2nix/archive/master.tar.gz"

# Build and install in the user profile
nix-env -if "https://github.com/tadfisher/gradle2nix/archive/master.tar.gz"

gradle2nix is not yet packaged in nixpkgs itself, but work is in progress.

The buildGradlePackage function is provided via the gradle2nix.passthru.buildGradlePackage attribute.

{ pkgs ? import <nixpkgs> {} }:

let
  gradle2nix = import (fetchTarball "https://github.com/tadfisher/gradle2nix/archive/master.tar.gz")  {}
in
gradle2nix.buildGradlePackage {
  pname = "my-package";
  version = "1.0";
  lockFile = ./gradle.lock;
  gradleFlags = [ "installDist" ];
  # ...
}

Flake

A flake.nix is provided for those using Nix flakes. For example, the following will build and run gradle2nix with the arguments provided after --:

nix run github:tadfisher/gradle2nix -- --help

The buildGradlePackage function is provided via the builders output.

{
  inputs.gradle2nix.url = "github:tadfisher/gradle2nix";

  outputs = { self, gradle2nix }: {

    packages.x86_64-linux.default = gradle2nix.builders.x86_64-linux.buildGradlePackage {
      pname = "my-package";
      version = "1.0";
      lockFile = ./gradle.lock;
      gradleFlags = [ "installDist" ];
      # ...
    };
  };
}

Usage

Usage: gradle2nix [<options>] [<args>]...

Gradle installation:

  Where to find Gradle. By default, use the project's wrapper.

  --gradle-dist=<uri>       Gradle distribution URI
  --gradle-home=<dir>       Gradle home path (e.g. `nix eval --raw nixpkgs#gradle.outPath`/lib/gradle)
  --gradle-wrapper=<value>  Gradle wrapper version

Options:
  -t, --task=<task>              Gradle tasks to run
  -p, --project=<path>           Path to the project root (default: Current directory)
  -o, --out-dir=<dir>            Path to write generated files (default: <project>)
  -l, --lock-file=<filename>     Name of the generated lock file (default: gradle.lock)
  -j, --gradle-jdk=<dir>         JDK home to use for launching Gradle (e.g. `nix eval --raw nixpkgs#openjdk.home`)
  --log=(debug|info|warn|error)  Print messages with this priority or higher (default: info)
  --dump-events                  Dump Gradle event logs to the output directory
  --stacktrace                   Print a stack trace on error
  -h, --help                     Show this message and exit

Arguments:
  <args>  Extra arguments to pass to Gradle

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.

For packagers

If you're creating a Nix package for an existing Gradle project, you can reduce the number of pinned dependencies by passing one or more --task arguments. This will only pin the dependencies that were resolved as part of the build, instead of the default behavior where all possible dependencies are pinned.

For example, if the package produces its build output via the :app:installDist task, use the following:

gradle2nix -t :app:installDist

Note: This may be required if the build resolves configurations at execution time.

Specifying the Gradle installation

By default, if the project has configured the Gradle wrapper, it will be used; otherwise, the version of Gradle used to build gradle2nix will be used. You can override this to use any of the following:

# Gradle distribution URL:
gradle2nix --gradle-dist='https://services.gradle.org/distributions/gradle-8.7-bin.zip'

# Path to a local Gradle installation:
gradle2nix --gradle-home=`nix eval nixpkgs#gradle.outPath`/lib/gradle

# A specific wrapper version:
gradle2nix --gradle-wrapper=8.7

Contributing

Bug reports and feature requests are encouraged.

Create an issue

Code contributions are also encouraged. Please review the test cases in the fixtures directory and create a new one to reproduce any fixes or test new features. See the existing tests for examples of testing with these fixtures.

License

gradle2nix is licensed under the Apache License 2.0.

Description
Generate Nix expressions which build Gradle-based projects.
Readme 2.9 MiB
Languages
Kotlin 82.2%
Nix 17.5%
Java 0.3%