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
someBinary
directly, 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
nixpkgs
version 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.drv
file 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.drv
using 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?