Skip to content

Commit c9f71f3

Browse files
authored
Merge pull request #338 from DeterminateSystems/getflake-path
builtins.getFlake: Support path values
2 parents ae71c42 + c80220e commit c9f71f3

4 files changed

Lines changed: 64 additions & 36 deletions

File tree

src/libflake/flake-primops.cc

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -35,36 +35,39 @@ namespace nix::flake::primops {
3535
PrimOp getFlake(const Settings & settings)
3636
{
3737
auto prim_getFlake = [&settings](EvalState & state, const PosIdx pos, Value ** args, Value & v) {
38-
NixStringContext context;
39-
std::string flakeRefS(
40-
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.getFlake"));
41-
auto rewrites = state.realiseContext(context);
42-
flakeRefS = state.devirtualize(rewriteStrings(flakeRefS, rewrites), context);
43-
if (hasContext(context))
44-
// FIXME: this should really be an error.
45-
warn(
46-
"In 'builtins.getFlake', the flakeref '%s' has string context, but that's not allowed. This may become a fatal error in the future.",
47-
flakeRefS);
48-
auto flakeRef = nix::parseFlakeRef(state.fetchSettings, flakeRefS, {}, true);
49-
if (state.settings.pureEval && !flakeRef.input.isLocked(state.fetchSettings))
50-
throw Error(
51-
"cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)",
52-
flakeRefS,
53-
state.positions[pos]);
54-
55-
callFlake(
56-
state,
57-
lockFlake(
58-
settings,
59-
state,
60-
flakeRef,
61-
LockFlags{
62-
.updateLockFile = false,
63-
.writeLockFile = false,
64-
.useRegistries = !state.settings.pureEval && settings.useRegistries,
65-
.allowUnlocked = !state.settings.pureEval,
66-
}),
67-
v);
38+
state.forceValue(*args[0], pos);
39+
40+
LockFlags lockFlags{
41+
.updateLockFile = false,
42+
.writeLockFile = false,
43+
.useRegistries = !state.settings.pureEval && settings.useRegistries,
44+
.allowUnlocked = !state.settings.pureEval,
45+
};
46+
47+
if (args[0]->type() == nPath) {
48+
auto path = state.realisePath(pos, *args[0]);
49+
callFlake(state, lockFlake(settings, state, path, lockFlags), v);
50+
} else {
51+
NixStringContext context;
52+
std::string flakeRefS(
53+
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.getFlake"));
54+
auto rewrites = state.realiseContext(context);
55+
flakeRefS = state.devirtualize(rewriteStrings(flakeRefS, rewrites), context);
56+
if (hasContext(context))
57+
// FIXME: this should really be an error.
58+
warn(
59+
"In 'builtins.getFlake', the flakeref '%s' has string context, but that's not allowed. This may become a fatal error in the future.",
60+
flakeRefS);
61+
62+
auto flakeRef = nix::parseFlakeRef(state.fetchSettings, flakeRefS, {}, true);
63+
if (state.settings.pureEval && !flakeRef.input.isLocked(state.fetchSettings))
64+
throw Error(
65+
"cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)",
66+
flakeRefS,
67+
state.positions[pos]);
68+
69+
callFlake(state, lockFlake(settings, state, flakeRef, lockFlags), v);
70+
}
6871
};
6972

7073
return PrimOp{

src/libflake/flake.cc

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -428,17 +428,13 @@ static LockFile readLockFile(const fetchers::Settings & fetchSettings, const Sou
428428
: LockFile();
429429
}
430430

431-
/* Compute an in-memory lock file for the specified top-level flake,
432-
and optionally write it to file, if the flake is writable. */
433-
LockedFlake
434-
lockFlake(const Settings & settings, EvalState & state, const FlakeRef & topRef, const LockFlags & lockFlags)
431+
LockedFlake lockFlake(
432+
const Settings & settings, EvalState & state, const FlakeRef & topRef, const LockFlags & lockFlags, Flake flake)
435433
{
436434
auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries);
437435
auto useRegistriesTop = useRegistries ? fetchers::UseRegistries::All : fetchers::UseRegistries::No;
438436
auto useRegistriesInputs = useRegistries ? fetchers::UseRegistries::Limited : fetchers::UseRegistries::No;
439437

440-
auto flake = getFlake(state, topRef, useRegistriesTop, {}, false);
441-
442438
if (lockFlags.applyNixConfig) {
443439
flake.config.apply(settings);
444440
state.store->setOptions();
@@ -952,6 +948,22 @@ lockFlake(const Settings & settings, EvalState & state, const FlakeRef & topRef,
952948
}
953949
}
954950

951+
LockedFlake
952+
lockFlake(const Settings & settings, EvalState & state, const FlakeRef & topRef, const LockFlags & lockFlags)
953+
{
954+
auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries);
955+
auto useRegistriesTop = useRegistries ? fetchers::UseRegistries::All : fetchers::UseRegistries::No;
956+
return lockFlake(settings, state, topRef, lockFlags, getFlake(state, topRef, useRegistriesTop, {}, false));
957+
}
958+
959+
LockedFlake
960+
lockFlake(const Settings & settings, EvalState & state, const SourcePath & flakeDir, const LockFlags & lockFlags)
961+
{
962+
/* We need a fake flakeref to put in the `Flake` struct, but it's not used for anything. */
963+
auto fakeRef = parseFlakeRef(state.fetchSettings, "flake:get-flake");
964+
return lockFlake(settings, state, fakeRef, lockFlags, readFlake(state, fakeRef, fakeRef, fakeRef, flakeDir, {}));
965+
}
966+
955967
static ref<SourceAccessor> makeInternalFS()
956968
{
957969
auto internalFS = make_ref<MemorySourceAccessor>(MemorySourceAccessor{});

src/libflake/include/nix/flake/flake.hh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,9 +232,16 @@ struct LockFlags
232232
bool requireLockable = true;
233233
};
234234

235+
/*
236+
* Compute an in-memory lock file for the specified top-level flake, and optionally write it to file, if the flake is
237+
* writable.
238+
*/
235239
LockedFlake
236240
lockFlake(const Settings & settings, EvalState & state, const FlakeRef & flakeRef, const LockFlags & lockFlags);
237241

242+
LockedFlake
243+
lockFlake(const Settings & settings, EvalState & state, const SourcePath & flakeDir, const LockFlags & lockFlags);
244+
238245
void callFlake(EvalState & state, const LockedFlake & lockedFlake, Value & v);
239246

240247
/**

tests/functional/flakes/get-flake.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,19 @@ cat > "$flake1Dir/subflake/flake.nix" <<EOF
99
{
1010
outputs = { self }:
1111
let
12+
# Bad, legacy way of getting a flake from an input.
1213
parentFlake = builtins.getFlake (builtins.flakeRefToString { type = "path"; path = self.sourceInfo.outPath; narHash = self.narHash; });
14+
# Better way using a path value.
15+
parentFlake2 = builtins.getFlake ./..;
1316
in {
1417
x = parentFlake.number;
18+
y = parentFlake2.number;
1519
};
1620
}
1721
EOF
1822
git -C "$flake1Dir" add subflake/flake.nix
1923

2024
expectStderr 0 nix eval "$flake1Dir/subflake#x" | grepQuiet "This may become a fatal error in the future"
2125
[[ $(nix eval "$flake1Dir/subflake#x") = 123 ]]
26+
27+
[[ $(nix eval "$flake1Dir/subflake#y") = 123 ]]

0 commit comments

Comments
 (0)