@@ -116,12 +116,47 @@ static void expectType(EvalState & state, ValueType type,
116116 showType (type), showType (value.type ()), state.positions [pos]);
117117}
118118
119- static std::map<FlakeId, FlakeInput> parseFlakeInputs (
119+ static std::pair<std:: map<FlakeId, FlakeInput>, fetchers::Attrs > parseFlakeInputs (
120120 EvalState & state,
121121 Value * value,
122122 const PosIdx pos,
123123 const InputAttrPath & lockRootAttrPath,
124- const SourcePath & flakeDir);
124+ const SourcePath & flakeDir,
125+ bool allowSelf);
126+
127+ static void parseFlakeInputAttr (
128+ EvalState & state,
129+ const Attr & attr,
130+ fetchers::Attrs & attrs)
131+ {
132+ // Allow selecting a subset of enum values
133+ #pragma GCC diagnostic push
134+ #pragma GCC diagnostic ignored "-Wswitch-enum"
135+ switch (attr.value ->type ()) {
136+ case nString:
137+ attrs.emplace (state.symbols [attr.name ], attr.value ->c_str ());
138+ break ;
139+ case nBool:
140+ attrs.emplace (state.symbols [attr.name ], Explicit<bool > { attr.value ->boolean () });
141+ break ;
142+ case nInt: {
143+ auto intValue = attr.value ->integer ().value ;
144+ if (intValue < 0 )
145+ state.error <EvalError>(" negative value given for flake input attribute %1%: %2%" , state.symbols [attr.name ], intValue).debugThrow ();
146+ attrs.emplace (state.symbols [attr.name ], uint64_t (intValue));
147+ break ;
148+ }
149+ default :
150+ if (attr.name == state.symbols .create (" publicKeys" )) {
151+ experimentalFeatureSettings.require (Xp::VerifiedFetches);
152+ NixStringContext emptyContext = {};
153+ attrs.emplace (state.symbols [attr.name ], printValueAsJSON (state, true , *attr.value , attr.pos , emptyContext).dump ());
154+ } else
155+ state.error <TypeError>(" flake input attribute '%s' is %s while a string, Boolean, or integer is expected" ,
156+ state.symbols [attr.name ], showType (*attr.value )).debugThrow ();
157+ }
158+ #pragma GCC diagnostic pop
159+ }
125160
126161static FlakeInput parseFlakeInput (
127162 EvalState & state,
@@ -166,44 +201,14 @@ static FlakeInput parseFlakeInput(
166201 expectType (state, nBool, *attr.value , attr.pos );
167202 input.isFlake = attr.value ->boolean ();
168203 } else if (attr.name == sInputs ) {
169- input.overrides = parseFlakeInputs (state, attr.value , attr.pos , lockRootAttrPath, flakeDir) ;
204+ input.overrides = parseFlakeInputs (state, attr.value , attr.pos , lockRootAttrPath, flakeDir, false ). first ;
170205 } else if (attr.name == sFollows ) {
171206 expectType (state, nString, *attr.value , attr.pos );
172207 auto follows (parseInputAttrPath (attr.value ->c_str ()));
173208 follows.insert (follows.begin (), lockRootAttrPath.begin (), lockRootAttrPath.end ());
174209 input.follows = follows;
175- } else {
176- // Allow selecting a subset of enum values
177- #pragma GCC diagnostic push
178- #pragma GCC diagnostic ignored "-Wswitch-enum"
179- switch (attr.value ->type ()) {
180- case nString:
181- attrs.emplace (state.symbols [attr.name ], attr.value ->c_str ());
182- break ;
183- case nBool:
184- attrs.emplace (state.symbols [attr.name ], Explicit<bool > { attr.value ->boolean () });
185- break ;
186- case nInt: {
187- auto intValue = attr.value ->integer ().value ;
188-
189- if (intValue < 0 ) {
190- state.error <EvalError>(" negative value given for flake input attribute %1%: %2%" , state.symbols [attr.name ], intValue).debugThrow ();
191- }
192-
193- attrs.emplace (state.symbols [attr.name ], uint64_t (intValue));
194- break ;
195- }
196- default :
197- if (attr.name == state.symbols .create (" publicKeys" )) {
198- experimentalFeatureSettings.require (Xp::VerifiedFetches);
199- NixStringContext emptyContext = {};
200- attrs.emplace (state.symbols [attr.name ], printValueAsJSON (state, true , *attr.value , pos, emptyContext).dump ());
201- } else
202- state.error <TypeError>(" flake input attribute '%s' is %s while a string, Boolean, or integer is expected" ,
203- state.symbols [attr.name ], showType (*attr.value )).debugThrow ();
204- }
205- #pragma GCC diagnostic pop
206- }
210+ } else
211+ parseFlakeInputAttr (state, attr, attrs);
207212 } catch (Error & e) {
208213 e.addTrace (
209214 state.positions [attr.pos ],
@@ -233,28 +238,39 @@ static FlakeInput parseFlakeInput(
233238 return input;
234239}
235240
236- static std::map<FlakeId, FlakeInput> parseFlakeInputs (
241+ static std::pair<std:: map<FlakeId, FlakeInput>, fetchers::Attrs > parseFlakeInputs (
237242 EvalState & state,
238243 Value * value,
239244 const PosIdx pos,
240245 const InputAttrPath & lockRootAttrPath,
241- const SourcePath & flakeDir)
246+ const SourcePath & flakeDir,
247+ bool allowSelf)
242248{
243249 std::map<FlakeId, FlakeInput> inputs;
250+ fetchers::Attrs selfAttrs;
244251
245252 expectType (state, nAttrs, *value, pos);
246253
247254 for (auto & inputAttr : *value->attrs ()) {
248- inputs.emplace (state.symbols [inputAttr.name ],
249- parseFlakeInput (state,
250- state.symbols [inputAttr.name ],
251- inputAttr.value ,
252- inputAttr.pos ,
253- lockRootAttrPath,
254- flakeDir));
255+ auto inputName = state.symbols [inputAttr.name ];
256+ if (inputName == " self" ) {
257+ if (!allowSelf)
258+ throw Error (" 'self' input attribute not allowed at %s" , state.positions [inputAttr.pos ]);
259+ expectType (state, nAttrs, *inputAttr.value , inputAttr.pos );
260+ for (auto & attr : *inputAttr.value ->attrs ())
261+ parseFlakeInputAttr (state, attr, selfAttrs);
262+ } else {
263+ inputs.emplace (inputName,
264+ parseFlakeInput (state,
265+ inputName,
266+ inputAttr.value ,
267+ inputAttr.pos ,
268+ lockRootAttrPath,
269+ flakeDir));
270+ }
255271 }
256272
257- return inputs;
273+ return { inputs, selfAttrs} ;
258274}
259275
260276static Flake readFlake (
@@ -286,8 +302,11 @@ static Flake readFlake(
286302
287303 auto sInputs = state.symbols .create (" inputs" );
288304
289- if (auto inputs = vInfo.attrs ()->get (sInputs ))
290- flake.inputs = parseFlakeInputs (state, inputs->value , inputs->pos , lockRootAttrPath, flakeDir);
305+ if (auto inputs = vInfo.attrs ()->get (sInputs )) {
306+ auto [flakeInputs, selfAttrs] = parseFlakeInputs (state, inputs->value , inputs->pos , lockRootAttrPath, flakeDir, true );
307+ flake.inputs = std::move (flakeInputs);
308+ flake.selfAttrs = std::move (selfAttrs);
309+ }
291310
292311 auto sOutputs = state.symbols .create (" outputs" );
293312
@@ -361,6 +380,23 @@ static Flake readFlake(
361380 return flake;
362381}
363382
383+ static FlakeRef applySelfAttrs (
384+ const FlakeRef & ref,
385+ const Flake & flake)
386+ {
387+ auto newRef (ref);
388+
389+ std::set<std::string> allowedAttrs{" submodules" };
390+
391+ for (auto & attr : flake.selfAttrs ) {
392+ if (!allowedAttrs.contains (attr.first ))
393+ throw Error (" flake 'self' attribute '%s' is not supported" , attr.first );
394+ newRef.input .attrs .insert_or_assign (attr.first , attr.second );
395+ }
396+
397+ return newRef;
398+ }
399+
364400static Flake getFlake (
365401 EvalState & state,
366402 const FlakeRef & originalRef,
@@ -372,6 +408,22 @@ static Flake getFlake(
372408 auto [accessor, resolvedRef, lockedRef] = fetchOrSubstituteTree (
373409 state, originalRef, useRegistries, flakeCache);
374410
411+ // Parse/eval flake.nix to get at the input.self attributes.
412+ auto flake = readFlake (state, originalRef, resolvedRef, lockedRef, {accessor}, lockRootAttrPath);
413+
414+ // Re-fetch the tree if necessary.
415+ auto newLockedRef = applySelfAttrs (lockedRef, flake);
416+
417+ if (lockedRef != newLockedRef) {
418+ debug (" refetching input '%s' due to self attribute" , newLockedRef);
419+ // FIXME: need to remove attrs that are invalidated by the changed input attrs, such as 'narHash'.
420+ newLockedRef.input .attrs .erase (" narHash" );
421+ auto [accessor2, resolvedRef2, lockedRef2] = fetchOrSubstituteTree (
422+ state, newLockedRef, false , flakeCache);
423+ accessor = accessor2;
424+ lockedRef = lockedRef2;
425+ }
426+
375427 // Copy the tree to the store.
376428 auto storePath = copyInputToStore (state, lockedRef.input , accessor);
377429
@@ -492,6 +544,7 @@ LockedFlake lockFlake(
492544 /* Get the overrides (i.e. attributes of the form
493545 'inputs.nixops.inputs.nixpkgs.url = ...'). */
494546 for (auto & [id, input] : flakeInputs) {
547+ // if (id == "self") continue;
495548 for (auto & [idOverride, inputOverride] : input.overrides ) {
496549 auto inputAttrPath (inputAttrPathPrefix);
497550 inputAttrPath.push_back (id);
0 commit comments