Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
50 changes: 27 additions & 23 deletions src/libflake/flake-primops.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,35 +35,39 @@ namespace nix::flake::primops {
PrimOp getFlake(const Settings & settings)
{
auto prim_getFlake = [&settings](EvalState & state, const PosIdx pos, Value ** args, Value & v) {
std::string flakeRefS(
state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake"));
auto flakeRef = nix::parseFlakeRef(state.fetchSettings, flakeRefS, {}, true);
if (state.settings.pureEval && !flakeRef.input.isLocked(state.fetchSettings))
throw Error(
"cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)",
flakeRefS,
state.positions[pos]);

callFlake(
state,
lockFlake(
settings,
state,
flakeRef,
LockFlags{
.updateLockFile = false,
.writeLockFile = false,
.useRegistries = !state.settings.pureEval && settings.useRegistries,
.allowUnlocked = !state.settings.pureEval,
}),
v);
state.forceValue(*args[0], pos);

LockFlags lockFlags{
.updateLockFile = false,
.writeLockFile = false,
.useRegistries = !state.settings.pureEval && settings.useRegistries,
.allowUnlocked = !state.settings.pureEval,
};

if (args[0]->type() == nPath) {
auto path = state.realisePath(pos, *args[0]);
callFlake(state, lockFlake(settings, state, path, lockFlags), v);
} else {
NixStringContext context;
Comment thread
xokdvium marked this conversation as resolved.
std::string flakeRefS(
state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake"));

auto flakeRef = nix::parseFlakeRef(state.fetchSettings, flakeRefS, {}, true);
if (state.settings.pureEval && !flakeRef.input.isLocked(state.fetchSettings))
throw Error(
"cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)",
flakeRefS,
state.positions[pos]);

callFlake(state, lockFlake(settings, state, flakeRef, lockFlags), v);
}
};

return PrimOp{
.name = "__getFlake",
.args = {"args"},
.doc = R"(
Fetch a flake from a flake reference, and return its output attributes and some metadata. For example:
Fetch a flake from a flake reference or a path, and return its output attributes and some metadata. For example:

```nix
(builtins.getFlake "nix/55bc52401966fbffa525c574c14f67b00bc4fb3a").packages.x86_64-linux.nix
Expand Down
24 changes: 18 additions & 6 deletions src/libflake/flake.cc
Original file line number Diff line number Diff line change
Expand Up @@ -416,19 +416,15 @@ static LockFile readLockFile(const fetchers::Settings & fetchSettings, const Sou
: LockFile();
}

/* Compute an in-memory lock file for the specified top-level flake,
and optionally write it to file, if the flake is writable. */
LockedFlake
lockFlake(const Settings & settings, EvalState & state, const FlakeRef & topRef, const LockFlags & lockFlags)
LockedFlake lockFlake(
const Settings & settings, EvalState & state, const FlakeRef & topRef, const LockFlags & lockFlags, Flake flake)
{
experimentalFeatureSettings.require(Xp::Flakes);

auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries);
auto useRegistriesTop = useRegistries ? fetchers::UseRegistries::All : fetchers::UseRegistries::No;
auto useRegistriesInputs = useRegistries ? fetchers::UseRegistries::Limited : fetchers::UseRegistries::No;

auto flake = getFlake(state, topRef, useRegistriesTop, {});

if (lockFlags.applyNixConfig) {
flake.config.apply(settings);
state.store->setOptions();
Expand Down Expand Up @@ -908,6 +904,22 @@ lockFlake(const Settings & settings, EvalState & state, const FlakeRef & topRef,
}
}

LockedFlake
lockFlake(const Settings & settings, EvalState & state, const FlakeRef & topRef, const LockFlags & lockFlags)
{
auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries);
auto useRegistriesTop = useRegistries ? fetchers::UseRegistries::All : fetchers::UseRegistries::No;
return lockFlake(settings, state, topRef, lockFlags, getFlake(state, topRef, useRegistriesTop, {}));
}

LockedFlake
lockFlake(const Settings & settings, EvalState & state, const SourcePath & flakeDir, const LockFlags & lockFlags)
{
/* We need a fake flakeref to put in the `Flake` struct, but it's not used for anything. */
auto fakeRef = parseFlakeRef(state.fetchSettings, "flake:get-flake");
Comment thread
xokdvium marked this conversation as resolved.
return lockFlake(settings, state, fakeRef, lockFlags, readFlake(state, fakeRef, fakeRef, fakeRef, flakeDir, {}));
}

static ref<SourceAccessor> makeInternalFS()
{
auto internalFS = make_ref<MemorySourceAccessor>(MemorySourceAccessor{});
Expand Down
7 changes: 7 additions & 0 deletions src/libflake/include/nix/flake/flake.hh
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,16 @@ struct LockFlags
std::set<NonEmptyInputAttrPath> inputUpdates;
};

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

LockedFlake
lockFlake(const Settings & settings, EvalState & state, const SourcePath & flakeDir, const LockFlags & lockFlags);

void callFlake(EvalState & state, const LockedFlake & lockedFlake, Value & v);

/**
Expand Down
2 changes: 2 additions & 0 deletions tests/functional/flakes/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ writeSimpleFlake() {
baseName = builtins.baseNameOf ./.;

root = ./.;

number = 123;
};
}
EOF
Expand Down
26 changes: 26 additions & 0 deletions tests/functional/flakes/get-flake.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env bash

source ./common.sh

createFlake1

mkdir -p "$flake1Dir/subflake"
cat > "$flake1Dir/subflake/flake.nix" <<EOF
{
outputs = { self }:
let
# Bad, legacy way of getting a flake from an input.
parentFlake = builtins.getFlake (builtins.flakeRefToString { type = "path"; path = self.sourceInfo.outPath; narHash = self.narHash; });
# Better way using a path value.
parentFlake2 = builtins.getFlake ./..;
in {
x = parentFlake.number;
y = parentFlake2.number;
};
}
EOF
git -C "$flake1Dir" add subflake/flake.nix

[[ $(nix eval "$flake1Dir/subflake#x") = 123 ]]

[[ $(nix eval "$flake1Dir/subflake#y") = 123 ]]
1 change: 1 addition & 0 deletions tests/functional/flakes/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ suites += {
'source-paths.sh',
'old-lockfiles.sh',
'trace-ifd.sh',
'get-flake.sh',
],
'workdir' : meson.current_source_dir(),
}
Loading