Nix and Clojure

The last post asking about the state of Clojure in Nix is about 1.5 years old. I checked the links and it seems that nothing has changed, is that correct? I don’t know much about maven and clojure build tooling but I’d like to code a bit in Clojure and I’d obviously like to use Nix for it. Is there someone who’d be kind enough to summarize the difficulty in using Nix for Clojure development? Is there any project or branch of a specific project I could contribute to and at least offer some time and effort, even in the absence of expertise with regards to Clojure?

6 Likes

Hi
I’m a Clojure user, and although I’m new to Nix as well, I’ve been able to package my own projects.
It was difficult though.

First of all, the nixpkgs support for clojure is rather limited. There is a clj2nix package to convert deps.edn to a nix file, but it doesn’t support some cases, such as cljfx. Which is because the maven downloader does not support classifiers…

To package your project as a Nix package, nix-build a project using Leiningen will not work, because it needs internet access to download dependencies. And Internet access is forbidden by nix-build for valid reasons.

Ideally clojure (deps.edn) projects are build in the same way as Go projects, there is a really easy to use Go module builder in Nix.

Now, here one simple example of how I have packaged a local project with simple dependencies:

{ pkgs ? import <nixpkgs> {} }:
let
  cljdeps = pkgs.callPackage ./deps.nix { };
  classp = cljdeps.makeClasspaths { };
  execName = "qif-edn";
  mainClass = "qif-edn.core";

  manifest = pkgs.writeText "${execName}-MANIFEST.MF" ''
    Manifest-Version: 1.0
    Main-Class: ${mainClass}

  '';
in pkgs.stdenv.mkDerivation {
  name = execName;

  nativeBuildInputs = [ pkgs.jdk pkgs.makeWrapper ];

  buildInputs = map (x: x.path) cljdeps.packages;

  src = ./src;

  phases = [ "unpackPhase" "buildPhase" "installPhase" ];

  buildPhase = ''
    mkdir classes
    java -cp .:${classp} clojure.main -e "(compile '${mainClass})"
    jar cmf ${manifest} out.jar -C classes qif_edn
  '';

  installPhase = ''
    mkdir -p $out/bin
    mkdir -p $out/share/java
    cp out.jar $out/share/java/${execName}.jar
    makeWrapper ${pkgs.jdk}/bin/java $out/bin/${execName} \
      --add-flags "-cp ${classp}:$out/share/java/${execName}.jar ${mainClass}"
  '';

}

The file deps.nix is created using clj2nix. Leiningen is not used. The src/ directory is used directly as source. Ideally this would be a git repository. Furthermore, it creates a wrapper around the jar file since this project is not only library but also an executable

3 Likes

Thanks for this!

A small thing: if clojure doesn’t have runtime dependencies on JDK-specific tools, it might be better to use jre for the wrapper (it’s smaller).

1 Like

For whatever it’s worth: after lots of spelunking in the depths of the WWW, I was able to formulate a good approach to packaging clojure applications with the help of clj2nix

example (a clojure wrapper lib for the dropbox java sdk lib) GitHub - samrose/clj-dropbox

I’ll lose the leiningen items there soon as I don’t need them anymore

1 Like

Please take a look at Packaging a Maven application with Nix | Farid Zakaria’s Blog
I wrote a little post describing how to package Maven + Java applications.

I know it’s not Maven but Leiningen should work the same way as it has an “offline mode”.

2 Likes

I hope these recent developments can make Java or Clojure packaging as good as currently Go packages as supported. To invoke Maven two times is a big step forward, would be great if this could be integrated in nixpkgs!

Here a potentially big advancement in the Nix + Clojure ecosystem:

Also, here, a native-image package of cljfmt

3 Likes

Since this is the top search result: GitHub - jlesquembre/clj-nix: Nix helpers for Clojure projects as mentioned above really seems like one of two ways to go for now. We’re using it in production for Clojure and ClojureScript with shadow-cljs, even replaced leiningen with the Clojure deps/build tools (and that switch was worth it well on its own, too!) to be able to use it.

Has anyone recently used GitHub - hlolli/clj2nix: Convert deps.edn to a nix expression and is able to compare the two?