Help creating a derivation / package (Java app) - how do I get started?

Hey,

In short

I have a (non-nixpkgs) Java application that doesn’t run (seemingly due to not being patched) and I want to package it so that it can run on NixOS. Where do I go looking for how to package it? ELI5.

In long form:

I want to create a derivation or two, but I’m not sure how and I don’t even really know where to start. I’ve looked at a few of the other topics around here and looked around a bit online, but I feel like I need something even more basic, so please keep that in mind.

Here’s the situation: As a new hire, I recently wanted to try out Enonic XP. They provide a CLI on their Get Started page, which is available either as a binary download or via Snap. It is not available in nixpkgs.

The binary seems to work fine when downloaded. However, the XP application is a Java application that the CLI downloads for you. The CLI can also start, stop, and manage XP for you, but XP is ultimately run (from my understanding) via shell scripts that use a (bundled) Java to run its jar files. This bundled version of Java fails to start. There are more details in this issue on their Discuss page, but the long and short of it is that it seems to fail because the binaries haven’t been patched using patchelf:

Failed to execute process './java'. Reason:
The file './java' does not exist or could not be executed.

If I understand correctly, I can get rid of this problem if I package the application for Nix(OS) somehow.

However, I don’t know how to do that or where to start finding out how to do it. I’ve read through the nix-pills series previously and skimmed over the nixpkgs manual today. Is there a more accessible “here’s how you package an application from scratch” guide available anywhere? If not, do you have any tips on how to get started? How do I test whether something works, etc?

I know that I’m asking a lot, but I’m honestly quite lost. Appreciate any and all input! Thanks.

Edit: source repos

For clarity, it might be worth mentioning that the applications can be found in their source form on their github:

Also added more details to how the two applications interact.

1 Like

Since you essentially want to package a binary, the relevant documentation can be found here. More or less replacing the values gives us (for Darwin, because that’s what I have at hand):

{ pkgs ? import <nixpkgs> {}, ... }:
with pkgs;
stdenv.mkDerivation {
  name = "enonic-xp";
  src = fetchurl {
    url = "http://repo.enonic.com/public/com/enonic/cli/enonic/1.0.0/enonic_1.0.0_Mac_64-bit.tar.gz";
    sha256 = "0f4xin450pqr7dvfyn5q2qciqhjs12sl9s8jivaf3n0xhfw410hp";
  };
  nativeBuildInputs = [
    autoPatchelfHook
  ];
  unpackPhase = ''
    tar xf $src
  '';
  installPhase = ''
    install -m755 -D enonic $out/bin/enonic
  '';
}

Thanks! But that only takes care of the CLI, right? Will it have any effect on the Java app? The CLI downloads the so-called ‘distribution’, which is the Java app, and this is what fails to run. The CLI itself seems to work even without patching.

I’ve also updated the original post with the source repos for these projects if that makes things any easier. The CLI is available here and the distribution is available here.

Thanks for the link to the docs too! I’ve seen that page before, but even that is is too complex for me to be able to follow, I’m afraid. The AutoPatchelfHook example doesn’t really explain why it does what it does or where it gets the inputs from. I also don’t know how I’d test or use this once you have the file. Do you just import it into a derivation, like a shell.nix, for instance? Do you use callPackage?

Edit: I also realize that it wasn’t entirely clear from the opening post that the CLI mainly acts as a front for downloading and starting the distribution. I’ll go and clear that up. In short: the CLI downloads the application and places it in a subfolder. You can then use the CLI as a way to run some shell scripts included with the Java application. The CLI doesn’t run the application.

Hi !

I guess this tool will help to compile everything then:

Oh, that looks very interesting (and promising)! I’ll give that a go on Tuesday. Thanks!

Thanks again for the input! It was pretty trivial to adapt that to make it work on NixOS, both as a package and as an overlay. For posterity, I’ll put what I got here:

As a package for NixOS
{ stdenv, fetchurl, autoPatchelfHook }:

stdenv.mkDerivation {
  name = "enonic-cli";

  src = fetchurl {
    url =
      "https://repo.enonic.com/public/com/enonic/cli/enonic/1.5.1/enonic_1.5.1_Linux_64-bit.tar.gz";
    sha256 = "0nf5prz372w920sh25dxdplc668vkbphj0d9c50pdnjnhy0l346m";
  };

  nativeBuildInputs = [ autoPatchelfHook ];
  unpackPhase = ''
    tar xf $src
  '';

  installPhase = ''
    install -m755 -D enonic $out/bin/enonic
  '';
}

As an overlay
self: super: {
  enonic = super.stdenv.mkDerivation {
    name = "enonic-cli";

    src = super.fetchurl {
      url =
        "https://repo.enonic.com/public/com/enonic/cli/enonic/1.5.1/enonic_1.5.1_Linux_64-bit.tar.gz";
      sha256 = "0nf5prz372w920sh25dxdplc668vkbphj0d9c50pdnjnhy0l346m";
    };

    nativeBuildInputs = [ super.autoPatchelfHook ];
    unpackPhase = ''
      tar xf $src
    '';

    installPhase = ''
      install -m755 -D enonic $out/bin/enonic
    '';
  };
}

But is there a reason why you use fetchurl instead of fetchTarball for this, @fricklerhandwerk? I’d have thought that fetchTarball would be more appropriate. I tried it out and removed the unpack phase, thinking that’d be enough, but it didn’t work (no file called enonic) and I’m not quite sure how it’s supposed to work.

And @freezeboy: thanks! I tried it out, but the builds seemed to fail for various reasons, and I’m don’t have the requisite Gradle/Java experience to know what’s going on.

However, I also found out that the application (XP) works as expected if you make it use a java installed by Nix instead of the one that’s bundled with the XP distribution, so that might be a better place to go looking for a solution for now.

1 Like

Exactly what I thought and did, and it didn’t work. No idea why, usually it’s fetchTarball.

My impression from looking around also was that building Gradle projects is somewhat of a mess in general, that’s why I hesitated to add anything to the topic. Maybe someone else can contribute something useful.

@thomas Good luck and welcome to the community!

1 Like