How do I compose multiple project .nix files?

I have three projects with three directories, a, b, and c.

Projects a and b are standalone things I build into a package and a docker container with just that package.

Directory a has a default.nix and a.nix and from directory a I can nix-build -A a just fine.
Directory b has a default.nix and a b.nix and from directory b I can nix-build -A b just fine.

In directory c I want to create a new project that includes the packages from a and b as well as some of its own content, and build these into a docker container.

I can’t figure out the syntax for this, and I haven’t been able to find documentation explaining how to do this. I’d appreciate any help or even just pointers to what documentation I should read to figure out how to do this.

From c/default.nix, you could use an expression like import ../a {} (passing whatever arguments, if any), or even import ../a/a.nix {}, similar to what you might do to use a.nix in default.nix.

If those nix files have arguments, you might pass those in import ../a/a.nix { pkgs = pkgs; }.

You could then access the a attribute from a/default.nix e.g. let aPkgs = import ../a {}; in .... aPkgs.a .....

(EDIT: if you don’t want to hard-code the relative paths of a and b in the c code, then you could pass these as arguments to a function in c/default.nix, or use some technique like that).

(These values can then be used as part of building a Docker image).

For reference:

Using callPackage might more practical: Package parameters and overrides with callPackage — nix.dev documentation

1 Like

Hm. User error. I just wasn’t sure where.

Before posting my question I had tried callPackage in the let binding, but then using that variable in the paths, it complains that the resulting store object /nix/store/.....-docker-image-a.tar.gz is a file and can’t be merged into an environment using pkgs.buildEnv

Later I tried a few different methods using import, which (after I figured out how to inherit the arguments to pass along) resulted in similar errors.

This, and reading the replies above, has led me to believe that I’ve made a false assumption reguarding a an b. They produce a docker image, they don’t produce a package. The logic to produce the package is inside a let binding just before the call to build the package!

When I wrote a.nix and b.nix I didn’t know how to reference an external file – I didn’t even realize that the things in the files were function definitions until many re-readings of the documentation later, I assumed I had the syntax wrong – so I used a let binding to hold all of the logic for building the package (so a was bound to the package inside a.nix) and then I basically replaced the body of a.nix with the logic to build the docker image. The result was from default.nix in a, a = the docker image. From within a.nix, a = the package. So trying to use those in c.nix I was trying to put the docker tar result into my paths instead of the packages.

So, I need to change a and b to just produce a package.

I moved the container logic out into a-docker.nix and b-docker.nix. Then I put callPackage references to these in their respective default.nix. In that container building logic I used let bindings to assign the project name variable to the package. Now I can build my containers with nix-build -A a-docker, nix-build -A b-docker but doing nix-build -A a or nix-build -A b generates just the package. So now I can use that from c.nix with either callPackages or import and it works like it should.

1 Like