@@ -99,7 +99,7 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
9999 const std::optional<Path> & baseDir, InputPath lockRootPath);
100100
101101static FlakeInput parseFlakeInput (EvalState & state,
102- std::string_view inputName, Value * value, const PosIdx pos,
102+ std::optional<std:: string_view> inputName, Value * value, const PosIdx pos,
103103 const std::optional<Path> & baseDir, InputPath lockRootPath)
104104{
105105 expectType (state, nAttrs, *value, pos);
@@ -185,8 +185,8 @@ static FlakeInput parseFlakeInput(EvalState & state,
185185 input.ref = parseFlakeRef (state.fetchSettings , *url, baseDir, true , input.isFlake );
186186 }
187187
188- if (!input.follows && !input.ref )
189- input.ref = FlakeRef::fromAttrs (state.fetchSettings , {{" type" , " indirect" }, {" id" , std::string (inputName)}});
188+ if (inputName && !input.follows && !input.ref )
189+ input.ref = FlakeRef::fromAttrs (state.fetchSettings , {{" type" , " indirect" }, {" id" , std::string (* inputName)}});
190190
191191 return input;
192192}
@@ -735,7 +735,10 @@ LockedFlake lockFlake(
735735 } else
736736 throw Error (" cannot write modified lock file of flake '%s' (use '--no-write-lock-file' to ignore)" , topRef);
737737 } else {
738- warn (" not writing modified lock file of flake '%s':\n %s" , topRef, chomp (diff));
738+ if (lockFlags.warnModifiedLockFile )
739+ warn (" not writing modified lock file of flake '%s':\n %s" , topRef, chomp (diff));
740+ else
741+ debug (" not writing modified lock file of flake '%s':\n %s" , topRef, chomp (diff));
739742 flake.forceDirty = true ;
740743 }
741744 }
@@ -823,20 +826,63 @@ void initLib(const Settings & settings)
823826{
824827 auto prim_getFlake = [&settings](EvalState & state, const PosIdx pos, Value * * args, Value & v)
825828 {
826- std::string flakeRefS (state.forceStringNoCtx (*args[0 ], pos, " while evaluating the argument passed to builtins.getFlake" ));
827- auto flakeRef = parseFlakeRef (state.fetchSettings , flakeRefS, {}, true );
828- if (state.settings .pureEval && !flakeRef.input .isLocked ())
829- throw Error (" cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)" , flakeRefS, state.positions [pos]);
830-
831- callFlake (state,
832- lockFlake (settings, state, flakeRef,
833- LockFlags {
834- .updateLockFile = false ,
835- .writeLockFile = false ,
836- .useRegistries = !state.settings .pureEval && settings.useRegistries ,
837- .allowUnlocked = !state.settings .pureEval ,
838- }),
839- v);
829+ state.forceValue (*args[0 ], pos);
830+
831+ LockFlags lockFlags {
832+ .updateLockFile = false ,
833+ .writeLockFile = false ,
834+ .warnModifiedLockFile = false ,
835+ .useRegistries = !state.settings .pureEval && settings.useRegistries ,
836+ .allowUnlocked = !state.settings .pureEval ,
837+ };
838+
839+ auto flakeRef =
840+ args[0 ]->type () == nString
841+ ? ({
842+ std::string flakeRefS (state.forceStringNoCtx (*args[0 ], pos, " while evaluating the argument passed to builtins.getFlake" ));
843+ auto flakeRef = parseFlakeRef (state.fetchSettings , flakeRefS, {}, true );
844+ if (state.settings .pureEval && !flakeRef.input .isLocked ())
845+ throw Error (" cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)" , flakeRefS, state.positions [pos]);
846+ flakeRef;
847+ })
848+ : ({
849+ auto flakeInput = parseFlakeInput (state, std::nullopt , args[0 ], pos, {}, {});
850+
851+ /* Convert the result of parseFlakeInput() into a
852+ overrides map and a top-level flakeref. */
853+ std::function<void (const InputPath & inputPath, const FlakeInput & input)> recurse;
854+
855+ recurse = [&](const InputPath & inputPath, const FlakeInput & input)
856+ {
857+ if (!input.ref )
858+ state.error <EvalError>(" 'builtins.getFlake' requires attribute 'url'" )
859+ .atPos (*args[0 ])
860+ .debugThrow ();
861+ if (input.follows )
862+ state.error <EvalError>(" 'builtins.getFlake' does not permit attribute 'follows'" )
863+ .atPos (*args[0 ])
864+ .debugThrow ();
865+ if (!input.isFlake )
866+ state.error <EvalError>(" 'builtins.getFlake' does not permit attribute 'flake = false'; use 'builtins.fetchTree' instead" )
867+ .atPos (*args[0 ])
868+ .debugThrow ();
869+
870+ for (auto & [inputName, input2] : input.overrides ) {
871+ auto inputPath2{inputPath};
872+ inputPath2.push_back (inputName);
873+
874+ recurse (inputPath2, input2);
875+
876+ lockFlags.inputOverrides .insert_or_assign (inputPath2, input2.ref .value ());
877+ }
878+ };
879+
880+ recurse ({}, flakeInput);
881+
882+ flakeInput.ref .value ();
883+ });
884+
885+ callFlake (state, lockFlake (settings, state, flakeRef, lockFlags), v);
840886 };
841887
842888 RegisterPrimOp::primOps->push_back ({
@@ -856,6 +902,15 @@ void initLib(const Settings & settings)
856902 ```nix
857903 (builtins.getFlake "github:edolstra/dwarffs").rev
858904 ```
905+
906+ It is possible to override inputs of the flake using the same syntax to specify flake inputs in `flake.nix`, e.g.
907+
908+ ```nix
909+ builtins.getFlake {
910+ url = "github:NixOS/nix/55bc52401966fbffa525c574c14f67b00bc4fb3a";
911+ inputs.nixpkgs.url = "github:NixOS/nixpkgs/c69a9bffbecde46b4b939465422ddc59493d3e4d";
912+ }
913+ ```
859914 )" ,
860915 .fun = prim_getFlake,
861916 .experimentalFeature = Xp::Flakes,
0 commit comments