1

I'm currently trying to package some closed-source software into Nix derivations. The app is distributed as a large pile of .deb files, and most of them contain libraries that the other parts of the app can use.

To simplify, assume that app.deb contains the actual application, and lib.deb contains a library that the app requires.

Currently I have:

# default.nix let nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-23.11"; pkgs = import nixpkgs { config = { }; overlays = [ ]; }; in { lib = pkgs.callPackage ./lib.nix { }; app = pkgs.callPackage ./app.nix { }; } 
# lib.nix { lib, stdenv, autoPatchelfHook, dpkg, requireFile, libcxx, libgcc, }: stdenv.mkDerivation { pname = "myapp-lib"; version = "1.0.0"; src = requireFile { name = "lib.deb"; sha256 = "313e8686118ccba397de0bdfca101f1053b758227fd9d3510ea78644f2450bfe"; url = "https://softwarecorp.example/downloads"; }; nativeBuildInputs = [ dpkg autoPatchelfHook ]; unpackPhase = "dpkg-deb -x $src ."; buildInputs = [ libcxx libgcc ]; installPhase = '' cp -r lib $out/ ''; } 
# app.nix { lib, stdenv, autoPatchelfHook, dpkg, requireFile, libcxx, }: stdenv.mkDerivation { pname = "myapp-bin"; version = "1.0.0"; src = requireFile { name = "app.deb"; sha256 = "f4abbdb3f83d982569c5cd30ce5ad63ec4e49011d165e17a2c59d9a613f163b9"; url = "https://softwarecorp.example/downloads"; }; nativeBuildInputs = [ dpkg autoPatchelfHook ]; unpackPhase = "dpkg-deb -x $src ."; buildInputs = [ libcxx ]; installPhase = '' cp -r bin $out/ ''; runtimeDependencies = [ myapp-lib ]; # <-- how to do this? } 

The lib derivation builds on its own, and now I want to add the things it contains to the app derivation. I can't just add it to the regular list of package dependencies at the top of the file. I'd also like to avoid submitting the package to nixpkgs at this time, because I'm not sure I'll be able to package the app fully, and I don't want to become its maintainer before I know it can be done.

Alternatively, are there any good patterns for packaging closed-source software like this that would avoid needing to make lots of derivations? From what I can tell, the libraries here all have the same version, and are never used anywhere else in the company's products, so building a single derivation could be acceptable here.

2 Answers 2

1

First of all, you need to be able to access the lib derivation while you are writing the expression for app. So you should change the bottom of default.nix to have a "recursive" set where the elements can refer to each other, and pass lib as an argument to app:

{ lib = pkgs.callPackage ./lib.nix { }; app = pkgs.callPackage ./app.nix { inherit lib; }; } 

Now app.nix needs to have an argument named lib, but it looks like you already added that. (Actually, in your real code I hope the argument is named something else, because pkgs.lib is a library provided by nixpkgs, and you want to be able to use that in your derivation without a name conflict.)

Now in app.nix, just write inherit lib; in your set that you are passing to mkDerivation. This is equivalent to lib = lib;, and that means it will set an environment variable named lib equal to the output path of the lib derivation. (Despite what you wrote in your question, I don't think runtimeDependencies is a valid argument to mkDerivation.)

Now in your builder script for app you can do whatever is needed to make lib be a runtime dependency of app. For example, you might write ln -s $out/lib $lib to make a symbolic link. The exact details of what you are going to do here depends on the details of how the app finds its libraries, and I don't have those details so I can only tell your generalities.

Run nix-build -A app and look in the result symlink in your current directory to see what you built, and make sure it refers to lib in the way you are expecting.

2
  • Like I asked the other answer, how do you extend this to many derivations being used? Just copy the line into lib2 = ... and inherit lib2;? Commented Mar 20, 2024 at 20:44
  • Yeah, for each derivation that depends on libraries defined in the same set, just write inherit lib1 lib2 lib3;. There is probably some fancy Nix way to make callPackage work for your project and save you a little bit of typing, but writing out the dependencies explicitly will help you understand the structure of your system better. Commented Mar 20, 2024 at 21:10
0

To avoid making two derivation could simply not package the library and simply import it before the installPhase. This could be done by extracting the library files in postUnpack:

# app.nix { stdenv, autoPatchelfHook, dpkg, requireFile, libcxx, }: let myLib = requireFile { name = "lib.deb"; sha256 = "313e8686118ccba397de0bdfca101f1053b758227fd9d3510ea78644f2450bfe"; url = "https://softwarecorp.example/downloads"; }; in stdenv.mkDerivation { pname = "myapp-bin"; version = "1.0.0"; src = requireFile { name = "app.deb"; sha256 = "f4abbdb3f83d982569c5cd30ce5ad63ec4e49011d165e17a2c59d9a613f163b9"; url = "https://softwarecorp.example/downloads"; }; nativeBuildInputs = [ dpkg autoPatchelfHook ]; unpackPhase = "dpkg-deb -x $src ."; postUnpack = '' dpkg-deb -x ${myLib} . cp -r lib $out/ ''; buildInputs = [libcxx]; installPhase = '' cp -r bin $out/ ''; } 

The package epson-alc1100 is an example of this kind of implementation.

4
  • And if I need to extend this to many other libraries, then I'd add multiple instances of the myLib declaration and more dpkg-deb -x lines into the postUnpack section? Commented Mar 17, 2024 at 11:13
  • Yes exactly, just beware of potential conflicts when extracting multiple libraries with dpkg -x. Commented Mar 17, 2024 at 11:17
  • Well, the original software intends that you just install every .deb file in sequence, so I assume that's not going to be an issue. Commented Mar 17, 2024 at 11:18
  • It should not be since usually each library file have a different name. Commented Mar 17, 2024 at 11:23

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.