Override / modify a derivation from a .drv file


#1

There’s a few ways I know of to programmatically modify a derivation. There’s lib.overrideDerivation which works on a fairly low-level (and doesn’t compose), and there’s also drv.overrideAttrs which is preferred.

Is it possible to override the result of importing a .drv file? I can import it and pass the result to nix-build, and lib.isDerivation returns true as well. But neither lib.overrideDerivation nor drv.overrideAttrs works on the result of importing a .drv file. It looks like a sample derivation’s attrNames is [ "all" "drvPath" "name" "out" "outPath" "outputName" "type" ], which doesn’t look like it gives me much room for messing with.

Why?

I want to make a tool / function which can override an arbitrary derivation (to replace its src attribute, specifically). I’ve tried this sort of thing before, and it tends to be kind of awkward to write this as a wrapper for nix-build - you need to understand how to interpret paths, -A flags, etc in order to construct a derivation which is like what nix-build will do, but also injecting your wrapper function into the mix.

So I’m wondering if it’s possible to override the resulting derivation - i.e. take the arguments to nix-build, pass them to nix-instantiate instead, then modify that since that will boil anything down to a .drv file.


#2

In the worst case it shouldn’t be too hard to read the *.drv file, transform it syntactically, and pass that to nix normally. At least, I think so.


#3

I wondered about that, but if I’m changing the src attribute, would I need to have to calculate a new outPath?

It’s easy enough to try out, but it looks like bad news:

$ nix-instantiate --expr --eval 'import /nix/store/bd51pl894jlww0bj8c711jb63k5j9dq5-gup-0.7.0.drv'
{ all = <CODE>; < ... >; type = "derivation"; }


$ cp /nix/store/bd51pl894jlww0bj8c711jb63k5j9dq5-gup-0.7.0.drv ./
$ nix-instantiate --expr --eval 'import ./bd51pl894jlww0bj8c711jb63k5j9dq5-gup-0.7.0.drv'
error: syntax error, unexpected ',', expecting ')', at /home/tim/bd51pl894jlww0bj8c711jb63k5j9dq5-gup-0.7.0.drv:1:15

If I copy a .drv and try to import that, it gets treated as a .nix expression and not a .drv any more.
So somewhere there’s something special about a derivation in the nix store.

Out of curiosity, I tried adding the plain .drv file to the store and that wasn’t happy either:

$ nix-store --add bd51pl894jlww0bj8c711jb63k5j9dq5-gup-0.7.0.drv 
error: derivation '/nix/store/2373f7gi19cy3vsv3c6i08j8cw9gdbq2-bd51pl894jlww0bj8c711jb63k5j9dq5-gup-0.7.0.drv' has incorrect output '/nix/store/4azq6pg86n3ra2j75nznmp9kzc9sx87x-gup-0.7.0', should be '/nix/store/yfy6qyx9hnxgkc7xrjn5wag5y9l6bqcn-bd51pl894jlww0bj8c711jb63k5j9dq5-gup-0.7.0'

(I changed the file to contain the path it suggested, but since I altered the contents of the file it then wanted a different hash)


#4

The de-compilation from .drv back to a builtins.derivation primitive call would be a little more complicated, but I think it could still be done just syntactically. You certainly wouldn’t need to specify output path hashes – those would be re-computed by nix when evaluating the resulting/modified .nix file. Anyway, perhaps someone will have a completely different kind of idea.


#5

Oh right, you mean building a nix expression from a .drv, not just modifying the .drv and importing that. Interesting, might have to read up on how a .drv actually works…


#6

OK, so given that nix show-derivation produces JSON, I was able to whip something up in plain nix (using an intermediate derivation):

There’s a bit of black magic in there to propagate inputDrvs and inputSrcs properly, the rest is just a couple of awkward differences in structure for the .drv JSON compared to what you give the derivation function.

But this does roundtrip (i.e. produce the exact same derivation) on a sample .drv file I tried, so it seems doable. I’d still be very keen to hear if anyone knows of easier options, this feels fairly brittle.