There are some packages, that I rarely use, but still need occasionally. Some of these packages are kind of large, so I don’t want to permanently include them in my NixOS closure.
nix run nixpkgs#someBinary is mostly fine, but it’s not perfect. My main UX gripes with this approach are
- I have muscle memory for some of these commands, so I often forget that I don’t have the package permanently installed and try to use
someBinarydirectly, before correcting it tonix run ... - The command is kind of long to type and doesn’t always play nicely with shell history/autocompletion
- The name of the package in nixpkgs doesn’t always match with the actual binary name, so I sometimes have to go to search.nixos.org.
- Even with a pinned
nixpkgsversion in my registry, thenix run nixpkgs#...approach still doesn’t include the overlays/customizations that I declare in my/etc/nixos
So, I had the following idea. In principle, it is possible to instantiate a derivation and get its corresponding derivation (/nix/store/aaaa...-someBinary.drv) without building/downloading the actual full derivation (/nix/store/bbbb...-someBinary).
I have verified that this works in the CLI:
-
DRV=$(nix eval --raw .#someBinary.drvPath), see that the.drvfile now exists -
OUT=$(nix eval --raw .#someBinary.outPath), check that the output path doesn’t exist at this point - use
nix-build --no-out-link ${DRV}to show that you can build the package straight from the.drv
(btw, can somebody tell me, how to build straight from the.drvusing the new nix3 CLI?) - check that
${OUT}now exists
So, now all we have to do is to automate an approximation of the above with nix. I wrote the following wrapper
mkLazyPackage = package:
pkgs.writeShellScriptBin
package.meta.mainProgram
''
# Instead of using the package directly, remember its `.drv` path
DRV="${package.drvPath}"
# And build it when the user tries to run the package
PACKAGE="$(${pkgs.nix}/bin/nix-build --no-out-link "$DRV")"
# Run the main program by default
exec "$PACKAGE/bin/${package.meta.mainProgram}" "$@"
'';
that could be used like this in your NixOS config
environment.systemPackages = with pkgs;
map mkLazyPackage [
# Lazy packages (will be built/installed on first run)
someBigApplication
anotherBigApplication
...
];
Unfortunately, this doesn’t work. The supposedly “lazy” packages are built/downloaded during the NixOS build.
Further testing seems to indicate that using package.drvPath in a derivation makes it depend on package itself (even though the actual package shouldn’t be required to build itself).
Does anybody know, how to get the package.drvPath string without getting a dependency on the package itself?