Hi,
I’ve been working on packaging a Java application (Enonic XP) recently, and have made some progress, but I’m stuck now when working on creating an actual derivation. I’ll list my immediate questions first and then provide more context.
- This application comes as an entire directory structure. How do I install that into the nix store?
- There are other applications that expect to find this directory structure located at a specific point on disk (the user’s home folder). Is there a way to fake this? Do I create an automatic symlink? Do I have to configure the tools to look for the application in the Nix store somehow?
- What is the ergonomic way of working with building Nix packages? How do I test whether the derivation works? It’s always seemed like a bit of magic to me.
I’ve also gotten some help with this process previously, both on this forum and on Enonic’s help forum, and I’m very grateful for that. This feels like a new step and a new question, however, so I feel like a new thread is appropriate.
Context
I recently started working with a Java-based CMS called Enonic XP. When you install a version of it, it installs a tree structure into your home folder that includes, among other things, a Java executable and some shell scripts that start everything. As expected, this doesn’t run well on NixOS, because the Java executable isn’t patched. More details on how this manifests can be found in the Enonic forum thread.
After finding other workarounds, I finally decided I’d try and patch it properly. I followed the tips given in this well-cited Stack Exchange answer and managed to manually patch the Java executable (more details available in this post from the Enonic forum thread).
Next would be packaging it so that it would be easy to re-use and share. Following the same Stack Exchange post and the NixOS wiki document on packaging/binaries, I’ve managed to create a derivation that seems to almost get it right, but I’m stuck at what feels like the end: the installation phase. I’m also not sure exactly how I’d use this.
Derivation
Here’s the derivation as it stands currently:
{ alsaLib
, autoPatchelfHook
, fetchurl
, freetype
, glibc
, lib
, libX11
, libXext
, libXi
, libXtst
, stdenv
, unzip
, xorg
, zlib
}:
let
version = "7.6.1";
url =
"https://repo.enonic.com/public/com/enonic/xp/enonic-xp-linux-sdk/${version}/enonic-xp-linux-sdk-7.6.1.zip";
in
stdenv.mkDerivation {
name = "enonic-xp-${version}";
inherit version;
src = fetchurl {
sha256 = "0c58zcyddxa0041bvyafyz8250ylcnqgh4ckqgyq078dzzkg5mbd";
inherit url;
};
nativeBuildInputs = [ autoPatchelfHook unzip ];
buildInputs = [
alsaLib
freetype
glibc
libX11
libXext
libXi
libXtst
xorg.libXrender
zlib
];
unpackPhase = ''
unzip $src
'';
installPhase = ''
mkdir -p $out
# install -m755 -D enonic-xp-linux-sdk-${version}/* -t $out
cp -r enonic-xp-linux-sdk-${version}/* $out/
'';
meta = with lib; {
description = "Enonic XP distribution";
homepage = "https://enonic.com";
license = licenses.gpl3;
maintainers = with stdenv.lib.maintainers; [];
platforms = platforms.linux;
};
}
It seems to be able to patch the elf correctly (though I needed a lot more dependencies than when I did the manual patch). After the patching, it seems as if it’s trying to unpack and build something again. The build log ends with
setting RPATH to: /nix/store/rldppqna2kya26zpdrl7p1wlbz0jgvj3-zlib-1.2.11/lib:/nix/store/7j0yfkjx2fc4hq0qswgm8nr2dwblz933-enonic-xp-7.6.1/jdk/lib/jli
building '/nix/store/s3j0lxph6rc43qgdd8yn8m87329ldjdn-enonic-xp.drv'...
unpacking sources
unpacking source archive /nix/store/7j0yfkjx2fc4hq0qswgm8nr2dwblz933-enonic-xp-7.6.1
source root is enonic-xp-7.6.1
patching sources
configuring
no configure script, doing nothing
building
no Makefile, doing nothing
installing
install flags: SHELL=/nix/store/9ywr69qi622lrmx5nn88gk8jpmihy0dz-bash-4.4-p23/bin/bash install
make: *** No rule to make target 'install'. Stop.
builder for '/nix/store/s3j0lxph6rc43qgdd8yn8m87329ldjdn-enonic-xp.drv' failed with exit code 2
For building I use nix-build
and a default.nix
right next to the above derivation which looks like this:
{ pkgs ? import <nixpkgs> {}, ... }:
with pkgs;
stdenv.mkDerivation {
name = "enonic-xp";
src = pkgs.callPackage (import ./enonic-xp.nix) {};
}
Upon closer inspection, I now realize that this is probably why the build fails: it tries to build the output of the callPackage
import? (Aha moment!)
Questions
Now that we’ve got some more context, let me try and elaborate on the questions a bit.
Installing a directory structure
When unzipping the sources in the derivation’s install phase, I end up with a directory tree. I’d like all the contents of this tree to be available in the Nix store. I tried using install
with a number of different arguments, but ended up falling back to cp -r
which seems to work (inspired by this Stack Overflow question).
Is this the ‘right’/recommended way of doing it? Is there a way to make it work with install
or is this just as good?
Making the application available to other apps
Some other applications expect to find this application (the whole directory tree) at a specified point on disk (in a directory in the user’s home directory). How should I go about doing this? Can I create a symlink from the expected location to the nix store? I think home-manager does something like that, but I don’t know if it’s recommended or how I’d do it. If it is the right way to do it, how do I do it? And if it isn’t, what options are there?
How do I work with building nix packages
For all the reading I’ve done (or at least tried to do) on building packages with Nix, I haven’t been able to find much that explains concisely how I build packages and what I need. What’s the build-test feedback loop?
For instance, the reason I created a default.nix
to import this package and build it, is that just using nix-build
on the package.nix file gives an error saying you ‘cannot auto-call a function that has an argument without a default value’. I understand that this is because the package file is just a function, but I also thought you shouldn’t manually import <nixpkgs>
into a package. So then: how do I build it and how do I test it?
Is the nix repl relevant here? If so, how do I use that? I still don’t understand what I should use the repl for (but I guess that’s for a different thread).
Apologies for the long question, but any input (towards any of the questions or just tips in general) would be very much appreciated! I’m also happy to supply any other information you may want; just let me know and I’ll try to get back to you as soon as possible. Also: if I’m going about this in the wrong way, that’s also useful info!
Cheers!