nix: Add --flake flag
This allows using an arbitrary "provides" attribute from the specified flake. For example: nix build --flake nixpkgs packages.hello (Maybe provides.packages should be used for consistency...)
This commit is contained in:
parent
272b58220d
commit
beab05851b
|
@ -318,8 +318,6 @@ public:
|
||||||
|
|
||||||
const FlakeRegistry & getFlakeRegistry();
|
const FlakeRegistry & getFlakeRegistry();
|
||||||
|
|
||||||
Value * makeFlakeRegistryValue();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<FlakeRegistry> _flakeRegistry;
|
std::unique_ptr<FlakeRegistry> _flakeRegistry;
|
||||||
std::once_flag _flakeRegistryInit;
|
std::once_flag _flakeRegistryInit;
|
||||||
|
|
|
@ -40,18 +40,18 @@ const FlakeRegistry & EvalState::getFlakeRegistry()
|
||||||
return *_flakeRegistry;
|
return *_flakeRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value * EvalState::makeFlakeRegistryValue()
|
Value * makeFlakeRegistryValue(EvalState & state)
|
||||||
{
|
{
|
||||||
auto v = allocValue();
|
auto v = state.allocValue();
|
||||||
|
|
||||||
auto registry = getFlakeRegistry();
|
auto registry = state.getFlakeRegistry();
|
||||||
|
|
||||||
mkAttrs(*v, registry.entries.size());
|
state.mkAttrs(*v, registry.entries.size());
|
||||||
|
|
||||||
for (auto & entry : registry.entries) {
|
for (auto & entry : registry.entries) {
|
||||||
auto vEntry = allocAttr(*v, entry.first);
|
auto vEntry = state.allocAttr(*v, entry.first);
|
||||||
mkAttrs(*vEntry, 2);
|
state.mkAttrs(*vEntry, 2);
|
||||||
mkString(*allocAttr(*vEntry, symbols.create("uri")), entry.second.ref.to_string());
|
mkString(*state.allocAttr(*vEntry, state.symbols.create("uri")), entry.second.ref.to_string());
|
||||||
vEntry->attrs->sort();
|
vEntry->attrs->sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,16 +163,19 @@ static Flake getFlake(EvalState & state, const FlakeRef & flakeRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Given a flake reference, recursively fetch it and its
|
/* Given a flake reference, recursively fetch it and its
|
||||||
dependencies. */
|
dependencies.
|
||||||
static std::map<FlakeId, Flake> resolveFlake(EvalState & state,
|
FIXME: this should return a graph of flakes.
|
||||||
|
*/
|
||||||
|
static std::tuple<FlakeId, std::map<FlakeId, Flake>> resolveFlake(EvalState & state,
|
||||||
const FlakeRef & topRef, bool impureTopRef)
|
const FlakeRef & topRef, bool impureTopRef)
|
||||||
{
|
{
|
||||||
std::map<FlakeId, Flake> done;
|
std::map<FlakeId, Flake> done;
|
||||||
std::queue<std::tuple<FlakeRef, bool>> todo;
|
std::queue<std::tuple<FlakeRef, bool>> todo;
|
||||||
todo.push({topRef, impureTopRef});
|
std::optional<FlakeId> topFlakeId; /// FIXME: ambiguous
|
||||||
|
todo.push({topRef, true});
|
||||||
|
|
||||||
while (!todo.empty()) {
|
while (!todo.empty()) {
|
||||||
auto [flakeRef, impureRef] = todo.front();
|
auto [flakeRef, toplevel] = todo.front();
|
||||||
todo.pop();
|
todo.pop();
|
||||||
|
|
||||||
if (auto refData = std::get_if<FlakeRef::IsFlakeId>(&flakeRef.data)) {
|
if (auto refData = std::get_if<FlakeRef::IsFlakeId>(&flakeRef.data)) {
|
||||||
|
@ -180,26 +183,27 @@ static std::map<FlakeId, Flake> resolveFlake(EvalState & state,
|
||||||
flakeRef = lookupFlake(state, flakeRef);
|
flakeRef = lookupFlake(state, flakeRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evalSettings.pureEval && !flakeRef.isImmutable() && !impureRef)
|
if (evalSettings.pureEval && !flakeRef.isImmutable() && (!toplevel || !impureTopRef))
|
||||||
throw Error("mutable flake '%s' is not allowed in pure mode; use --no-pure-eval to disable", flakeRef.to_string());
|
throw Error("mutable flake '%s' is not allowed in pure mode; use --no-pure-eval to disable", flakeRef.to_string());
|
||||||
|
|
||||||
auto flake = getFlake(state, flakeRef);
|
auto flake = getFlake(state, flakeRef);
|
||||||
|
|
||||||
if (done.count(flake.id)) continue;
|
if (done.count(flake.id)) continue;
|
||||||
|
|
||||||
|
if (toplevel) topFlakeId = flake.id;
|
||||||
|
|
||||||
for (auto & require : flake.requires)
|
for (auto & require : flake.requires)
|
||||||
todo.push({require, false});
|
todo.push({require, false});
|
||||||
|
|
||||||
done.emplace(flake.id, flake);
|
done.emplace(flake.id, flake);
|
||||||
}
|
}
|
||||||
|
|
||||||
return done;
|
assert(topFlakeId);
|
||||||
|
return {*topFlakeId, done};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
Value * makeFlakeValue(EvalState & state, std::string flakeUri, Value & v)
|
||||||
{
|
{
|
||||||
auto flakeUri = state.forceStringNoCtx(*args[0], pos);
|
|
||||||
|
|
||||||
// FIXME: temporary hack to make the default installation source
|
// FIXME: temporary hack to make the default installation source
|
||||||
// work.
|
// work.
|
||||||
bool impure = false;
|
bool impure = false;
|
||||||
|
@ -210,14 +214,20 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
|
|
||||||
auto flakeRef = FlakeRef(flakeUri);
|
auto flakeRef = FlakeRef(flakeUri);
|
||||||
|
|
||||||
auto flakes = resolveFlake(state, flakeUri, impure);
|
auto [topFlakeId, flakes] = resolveFlake(state, flakeUri, impure);
|
||||||
|
|
||||||
|
// FIXME: we should call each flake with only its dependencies
|
||||||
|
// (rather than the closure of the top-level flake).
|
||||||
|
|
||||||
auto vResult = state.allocValue();
|
auto vResult = state.allocValue();
|
||||||
|
|
||||||
state.mkAttrs(*vResult, flakes.size());
|
state.mkAttrs(*vResult, flakes.size());
|
||||||
|
|
||||||
|
Value * vTop = 0;
|
||||||
|
|
||||||
for (auto & flake : flakes) {
|
for (auto & flake : flakes) {
|
||||||
auto vFlake = state.allocAttr(*vResult, flake.second.id);
|
auto vFlake = state.allocAttr(*vResult, flake.second.id);
|
||||||
|
if (topFlakeId == flake.second.id) vTop = vFlake;
|
||||||
state.mkAttrs(*vFlake, 2);
|
state.mkAttrs(*vFlake, 2);
|
||||||
mkString(*state.allocAttr(*vFlake, state.sDescription), flake.second.description);
|
mkString(*state.allocAttr(*vFlake, state.sDescription), flake.second.description);
|
||||||
auto vProvides = state.allocAttr(*vFlake, state.symbols.create("provides"));
|
auto vProvides = state.allocAttr(*vFlake, state.symbols.create("provides"));
|
||||||
|
@ -228,6 +238,14 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
vResult->attrs->sort();
|
vResult->attrs->sort();
|
||||||
|
|
||||||
v = *vResult;
|
v = *vResult;
|
||||||
|
|
||||||
|
assert(vTop);
|
||||||
|
return vTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
|
{
|
||||||
|
makeFlakeValue(state, state.forceStringNoCtx(*args[0], pos), v);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp r2("getFlake", 1, prim_getFlake);
|
static RegisterPrimOp r2("getFlake", 1, prim_getFlake);
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
struct Value;
|
||||||
|
class EvalState;
|
||||||
|
|
||||||
struct FlakeRegistry
|
struct FlakeRegistry
|
||||||
{
|
{
|
||||||
struct Entry
|
struct Entry
|
||||||
|
@ -14,4 +17,8 @@ struct FlakeRegistry
|
||||||
std::map<FlakeId, Entry> entries;
|
std::map<FlakeId, Entry> entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Value * makeFlakeRegistryValue(EvalState & state);
|
||||||
|
|
||||||
|
Value * makeFlakeValue(EvalState & state, std::string flakeUri, Value & v);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,8 @@ struct Installable
|
||||||
|
|
||||||
struct SourceExprCommand : virtual Args, StoreCommand, MixEvalArgs
|
struct SourceExprCommand : virtual Args, StoreCommand, MixEvalArgs
|
||||||
{
|
{
|
||||||
Path file;
|
std::optional<Path> file;
|
||||||
|
std::optional<std::string> flakeUri;
|
||||||
|
|
||||||
SourceExprCommand();
|
SourceExprCommand();
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "get-drvs.hh"
|
#include "get-drvs.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
|
#include "primops/flake.hh"
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
|
@ -18,8 +19,15 @@ SourceExprCommand::SourceExprCommand()
|
||||||
.shortName('f')
|
.shortName('f')
|
||||||
.longName("file")
|
.longName("file")
|
||||||
.label("file")
|
.label("file")
|
||||||
.description("evaluate FILE rather than the default")
|
.description("evaluate FILE rather than use the default installation source")
|
||||||
.dest(&file);
|
.dest(&file);
|
||||||
|
|
||||||
|
mkFlag()
|
||||||
|
.shortName('F')
|
||||||
|
.longName("flake")
|
||||||
|
.label("flake")
|
||||||
|
.description("evaluate FLAKE rather than use the default installation source")
|
||||||
|
.dest(&flakeUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value * SourceExprCommand::getSourceExpr(EvalState & state)
|
Value * SourceExprCommand::getSourceExpr(EvalState & state)
|
||||||
|
@ -28,9 +36,17 @@ Value * SourceExprCommand::getSourceExpr(EvalState & state)
|
||||||
|
|
||||||
vSourceExpr = state.allocValue();
|
vSourceExpr = state.allocValue();
|
||||||
|
|
||||||
if (file != "")
|
if (file && flakeUri)
|
||||||
state.evalFile(lookupFileArg(state, file), *vSourceExpr);
|
throw Error("cannot use both --file and --flake");
|
||||||
else {
|
|
||||||
|
if (file)
|
||||||
|
state.evalFile(lookupFileArg(state, *file), *vSourceExpr);
|
||||||
|
else if (flakeUri) {
|
||||||
|
// FIXME: handle flakeUri being a relative path
|
||||||
|
auto vTemp = state.allocValue();
|
||||||
|
auto vFlake = *makeFlakeValue(state, "impure:" + *flakeUri, *vTemp);
|
||||||
|
*vSourceExpr = *((*vFlake.attrs->get(state.symbols.create("provides")))->value);
|
||||||
|
} else {
|
||||||
// FIXME: remove "impure" hack, call some non-user-accessible
|
// FIXME: remove "impure" hack, call some non-user-accessible
|
||||||
// variant of getFlake instead.
|
// variant of getFlake instead.
|
||||||
auto fun = state.parseExprFromString(
|
auto fun = state.parseExprFromString(
|
||||||
|
@ -38,7 +54,7 @@ Value * SourceExprCommand::getSourceExpr(EvalState & state)
|
||||||
" (getFlake (\"impure:\" + flakeInfo.uri)).${flakeName}.provides.packages or {})", "/");
|
" (getFlake (\"impure:\" + flakeInfo.uri)).${flakeName}.provides.packages or {})", "/");
|
||||||
auto vFun = state.allocValue();
|
auto vFun = state.allocValue();
|
||||||
state.eval(fun, *vFun);
|
state.eval(fun, *vFun);
|
||||||
auto vRegistry = state.makeFlakeRegistryValue();
|
auto vRegistry = makeFlakeRegistryValue(state);
|
||||||
mkApp(*vSourceExpr, *vFun, *vRegistry);
|
mkApp(*vSourceExpr, *vFun, *vRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue