Skip to content

feat(lib): add mkPackagesFor for overlay-friendly package sets - #150

Merged
r-vdp merged 1 commit into
numtide:mainfrom
r-vdp:mk-packages-for
Apr 15, 2026
Merged

feat(lib): add mkPackagesFor for overlay-friendly package sets#150
r-vdp merged 1 commit into
numtide:mainfrom
r-vdp:mk-packages-for

Conversation

@r-vdp

@r-vdp r-vdp commented Apr 15, 2026

Copy link
Copy Markdown
Member

Problem

A blueprint flake that wants to expose its packages/ as a nixpkgs overlay
currently has no good option. The obvious

overlays.default = final: _: {
  foo = self.packages.${final.stdenv.hostPlatform.system};
};

doesn't build anything against final — it hands back the set blueprint
already built from inputs.nixpkgs (with whatever nixpkgs.config/overlays
the flake configured). The consumer ends up evaluating a second, independent
nixpkgs instance for those packages. inputs.nixpkgs.follows doesn't help; it
only changes which source gets re-imported, but still imports a second instance.

Concretely, in a NixOS config consuming llm-agents.nix (which uses exactly
this pattern), this accounts for ~2M extra function calls / ~9% of total eval
time per host.

Change

Add a mkPackagesFor output that reloads the packages/ tree against a
caller-supplied pkgs. Packages see the same scope arguments as in
packages.<system> (pkgs, flake, inputs, system, perSystem,
pname); only pkgs is swapped. perSystem.self resolves within the new
scope so intra-set references stay on the caller's nixpkgs.

outputs = inputs:
  let bp = inputs.blueprint { inherit inputs; };
  in bp // {
    overlays.default = final: _prev: {
      foo = bp.mkPackagesFor final;
    };
  };

unfilteredPackages is redefined in terms of mkPackagesFor, so there is a
single loader for the packages/ tree. Existing outputs are unchanged
(verified: identical drv hashes for packages and devShells via
llm-agents.nix as a fixture).

Related: #90, numtide/llm-agents.nix#1172

@r-vdp
r-vdp requested a review from zimbatm April 15, 2026 09:03
@r-vdp
r-vdp force-pushed the mk-packages-for branch 2 times, most recently from 0fba6b1 to e69e4fa Compare April 15, 2026 09:20
A blueprint flake's `packages.${system}` is built against blueprint's
own nixpkgs instance (derived from inputs.nixpkgs plus any
nixpkgs.config/overlays). An overlay that just exposes that set, e.g.

  final: _: { foo = self.packages.${final.system}; }

drags a second, independent nixpkgs evaluation into every consumer,
since `final` is never used to build anything. `inputs.nixpkgs.follows`
doesn't help — it only changes which source gets re-imported.

mkPackagesFor re-loads the packages/ tree against a caller-supplied
pkgs, with the same scope arguments (pkgs, flake, inputs, system,
perSystem, pname). perSystem.self resolves within the new scope so
intra-set references stay on the caller's nixpkgs.

  bp // {
    overlays.default = final: _: { foo = bp.mkPackagesFor final; };
  }

unfilteredPackages is now defined in terms of mkPackagesFor, so there
is a single loader for the packages/ tree. Existing outputs are
unchanged (verified: identical drv hashes for packages and devShells
via llm-agents.nix as a fixture).
@r-vdp
r-vdp force-pushed the mk-packages-for branch from e69e4fa to c43dd7a Compare April 15, 2026 09:27
@r-vdp
r-vdp merged commit 56131e8 into numtide:main Apr 15, 2026
17 checks passed
@r-vdp
r-vdp deleted the mk-packages-for branch April 15, 2026 10:35
r-vdp added a commit to r-vdp/llm-agents.nix that referenced this pull request Apr 15, 2026
overlays.default hands back packages.${system} as-is, which means
applying it pulls a second nixpkgs evaluation into the consumer
(import inputs.nixpkgs { config.allowUnfree = true; } via blueprint).
inputs.nixpkgs.follows doesn't help — it only changes which source
gets re-imported, not that it gets re-imported.

Add overlays.shared-nixpkgs using blueprint's new mkPackagesFor
(numtide/blueprint#150) so packages are rebuilt against the consumer's
final and share its dependency graph. Measured on a NixOS config that
uses pi/tuicr/rtk/openspec via the overlay: ~0.5M fewer function calls
out of ~20M (~2.5%). overlays.default is unchanged for users who want
cache hits independent of their nixpkgs pin.

Fixes numtide#1172
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant