Download and wrap a JAR: extensively documented example


I wrote myself some lines of Nix which download a JAR file, use makeWrapper to create a wrapper shell script, and define a Nix shell environment with this shell script in PATH:

scolobb/nix-GINsim: This repository packages for Nix. - nix-GINsim - Gitea: the awesomest Marvid git repos

These are trivial actions, but it took me a couple of hours to figure out how to do them. I run NixOS on my main machine since 2016 and I contributed a couple simple packages to Nixpkgs. So I thought I’d share this code as a very simple example. Besides, since I tend to forget how exactly I do the stuff I do, there are a lot of comments all over the place. I think I would have liked to have such an example when starting with Nix, or even this morning (but maybe I didn’t look in the right places :man_shrugging:).

Feedback is welcome of course: I’m still very much a Nix beginner.



First off – thank you for your contribution!
I went over derivation and that is the level of comments all derivations should have!

While it may seem unnecessary to keep explanatory/introductionary comments on every derivation; most people I find (including myself) are learning Nix from scanning derivations they peruse on GitHub. Having them well articulated is just a nice accompaniment to why the author chose to craft the derivation in the way that they have.

Specifically for Java it looks like that approach works for a single JAR very straightforward but I would be very much interested to see clear derivations that demonstrate the following:

  1. Building a JAR with dependencies where a project is setup with Maven / Gradle

I’m pretty perplexed on this one – a Nix build should not have any network access.
In this case I suspect, the derivation should declare all necessary JARs and then place them in a temporary local Maven repository such that they are already cached?

  1. Use jlink to craft a minimal JRE; this just makes the dependency closure much smaller since it does not need the full JRE distribution.

  2. An example that performs Graal AOT

1 Like

Hi @fzakaria and thank you for your feedback!

In what concerns your questions, I don’t actually know how to do the things you asking :slightly_smiling_face: Maybe someone around here will be able to give you a better help.

This is the most recent discussion I’ve been involved in about writing nix expressions for maven builds. Spoiler alert: there is no silver bullet right now. :-/

1 Like

Thanks for the link @Jerith – that sounds way more confusing than it needs to be.

I have some deep knowledge of Maven + Java but just getting started on Nix; and those solutions look a bit janky. (I hope to contribute to them eventually)

If I were to package up a maven application now; I’d probably just let that derivation escape the sandbox if it was for personal use and just leverage maven already.

Curious if either of those solutions: mavenix or mvn2nix fail if any of the dependencies are SNAPSHOT as they are sure to eventually fail (unless the timestamped version is used).

Thanks for the link!

Another option I’ve come across since posting in that issue is the way the javafx build in nixpkgs works. It basically runs the build twice, once as a fixed-output derivation with network access to populate the local maven (gradle) cache/repo. Then it takes that as input to the real build, without network access. Inefficient, but maybe elegant in its own way? :slight_smile: Annoying to maintain, though, because as you say snapshot dependencies can change.

I really think what’s needed is a way for maven to create a lock file, like several other package managers from other ecosystems do nowadays. That would be useful even outside of nix. Though, FWIW, I did chat briefly with Robert Scholte about that at CodeOne last fall, and he didn’t think it was something the Maven core team would have time to work on in the near future.

1 Like

Interesting – I didn’t know about fixed-output derivations.

If the fixed-output passes why even run it twice ?
It seems as if that’s the intent with fixed-output to loosen the network restriction.
That’s seems like a nice win as long as JDK + Maven + the build itself is hermetic.

I mean a lock file isn’t really as necessary for maven since version range aren’t really a thing like many other dependency managers. The only oddity is SNAPSHOT but that can also be resolved to the timestamp version (maven must be told to ignore that it’s out of date from time of biuld)

The complete result may change for other reasons, and it would be annoying to have to update the fix-output hash in those cases. Some examples include:

  1. changes to input derivations, e.g. updating to a new JDK that will produce new bytecode
  2. changes the source code of the package itself
  3. changes to derivations referenced in the output, e.g. in shell script #!s

You might also find this interesting reading:

That RFC is a bit thin on the motivation at the start.
I confess I’m only <1-2 months into Nix so I’m still having to remember some concepts.

I’m open to creating a new channel though in discord for #java to also talk in real-time about some of the pitfalls and maybe hack on a solution.

Improving the maven plugin or even writing a separate small utility seems within bounds.
I think there’s a lot to go off from by learning what Ruby’s bundlerEnv is doing on my part.

@Jerith I started GitHub - fzakaria/mvn2nix: Easily package your Maven Java application with the Nix package manager. which is a faster alternative I think to GitHub - NixOS/mvn2nix-maven-plugin: Generate project-info.json for use with nix's Maven repository generation functions

1 - Not relying on Maven’s plugin infrastructure is a big win. It’s a bit confusing to understand the dependency injection.

2 - It is packaged up as an individual application so you can just run mvn2nix. Future wins could be generating the AOT code so startup is very fast.

3 - Generates a Nix expression vs. JSON

Just following up here; I finally contributed the other missing piece of the puzzle.
Turn the nix expression generated by mvn2nix back into a maven repository

let pkgs = import <nixpkgs> {};
    buildMavenRepsitory = import (
# this automatically searches for dependencies.nix
buildMavenRepsitory {}
$ tree /nix/store/2ps43297g5nii2k15kfy8z46fam51d8x-buildMavenRepsitory | head

├── com
│   └── google
│       ├── code
│       │   └── findbugs
│       │       └── jsr305
│       │           └── 3.0.2
│       │               └── jsr305-3.0.2.jar -> /nix/store/w20lb1dk730v77qis8l6sjqpljwkyql7-jsr305-3.0.2.jar
│       ├── errorprone
│       │   └── error_prone_annotations