@@ -91,11 +91,11 @@ static void expectType(EvalState & state, ValueType type,
91
91
92
92
static std::map<FlakeId, FlakeInput> parseFlakeInputs (
93
93
EvalState & state, Value * value, const PosIdx pos,
94
- const std::optional<Path> & baseDir, InputPath lockRootPath);
94
+ const std::optional<Path> & baseDir, InputPath lockRootPath, unsigned depth );
95
95
96
96
static FlakeInput parseFlakeInput (EvalState & state,
97
97
const std::string & inputName, Value * value, const PosIdx pos,
98
- const std::optional<Path> & baseDir, InputPath lockRootPath)
98
+ const std::optional<Path> & baseDir, InputPath lockRootPath, unsigned depth )
99
99
{
100
100
expectType (state, nAttrs, *value, pos);
101
101
@@ -119,7 +119,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
119
119
expectType (state, nBool, *attr.value , attr.pos );
120
120
input.isFlake = attr.value ->boolean ;
121
121
} else if (attr.name == sInputs ) {
122
- input.overrides = parseFlakeInputs (state, attr.value , attr.pos , baseDir, lockRootPath);
122
+ input.overrides = parseFlakeInputs (state, attr.value , attr.pos , baseDir, lockRootPath, depth + 1 );
123
123
} else if (attr.name == sFollows ) {
124
124
expectType (state, nString, *attr.value , attr.pos );
125
125
auto follows (parseInputPath (attr.value ->string .s ));
@@ -168,15 +168,19 @@ static FlakeInput parseFlakeInput(EvalState & state,
168
168
input.ref = parseFlakeRef (*url, baseDir, true , input.isFlake );
169
169
}
170
170
171
- if (!input.follows && !input.ref )
171
+ if (!input.follows && !input.ref && depth == 0 )
172
+ // in `input.nixops.inputs.nixpkgs.url = ...`, we assume `nixops` is from
173
+ // the flake registry absent `ref`/`follows`, but we should not assume so
174
+ // about `nixpkgs` (where `depth == 1`) as the `nixops` flake should
175
+ // determine its default source
172
176
input.ref = FlakeRef::fromAttrs ({{" type" , " indirect" }, {" id" , inputName}});
173
177
174
178
return input;
175
179
}
176
180
177
181
static std::map<FlakeId, FlakeInput> parseFlakeInputs (
178
182
EvalState & state, Value * value, const PosIdx pos,
179
- const std::optional<Path> & baseDir, InputPath lockRootPath)
183
+ const std::optional<Path> & baseDir, InputPath lockRootPath, unsigned depth )
180
184
{
181
185
std::map<FlakeId, FlakeInput> inputs;
182
186
@@ -189,7 +193,8 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
189
193
inputAttr.value ,
190
194
inputAttr.pos ,
191
195
baseDir,
192
- lockRootPath));
196
+ lockRootPath,
197
+ depth));
193
198
}
194
199
195
200
return inputs;
@@ -239,7 +244,7 @@ static Flake getFlake(
239
244
auto sInputs = state.symbols .create (" inputs" );
240
245
241
246
if (auto inputs = vInfo.attrs ->get (sInputs ))
242
- flake.inputs = parseFlakeInputs (state, inputs->value , inputs->pos , flakeDir, lockRootPath);
247
+ flake.inputs = parseFlakeInputs (state, inputs->value , inputs->pos , flakeDir, lockRootPath, 0 );
243
248
244
249
auto sOutputs = state.symbols .create (" outputs" );
245
250
@@ -322,6 +327,19 @@ Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup
322
327
return getFlake (state, originalRef, allowLookup, flakeCache);
323
328
}
324
329
330
+ /* Recursively merge `overrides` into `overrideMap` */
331
+ static void updateOverrides (std::map<InputPath, FlakeInput> & overrideMap, const FlakeInputs & overrides,
332
+ const InputPath & inputPathPrefix)
333
+ {
334
+ for (auto & [id, input] : overrides) {
335
+ auto inputPath (inputPathPrefix);
336
+ inputPath.push_back (id);
337
+ // Do not override existing assignment from outer flake
338
+ overrideMap.insert ({inputPath, input});
339
+ updateOverrides (overrideMap, input.overrides , inputPath);
340
+ }
341
+ }
342
+
325
343
/* Compute an in-memory lock file for the specified top-level flake,
326
344
and optionally write it to file, if the flake is writable. */
327
345
LockedFlake lockFlake (
@@ -394,12 +412,9 @@ LockedFlake lockFlake(
394
412
/* Get the overrides (i.e. attributes of the form
395
413
'inputs.nixops.inputs.nixpkgs.url = ...'). */
396
414
for (auto & [id, input] : flakeInputs) {
397
- for (auto & [idOverride, inputOverride] : input.overrides ) {
398
- auto inputPath (inputPathPrefix);
399
- inputPath.push_back (id);
400
- inputPath.push_back (idOverride);
401
- overrides.insert_or_assign (inputPath, inputOverride);
402
- }
415
+ auto inputPath (inputPathPrefix);
416
+ inputPath.push_back (id);
417
+ updateOverrides (overrides, input.overrides , inputPath);
403
418
}
404
419
405
420
/* Check whether this input has overrides for a
@@ -434,6 +449,12 @@ LockedFlake lockFlake(
434
449
// Respect the “flakeness” of the input even if we
435
450
// override it
436
451
i->second .isFlake = input2.isFlake ;
452
+ if (!i->second .ref )
453
+ i->second .ref = input2.ref ;
454
+ if (!i->second .follows )
455
+ i->second .follows = input2.follows ;
456
+ // Note that `input.overrides` is not used in the following,
457
+ // so no need to merge it here (already done by `updateOverrides`)
437
458
}
438
459
auto & input = hasOverride ? i->second : input2;
439
460
0 commit comments