Relative path from within a derivation

I’m working on a derivation who’s output is a directory of Nix files, which may reference each other. I’ve run into an apparent snag however. Here’s my repl demo:

> b = pkgs.writeTextDir "x/b.nix" ''"hi"''
> a = pkgs.writeTextDir "x/a.nix" ''import ./b.nix''
> ab = pkgs.symlinkJoin { name = "ab"; paths = [ b a ]; }
> import "${ab}/x/a.nix"

I would expect this to evaluate to ”hi”, but instead it gives this error message:

error:
       … while calling the 'import' builtin
         at «string»:1:1:
            1| import "${ab}/x/a.nix"
             | ^

       … while evaluating the file '/nix/store/r4dsp5vv14xs7ylganpinh1widrhvy6f-a.nix/x/a.nix':

       … while calling the 'import' builtin
         at /nix/store/r4dsp5vv14xs7ylganpinh1widrhvy6f-a.nix/x/a.nix:1:1:
            1| import ./b.nix
             | ^

       error: path '/nix/store/r4dsp5vv14xs7ylganpinh1widrhvy6f-a.nix/x/b.nix' does not exist

I notice that the path it’s trying to resolve for b.nix (…f-a.nix/x/b.nix) is different from the path for a.nix (…z-ab/x/a.nix). Why is it not searching relative to the importing file as I’m expecting?

It’s searching relative to the real location of a.nix. You’ve imported a.nix via a symlink, and the real path of a.nix is the single-file store path created by writeTextDir. If you want this sort of thing to work, you have to create all of your Nix files in one derivation (or switch to import /${b}/x/b.nix).

Check the output of AB, you’ll get a bash script which references the a and b by appending their real store directories to the path.

If you need them collected and actually together, then mkDerivation is a way better implementation for you that will have the components locally together.