Why extending nixpkgs.legacyPackages behaves differently then extending flake packages?

Hi,
I have base flake A providing all packages (some libraries + packages built with those libraries).

In second flake B, I want to use the same packages as provided by A, but with one library (originally provided by A) built in different version and all packages from A rebuilt with this library version.

I was hoping that something like this can work:

{
  description = "Flake B";

  inputs.flake-A.url = "URL";
  inputs.utils.url = "github:numtide/flake-utils";


  outputs = { self, nixpkgs, flake-A, utils }@inputs:
    utils.lib.eachSystem [ "x86_64-linux" "x86_64-darwin" ] (system:
      let

        overlay = (final: prev: rec {
          my-custom-lib = prev.gdal.overrideAttrs (_: {
            version = "99.99.99";

            src = prev.fetchFromGitHub {
              owner = "xx";
              repo = "yy";
              rev = "xxxx";
              hash = "sha256-xxx";
            };
          });
        });

        my-pkgs = (nixpkgs.lib.makeExtensible (self: flake-A.packages.${system})).extend (overlay);

      in
      {

        devShells = {

          dev = nixpkgs.mkShellNoCC {
            packages = [ my-pkgs.my-custom-lib my-pkgs.package ];
          };

        };
      });
}

but what I get in shell is my-pkgs.my-custom-lib in correct version as specified by overlay, but my-pkgs.package which depends on my-custom-lib is not rebuilt.

Thank you for help.

1 Like

I could imagine that package.extend(...) only extends the resulting attribute set. I.e., my-pkgs.package.my-custom-lib might be the correct version that you specified in the overlay. However, that is only the output of the derivation, and not the input. For that I would assume you have to use overrideAttrs(...) (Overriding | nixpkgs) as usual.

So something like:

my-pkgs.package = flake-A.packages.${system}.package.overrideAttrs(...);

Alternatively, you can try overriding the nixpkgs input used by flake-A, so based on Flakes - NixOS Wiki and nix flake something like:

inputs.my-custom-nixpkgs.url = "...";

inputs.flake-A = {
  url = "URL";
  inputs.nixpkgs.follows = "my-custom-nixpkgs";
};

But this would require you to split your flake into two flakes, one that builds your custom lib (my-custom-nixpkgs) and another one that combines them.

Both have their advantages/disadvantages, and I somehow assume there is a better solution, but these would be my ideas.

@futile , thank you very much for answering.

I think I forgot to mention one important thing that if I extend nixpkgs input in the exactly the same way

my-pkgs = nixpkgs.legacyPackages.${system}.extend (overlay);  # doesn't need makeExtensible, it is nixpkgs are extensible by default

it works as I would expect. my-custom-lib is in custom version and all packages from flake-A are rebuilt against new version of my-custom-lib.

So the question is why adding the overlay to nixpkgs.legacyPackages.${system} behaves differently than doing the same to the flake-A.packages.${system} (after it is made extensible by using makeExtensible ).

1 Like

Combining flake-A provided overlay (containing all packages) with flake-B created overlay with my-custom-lib using composeManyExtensions doesn’t make any difference ?

combinedOverlays = lib.composeManyExtensions [ flake-A.overlays.${system} overlay ];

my-pkgs = (nixpkgs.lib.makeExtensible (self: flake-A.packages.${system})).extend combinedOverlays;

What lib.composeManyExtension actually does ?

That’s not “exactly the same way”. nixpkgs is already extensible by construction. You can’t impose that after the fact when the dependency relationships have already been fixed.

Essentially what your makeExtensible call did was just create a “package set” with no interdependency between the attributes.

Thank you very much for explanation @tejing .

Anybody with some idea what else I can try to achieve my goal without manually rebuilding all packages using override <package>.override { my-lib = my-custom-lib} ?

  • Write some nix code to do the overrides for you.
  • import the files directly from the flake instead of using its outputs, and manage it from there.

Thank you very much @tejing .