aboutsummaryrefslogtreecommitdiff
path: root/src/nix/flake.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/nix/flake.cc')
-rw-r--r--src/nix/flake.cc100
1 files changed, 75 insertions, 25 deletions
diff --git a/src/nix/flake.cc b/src/nix/flake.cc
index 7d7ada707..47a380238 100644
--- a/src/nix/flake.cc
+++ b/src/nix/flake.cc
@@ -13,6 +13,7 @@
#include "registry.hh"
#include "json.hh"
#include "eval-cache.hh"
+#include "markdown.hh"
#include <nlohmann/json.hpp>
#include <queue>
@@ -124,12 +125,13 @@ struct CmdFlakeLock : FlakeCommand
static void enumerateOutputs(EvalState & state, Value & vFlake,
std::function<void(const std::string & name, Value & vProvide, const Pos & pos)> callback)
{
- state.forceAttrs(vFlake);
+ auto pos = vFlake.determinePos(noPos);
+ state.forceAttrs(vFlake, pos);
auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs"));
assert(aOutputs);
- state.forceAttrs(*aOutputs->value);
+ state.forceAttrs(*aOutputs->value, pos);
auto sHydraJobs = state.symbols.create("hydraJobs");
@@ -252,6 +254,14 @@ struct CmdFlakeInfo : CmdFlakeMetadata
}
};
+static bool argHasName(std::string_view arg, std::string_view expected)
+{
+ return
+ arg == expected
+ || arg == "_"
+ || (hasPrefix(arg, "_") && arg.substr(1) == expected);
+}
+
struct CmdFlakeCheck : FlakeCommand
{
bool build = true;
@@ -317,7 +327,7 @@ struct CmdFlakeCheck : FlakeCommand
if (!drvInfo)
throw Error("flake attribute '%s' is not a derivation", attrPath);
// FIXME: check meta attributes
- return std::make_optional(store->parseStorePath(drvInfo->queryDrvPath()));
+ return drvInfo->queryDrvPath();
} catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the derivation '%s'", attrPath));
reportError(e);
@@ -346,10 +356,14 @@ struct CmdFlakeCheck : FlakeCommand
auto checkOverlay = [&](const std::string & attrPath, Value & v, const Pos & pos) {
try {
state->forceValue(v, pos);
- if (!v.isLambda() || v.lambda.fun->matchAttrs || std::string(v.lambda.fun->arg) != "final")
+ if (!v.isLambda()
+ || v.lambda.fun->hasFormals()
+ || !argHasName(v.lambda.fun->arg, "final"))
throw Error("overlay does not take an argument named 'final'");
auto body = dynamic_cast<ExprLambda *>(v.lambda.fun->body);
- if (!body || body->matchAttrs || std::string(body->arg) != "prev")
+ if (!body
+ || body->hasFormals()
+ || !argHasName(body->arg, "prev"))
throw Error("overlay does not take an argument named 'prev'");
// FIXME: if we have a 'nixpkgs' input, use it to
// evaluate the overlay.
@@ -363,7 +377,7 @@ struct CmdFlakeCheck : FlakeCommand
try {
state->forceValue(v, pos);
if (v.isLambda()) {
- if (!v.lambda.fun->matchAttrs || !v.lambda.fun->formals->ellipsis)
+ 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)
@@ -463,10 +477,7 @@ struct CmdFlakeCheck : FlakeCommand
state->forceValue(v, pos);
if (!v.isLambda())
throw Error("bundler must be a function");
- if (!v.lambda.fun->formals ||
- !v.lambda.fun->formals->argNames.count(state->symbols.create("program")) ||
- !v.lambda.fun->formals->argNames.count(state->symbols.create("system")))
- throw Error("bundler must take formal arguments 'program' and 'system'");
+ // TODO: check types of inputs/outputs?
} catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the template '%s'", attrPath));
reportError(e);
@@ -490,6 +501,17 @@ struct CmdFlakeCheck : FlakeCommand
state->forceValue(vOutput, pos);
+ std::string_view replacement =
+ name == "defaultPackage" ? "packages.<system>.default" :
+ name == "defaultApps" ? "apps.<system>.default" :
+ name == "defaultTemplate" ? "templates.default" :
+ name == "defaultBundler" ? "bundlers.<system>.default" :
+ name == "overlay" ? "overlays.default" :
+ name == "devShell" ? "devShells.<system>.default" :
+ "";
+ if (replacement != "")
+ warn("flake output attribute '%s' is deprecated; use '%s' instead", name, replacement);
+
if (name == "checks") {
state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) {
@@ -597,14 +619,27 @@ struct CmdFlakeCheck : FlakeCommand
*attr.value, *attr.pos);
}
- else if (name == "defaultBundler")
- checkBundler(name, vOutput, pos);
+ else if (name == "defaultBundler") {
+ state->forceAttrs(vOutput, pos);
+ for (auto & attr : *vOutput.attrs) {
+ checkSystemName(attr.name, *attr.pos);
+ checkBundler(
+ fmt("%s.%s", name, attr.name),
+ *attr.value, *attr.pos);
+ }
+ }
else if (name == "bundlers") {
state->forceAttrs(vOutput, pos);
- for (auto & attr : *vOutput.attrs)
- checkBundler(fmt("%s.%s", name, attr.name),
- *attr.value, *attr.pos);
+ for (auto & attr : *vOutput.attrs) {
+ checkSystemName(attr.name, *attr.pos);
+ state->forceAttrs(*attr.value, *attr.pos);
+ for (auto & attr2 : *attr.value->attrs) {
+ checkBundler(
+ fmt("%s.%s.%s", name, attr.name, attr2.name),
+ *attr2.value, *attr2.pos);
+ }
+ }
}
else
@@ -626,12 +661,14 @@ struct CmdFlakeCheck : FlakeCommand
}
};
+static Strings defaultTemplateAttrPathsPrefixes{"templates."};
+static Strings defaultTemplateAttrPaths = {"templates.default", "defaultTemplate"};
+
struct CmdFlakeInitCommon : virtual Args, EvalCommand
{
std::string templateUrl = "templates";
Path destDir;
- const Strings attrsPathPrefixes{"templates."};
const LockFlags lockFlags{ .writeLockFile = false };
CmdFlakeInitCommon()
@@ -646,8 +683,8 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
completeFlakeRefWithFragment(
getEvalState(),
lockFlags,
- attrsPathPrefixes,
- {"defaultTemplate"},
+ defaultTemplateAttrPathsPrefixes,
+ defaultTemplateAttrPaths,
prefix);
}}
});
@@ -662,15 +699,21 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment(templateUrl, absPath("."));
auto installable = InstallableFlake(nullptr,
- evalState, std::move(templateFlakeRef),
- Strings{templateName == "" ? "defaultTemplate" : templateName},
- Strings(attrsPathPrefixes), lockFlags);
+ evalState, std::move(templateFlakeRef), templateName,
+ defaultTemplateAttrPaths,
+ defaultTemplateAttrPathsPrefixes,
+ lockFlags);
auto [cursor, attrPath] = installable.getCursor(*evalState);
- auto templateDir = cursor->getAttr("path")->getString();
+ auto templateDirAttr = cursor->getAttr("path");
+ auto templateDir = templateDirAttr->getString();
- assert(store->isInStore(templateDir));
+ if (!store->isInStore(templateDir))
+ throw TypeError(
+ "'%s' was not found in the Nix store\n"
+ "If you've set '%s' to a string, try using a path instead.",
+ templateDir, templateDirAttr->getAttrPathStr());
std::vector<Path> files;
@@ -705,6 +748,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
else
throw Error("file '%s' has unsupported type", from2);
files.push_back(to2);
+ notice("wrote: %s", to2);
}
};
@@ -715,6 +759,11 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
for (auto & s : files) args.push_back(s);
runProgram("git", true, args);
}
+ auto welcomeText = cursor->maybeGetAttr("welcomeText");
+ if (welcomeText) {
+ notice("\n");
+ notice(renderMarkdownToTerminal(welcomeText->getString()));
+ }
}
};
@@ -1040,7 +1089,8 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
(attrPath.size() == 1 && attrPath[0] == "overlay")
|| (attrPath.size() == 2 && attrPath[0] == "overlays") ? std::make_pair("nixpkgs-overlay", "Nixpkgs overlay") :
attrPath.size() == 2 && attrPath[0] == "nixosConfigurations" ? std::make_pair("nixos-configuration", "NixOS configuration") :
- attrPath.size() == 2 && attrPath[0] == "nixosModules" ? std::make_pair("nixos-module", "NixOS module") :
+ (attrPath.size() == 1 && attrPath[0] == "nixosModule")
+ || (attrPath.size() == 2 && attrPath[0] == "nixosModules") ? std::make_pair("nixos-module", "NixOS module") :
std::make_pair("unknown", "unknown");
if (json) {
j.emplace("type", type);
@@ -1138,7 +1188,7 @@ struct CmdFlake : NixMultiCommand
{
if (!command)
throw UsageError("'nix flake' requires a sub-command.");
- settings.requireExperimentalFeature("flakes");
+ settings.requireExperimentalFeature(Xp::Flakes);
command->second->prepare();
command->second->run();
}