Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,12 @@ let
in
with-inputs outputs
```
Consumer projects that use `with-inputs` will automatically inject their own `inputs` as `inputsOverrides`.

[^output-trick]: To use the experimental `nix` cli commands, create a `flake.nix` containing only
```nix
{ outputs = _: import ./.; }
```
[^output-trick-2]: To additionally allow inputs overrides (eg, by a `with-inputs`-based consummer project):
```nix
{ outputs = inputsOverrides: import ./. { inherit inputsOverrides; }
```

### Flake backed by non-flake pins
When `with-inputs` detect a flake dependency which does not declare any inputs, that flake `output` function is still called with the all available inputs, so they could be used as overrides.
Expand Down
1 change: 0 additions & 1 deletion fixtures/with-inputs-flake/flake.nix

This file was deleted.

19 changes: 17 additions & 2 deletions tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ in
(with-inputs
{
my-lib = mkSrc ./fixtures/fake-flake;
with-inputs-dep = mkSrc ./fixtures/with-inputs-flake;
with-inputs-dep = mkSrc ./fixtures/with-inputs-project;
}
{
my-lib.inputs.nixpkgs.follows = "with-inputs-dep/nixpkgs2";
Expand All @@ -369,6 +369,21 @@ in
expected = npins.nixpkgs.outPath;
};

introspection.test-follow-sub-npins-with-inputs-input-with-source-arg = {
# s: my-lib.inputs.nixpkgs.follows = "with-inputs-dep/nixpkgs" → traverse native inputs
expr =
(with-inputs
{
my-lib = mkSrc ./fixtures/fake-flake;
with-inputs-dep = mkSrc ./fixtures/with-inputs-project;
}
{
my-lib = s: { inputs.nixpkgs.follows = "with-inputs-dep/nixpkgs2"; };
}
).my-lib.inputs.nixpkgs.outPath;
expected = npins.nixpkgs.outPath;
};

real-flakes.test-npins-nix-maid-nixosModules-output-is-readable = {
expr =
(with-inputs npins { } (inputs: {
Expand Down Expand Up @@ -407,7 +422,7 @@ in
(with-inputs
{
my-lib = mkFlake { nixpkgs = mkSrc "/nested-nixpkgs"; } { };
with-inputs-dep = mkSrc ./fixtures/with-inputs-flake;
with-inputs-dep = mkSrc ./fixtures/with-inputs-project;
}
{
with-inputs-dep.inputs.nixpkgs2.follows = "my-lib/nixpkgs";
Expand Down
99 changes: 61 additions & 38 deletions with-inputs.nix
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,9 @@ let
hostName: subName:
let
entry = inputs.${hostName} or null;
getSubInput = e: if builtins.isAttrs e && e ? inputs then e.inputs.${subName} or null else null;
in
if entry != null && builtins.isAttrs entry && entry ? inputs then
entry.inputs.${subName} or null
else
null;
getSubInput (if builtins.isFunction entry then (entry sources.${hostName}) else entry);

# Resolve an inputs entry to an actual input value, or null if unresolvable.
# Values with outPath but no _type go through mkInput so their flake.nix is loaded.
Expand Down Expand Up @@ -108,15 +106,68 @@ let
let
hasPath = sourceInfo ? outPath;
isFlake = hasPath && (sourceInfo.flake or true);
defaultPath = sourceInfo.outPath + "/default.nix";
defaultNix = import defaultPath;
defaultArgs = builtins.functionArgs defaultNix;
hadDefaultValues = builtins.all (withDefault: withDefault) (builtins.attrValues defaultArgs);
defaultExists = isFlake && builtins.pathExists defaultPath;
defaultGood = builtins.tryEval (
defaultExists && builtins.isFunction defaultNix && defaultArgs ? inputsOverrides && hadDefaultValues
);
flakePath = sourceInfo.outPath + "/flake.nix";
flakeExists = isFlake && builtins.pathExists flakePath;
allGood = builtins.tryEval flakeExists;
flakeGood = builtins.tryEval flakeExists;

in
if allGood.success && allGood.value then
mkFlakeInput name sourceInfo (import flakePath)
if flakeGood.success && flakeGood.value then
let
flake = import flakePath;
specs = flake.inputs or { };
# Prefer default.nix if flake inputs specs is empty,
# unless in a flake evaluation context to avoid loading flake-compat (which would fail for using builtins.currentSystem)
in
if (specs == { } && builtins ? currentSystem && defaultGood.success && defaultGood.value) then
mkDefaultWithInputsInput name sourceInfo defaultNix
else
mkFlakeInput name sourceInfo flake
else if defaultGood.success && defaultGood.value then
mkDefaultWithInputsInput name sourceInfo defaultNix
else
sourceInfo // { inherit sourceInfo; };

mkDefaultWithInputsInput =
name: sourceInfo: defaultNix:
let
inputsOverrides =
let
recFollows =
let
follows =
input:
isFollows inputs.${input}
&& (
let
followRoot = builtins.head (builtins.split "/" inputs.${input}.follows);
in
followRoot == name || follows followRoot
);
in
[ name ] ++ builtins.filter follows (builtins.attrNames inputs);
in
removeAttrs allInputs recFollows
// (builtins.mapAttrs (sub: spec: resolveSubInput name sub spec) (inputs.${name}.inputs or { }));
outputs = defaultNix { inherit inputsOverrides; };
self =
sourceInfo
// outputs
// {
_type = "flake";
inputs = outputs.inputs or { inherit self; };
inherit outputs sourceInfo;
};
in
self;

mkFlakeInput =
name: sourceInfo: flake:
let
Expand All @@ -129,42 +180,14 @@ let
builtins.functionArgs flake.outputs
);
nonEmptyInputs = direct != { } || indirect != { };
inputs =
if nonEmptyInputs then
indirect // direct
else
# Assume inputs are not handled by flake, but output function
# may still accept inputs overrides: we give it allInputs minus
# those that would obviously trigger infinite recursion
# (inputs defined as follows of inputs of the flake we are importing)
# plus inputs overrides declared for this flake.
let
recFollows =
let
follows =
input:
isFollows topLevelInputs.${input}
&& (
let
followRoot = builtins.head (builtins.split "/" topLevelInputs.${input}.follows);
in
followRoot == name || follows followRoot
);
in
[ name ] ++ builtins.filter follows (builtins.attrNames topLevelInputs);
in
removeAttrs allInputs recFollows
// (builtins.mapAttrs (sub: spec: resolveSubInput name sub spec) (
topLevelInputs.${name}.inputs or { }
));
outputs = flake.outputs (inputs // { inherit self; });
inputs = indirect // direct // { inherit self; };
outputs = flake.outputs inputs;
self =
sourceInfo
// outputs
// {
_type = "flake";
inputs = if nonEmptyInputs then inputs else outputs.inputs or { inherit self; };
inherit outputs sourceInfo;
inherit outputs inputs sourceInfo;
};
in
self;
Expand Down
Loading