diff options
Diffstat (limited to 'src/nix/flake.cc')
-rw-r--r-- | src/nix/flake.cc | 188 |
1 files changed, 135 insertions, 53 deletions
diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 336f6723a..053a9c9e1 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1,4 +1,5 @@ #include "command.hh" +#include "installable-flake.hh" #include "common-args.hh" #include "shared.hh" #include "eval.hh" @@ -7,7 +8,7 @@ #include "get-drvs.hh" #include "store-api.hh" #include "derivations.hh" -#include "path-with-outputs.hh" +#include "outputs-spec.hh" #include "attr-path.hh" #include "fetchers.hh" #include "registry.hh" @@ -126,12 +127,12 @@ static void enumerateOutputs(EvalState & state, Value & vFlake, std::function<void(const std::string & name, Value & vProvide, const PosIdx pos)> callback) { auto pos = vFlake.determinePos(noPos); - state.forceAttrs(vFlake, pos); + state.forceAttrs(vFlake, pos, "while evaluating a flake to get its outputs"); auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs")); assert(aOutputs); - state.forceAttrs(*aOutputs->value, pos); + state.forceAttrs(*aOutputs->value, pos, "while evaluating the outputs of a flake"); auto sHydraJobs = state.symbols.create("hydraJobs"); @@ -215,7 +216,7 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON if (!lockedFlake.lockFile.root->inputs.empty()) logger->cout(ANSI_BOLD "Inputs:" ANSI_NORMAL); - std::unordered_set<std::shared_ptr<Node>> visited; + std::set<ref<Node>> visited; std::function<void(const Node & node, const std::string & prefix)> recurse; @@ -227,7 +228,7 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON if (auto lockedNode = std::get_if<0>(&input.second)) { logger->cout("%s" ANSI_BOLD "%s" ANSI_NORMAL ": %s", prefix + (last ? treeLast : treeConn), input.first, - *lockedNode ? (*lockedNode)->lockedRef : flake.lockedRef); + (*lockedNode)->lockedRef); bool firstVisit = visited.insert(*lockedNode).second; @@ -348,7 +349,7 @@ struct CmdFlakeCheck : FlakeCommand // FIXME auto app = App(*state, v); for (auto & i : app.context) { - auto [drvPathS, outputName] = decodeContext(i); + auto [drvPathS, outputName] = NixStringContextElem::parse(i); store->parseStorePath(drvPathS); } #endif @@ -381,23 +382,6 @@ struct CmdFlakeCheck : FlakeCommand auto checkModule = [&](const std::string & attrPath, Value & v, const PosIdx pos) { try { state->forceValue(v, pos); - if (v.isLambda()) { - if (!v.lambda.fun->hasFormals() || !v.lambda.fun->formals->ellipsis) - throw Error("module must match an open attribute set ('{ config, ... }')"); - } else if (v.type() == nAttrs) { - for (auto & attr : *v.attrs) - try { - state->forceValue(*attr.value, attr.pos); - } catch (Error & e) { - e.addTrace( - state->positions[attr.pos], - hintfmt("while evaluating the option '%s'", state->symbols[attr.name])); - throw; - } - } else - throw Error("module must be a function or an attribute set"); - // FIXME: if we have a 'nixpkgs' input, use it to - // check the module. } catch (Error & e) { e.addTrace(resolve(pos), hintfmt("while checking the NixOS module '%s'", attrPath)); reportError(e); @@ -408,13 +392,13 @@ struct CmdFlakeCheck : FlakeCommand checkHydraJobs = [&](const std::string & attrPath, Value & v, const PosIdx pos) { try { - state->forceAttrs(v, pos); + state->forceAttrs(v, pos, ""); if (state->isDerivation(v)) throw Error("jobset should not be a derivation at top-level"); for (auto & attr : *v.attrs) { - state->forceAttrs(*attr.value, attr.pos); + state->forceAttrs(*attr.value, attr.pos, ""); auto attrPath2 = concatStrings(attrPath, ".", state->symbols[attr.name]); if (state->isDerivation(*attr.value)) { Activity act(*logger, lvlChatty, actUnknown, @@ -436,7 +420,7 @@ struct CmdFlakeCheck : FlakeCommand fmt("checking NixOS configuration '%s'", attrPath)); Bindings & bindings(*state->allocBindings(0)); auto vToplevel = findAlongAttrPath(*state, "config.system.build.toplevel", bindings, v).first; - state->forceAttrs(*vToplevel, pos); + state->forceValue(*vToplevel, pos); if (!state->isDerivation(*vToplevel)) throw Error("attribute 'config.system.build.toplevel' is not a derivation"); } catch (Error & e) { @@ -450,12 +434,12 @@ struct CmdFlakeCheck : FlakeCommand Activity act(*logger, lvlChatty, actUnknown, fmt("checking template '%s'", attrPath)); - state->forceAttrs(v, pos); + state->forceAttrs(v, pos, ""); if (auto attr = v.attrs->get(state->symbols.create("path"))) { if (attr->name == state->symbols.create("path")) { PathSet context; - auto path = state->coerceToPath(attr->pos, *attr->value, context); + auto path = state->coerceToPath(attr->pos, *attr->value, context, ""); if (!store->isInStore(path)) throw Error("template '%s' has a bad 'path' attribute"); // TODO: recursively check the flake in 'path'. @@ -464,7 +448,7 @@ struct CmdFlakeCheck : FlakeCommand throw Error("template '%s' lacks attribute 'path'", attrPath); if (auto attr = v.attrs->get(state->symbols.create("description"))) - state->forceStringNoCtx(*attr->value, attr->pos); + state->forceStringNoCtx(*attr->value, attr->pos, ""); else throw Error("template '%s' lacks attribute 'description'", attrPath); @@ -521,23 +505,27 @@ struct CmdFlakeCheck : FlakeCommand warn("flake output attribute '%s' is deprecated; use '%s' instead", name, replacement); if (name == "checks") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); - state->forceAttrs(*attr.value, attr.pos); + state->forceAttrs(*attr.value, attr.pos, ""); for (auto & attr2 : *attr.value->attrs) { auto drvPath = checkDerivation( fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), *attr2.value, attr2.pos); - if (drvPath && attr_name == settings.thisSystem.get()) - drvPaths.push_back(DerivedPath::Built{*drvPath}); + if (drvPath && attr_name == settings.thisSystem.get()) { + drvPaths.push_back(DerivedPath::Built { + .drvPath = *drvPath, + .outputs = OutputsSpec::All { }, + }); + } } } } else if (name == "formatter") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); @@ -548,11 +536,11 @@ struct CmdFlakeCheck : FlakeCommand } else if (name == "packages" || name == "devShells") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); - state->forceAttrs(*attr.value, attr.pos); + state->forceAttrs(*attr.value, attr.pos, ""); for (auto & attr2 : *attr.value->attrs) checkDerivation( fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), @@ -561,11 +549,11 @@ struct CmdFlakeCheck : FlakeCommand } else if (name == "apps") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); - state->forceAttrs(*attr.value, attr.pos); + state->forceAttrs(*attr.value, attr.pos, ""); for (auto & attr2 : *attr.value->attrs) checkApp( fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), @@ -574,7 +562,7 @@ struct CmdFlakeCheck : FlakeCommand } else if (name == "defaultPackage" || name == "devShell") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); @@ -585,7 +573,7 @@ struct CmdFlakeCheck : FlakeCommand } else if (name == "defaultApp") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); @@ -596,7 +584,7 @@ struct CmdFlakeCheck : FlakeCommand } else if (name == "legacyPackages") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) { checkSystemName(state->symbols[attr.name], attr.pos); // FIXME: do getDerivations? @@ -607,7 +595,7 @@ struct CmdFlakeCheck : FlakeCommand checkOverlay(name, vOutput, pos); else if (name == "overlays") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) checkOverlay(fmt("%s.%s", name, state->symbols[attr.name]), *attr.value, attr.pos); @@ -617,14 +605,14 @@ struct CmdFlakeCheck : FlakeCommand checkModule(name, vOutput, pos); else if (name == "nixosModules") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) checkModule(fmt("%s.%s", name, state->symbols[attr.name]), *attr.value, attr.pos); } else if (name == "nixosConfigurations") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) checkNixOSConfiguration(fmt("%s.%s", name, state->symbols[attr.name]), *attr.value, attr.pos); @@ -637,14 +625,14 @@ struct CmdFlakeCheck : FlakeCommand checkTemplate(name, vOutput, pos); else if (name == "templates") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) checkTemplate(fmt("%s.%s", name, state->symbols[attr.name]), *attr.value, attr.pos); } else if (name == "defaultBundler") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); @@ -655,11 +643,11 @@ struct CmdFlakeCheck : FlakeCommand } else if (name == "bundlers") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); - state->forceAttrs(*attr.value, attr.pos); + state->forceAttrs(*attr.value, attr.pos, ""); for (auto & attr2 : *attr.value->attrs) { checkBundler( fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), @@ -668,6 +656,19 @@ struct CmdFlakeCheck : FlakeCommand } } + else if ( + name == "lib" + || name == "darwinConfigurations" + || name == "darwinModules" + || name == "flakeModule" + || name == "flakeModules" + || name == "herculesCI" + || name == "homeConfigurations" + || name == "nixopsConfigurations" + ) + // Known but unchecked community attribute + ; + else warn("unknown flake output '%s'", name); @@ -966,6 +967,7 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun struct CmdFlakeShow : FlakeCommand, MixJSON { bool showLegacy = false; + bool showAllSystems = false; CmdFlakeShow() { @@ -974,6 +976,11 @@ struct CmdFlakeShow : FlakeCommand, MixJSON .description = "Show the contents of the `legacyPackages` output.", .handler = {&showLegacy, true} }); + addFlag({ + .longName = "all-systems", + .description = "Show the contents of outputs for all systems.", + .handler = {&showAllSystems, true} + }); } std::string description() override @@ -994,6 +1001,62 @@ struct CmdFlakeShow : FlakeCommand, MixJSON auto state = getEvalState(); auto flake = std::make_shared<LockedFlake>(lockFlake()); + auto localSystem = std::string(settings.thisSystem.get()); + + std::function<bool( + eval_cache::AttrCursor & visitor, + const std::vector<Symbol> &attrPath, + const Symbol &attr)> hasContent; + + // For frameworks it's important that structures are as lazy as possible + // to prevent infinite recursions, performance issues and errors that + // aren't related to the thing to evaluate. As a consequence, they have + // to emit more attributes than strictly (sic) necessary. + // However, these attributes with empty values are not useful to the user + // so we omit them. + hasContent = [&]( + eval_cache::AttrCursor & visitor, + const std::vector<Symbol> &attrPath, + const Symbol &attr) -> bool + { + auto attrPath2(attrPath); + attrPath2.push_back(attr); + auto attrPathS = state->symbols.resolve(attrPath2); + const auto & attrName = state->symbols[attr]; + + auto visitor2 = visitor.getAttr(attrName); + + if ((attrPathS[0] == "apps" + || attrPathS[0] == "checks" + || attrPathS[0] == "devShells" + || attrPathS[0] == "legacyPackages" + || attrPathS[0] == "packages") + && (attrPathS.size() == 1 || attrPathS.size() == 2)) { + for (const auto &subAttr : visitor2->getAttrs()) { + if (hasContent(*visitor2, attrPath2, subAttr)) { + return true; + } + } + return false; + } + + if ((attrPathS.size() == 1) + && (attrPathS[0] == "formatter" + || attrPathS[0] == "nixosConfigurations" + || attrPathS[0] == "nixosModules" + || attrPathS[0] == "overlays" + )) { + for (const auto &subAttr : visitor2->getAttrs()) { + if (hasContent(*visitor2, attrPath2, subAttr)) { + return true; + } + } + return false; + } + + // If we don't recognize it, it's probably content + return true; + }; std::function<nlohmann::json( eval_cache::AttrCursor & visitor, @@ -1020,7 +1083,12 @@ struct CmdFlakeShow : FlakeCommand, MixJSON { if (!json) logger->cout("%s", headerPrefix); - auto attrs = visitor.getAttrs(); + std::vector<Symbol> attrs; + for (const auto &attr : visitor.getAttrs()) { + if (hasContent(visitor, attrPath, attr)) + attrs.push_back(attr); + } + for (const auto & [i, attr] : enumerate(attrs)) { const auto & attrName = state->symbols[attr]; bool last = i + 1 == attrs.size(); @@ -1084,10 +1152,18 @@ struct CmdFlakeShow : FlakeCommand, MixJSON || (attrPath.size() == 3 && (attrPathS[0] == "checks" || attrPathS[0] == "packages" || attrPathS[0] == "devShells")) ) { - if (visitor.isDerivation()) - showDerivation(); - else - throw Error("expected a derivation"); + if (!showAllSystems && std::string(attrPathS[1]) != localSystem) { + if (!json) + logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)", headerPrefix)); + else { + logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPathS))); + } + } else { + if (visitor.isDerivation()) + showDerivation(); + else + throw Error("expected a derivation"); + } } else if (attrPath.size() > 0 && attrPathS[0] == "hydraJobs") { @@ -1106,6 +1182,12 @@ struct CmdFlakeShow : FlakeCommand, MixJSON else { logger->warn(fmt("%s omitted (use '--legacy' to show)", concatStringsSep(".", attrPathS))); } + } else if (!showAllSystems && std::string(attrPathS[1]) != localSystem) { + if (!json) + logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)", headerPrefix)); + else { + logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPathS))); + } } else { if (visitor.isDerivation()) showDerivation(); |