Flake packages can't find each other

I’m sure this is something very basic, but I’m trying to develop a flake which includes a pool of packages with interdependencies— I’ve manually created the first two, called catkin and cpp_common:


From the root of that repo, I can build the catkin package no problem with nix build .#ros.catkin, however when I try to build cpp_common, I get:

$ nix build .#ros.cpp_common -L
error: anonymous function at /nix/store/0rijgxwi9hmlv32fgbkc9s0v94ylk8n4-source/ros/cpp_common.nix:1:1 called without required argument 'catkin'

       at /nix/store/8i1c2px0walq5f213lf9wjbnml5acl28-source/lib/customisation.nix:69:16:

           68|     let
           69|       result = f origArgs;
             |                ^
(use '--show-trace' to show detailed location information)

How is best to set up a dict of packages such that they can find each other?

I a project of mine, I use a separate callPackage that provides merged set of nixpkgs and my output packages:

  callPackage' = pkgs.lib.callPackageWith (
    // self.packages.${system}
    // { inherit buildMix' callPackage'; }
in …

The way I do this is to pass all my packages as overlays to nixpkgs:

  outputs = { nixpkgs, ... }: let pkgs = import nixpkgs {
    system = someSystem; 
    overlays = [ ./pkgs.nix ]; 
  }; in {
    packages.someSystem = { inherit (pkgs) packageA packageB; };


final: prev: {
  packageA = prev.callPackage ./path/to/packageA { };
  packageB = prev.callPackage ./path/to/packageB { };
1 Like

This looks familiar — I think I’ve seen this elsewhere as the self: super: pattern, but I’ve had trouble finding a clear, Pill-style explanation of how it works. Is overlays basically just a magical key you pass to the nixpkgs import and that makes it do the thing?

But I guess the deeper question is how this is different from what I have currently. What is it about this structure that is delaying evaluation of packageA until the whole dict is available, in case any of the other entries in it will be required as a dependency?

1 Like

The arguments of an overlay are technically arbitrary. They used to be self: super: by convention, and now they are final: prev: to convey more clearly that final refers to the final output after all overlays have been applied, and prev refers to the previous iteration of the overlay, just before the one currently being computed.

Overlays allow you to insert your own layers into the fixed-point that generates all of nixpkgs so you can override a packages definition, or insert your own. I tend to declare all of my flake packages as overlays, to maintain maximum flexibility. I can just build and run the package, or I can pull the overlay into another flake as a dependency.

To try and answer the deeper question here, it has to do with the way lazy evaluation works. Tracking the exact order of evaluation in a lazy language can be extremely tricky, but in concept it’s simple enough to understand that if packageB has packageA as an input, then even if the interpreter begins building packageB first, once it reaches the inputs section and sees that packageA is a hard requirement, it will begin to evaluate packageA to completion so it can then finish packageB.

1 Like

Okay, thanks for that— and I will give the minimalist overlay example a try; that’s really helpful. I think my angst is just that I don’t understand why the lazy evaluation doesn’t also apply to my more basic example. When I try to build cpp_common, it fails with called without required argument, which I’m assuming is because the pkgs upon which callPackage is invoked doesn’t know about catkin at that point since so far it’s only in a let invocation.

What is it about the final: prev: pattern which permits the evaluation to be delayed? What’s different about it than what I’m doing?