aboutsummaryrefslogtreecommitdiff
path: root/src/nix
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2019-05-31 09:57:04 +0200
committerGitHub <noreply@github.com>2019-05-31 09:57:04 +0200
commit65e88694c27a2b1de583b9e6584287fcba0b75b4 (patch)
treedcac4ef693b8eeee60b130bceac8452946c3ef86 /src/nix
parent6636808e9000241face21141a43de1ab6c9aa494 (diff)
parent49436bdbb77f32ffec2035e836add04f98be49e3 (diff)
Merge pull request #2903 from NixOS/check-flake
Add "nix flake check"
Diffstat (limited to 'src/nix')
-rw-r--r--src/nix/command.hh5
-rw-r--r--src/nix/flake.cc146
-rw-r--r--src/nix/installables.cc26
3 files changed, 163 insertions, 14 deletions
diff --git a/src/nix/command.hh b/src/nix/command.hh
index a841b879a..26c308331 100644
--- a/src/nix/command.hh
+++ b/src/nix/command.hh
@@ -12,7 +12,10 @@ struct Value;
class Bindings;
class EvalState;
class Store;
+
+namespace flake {
enum HandleLockFile : unsigned int;
+}
/* A command that require a Nix store. */
struct StoreCommand : virtual Command
@@ -71,7 +74,7 @@ struct MixFlakeOptions : virtual Args
MixFlakeOptions();
- HandleLockFile getLockFileMode();
+ flake::HandleLockFile getLockFileMode();
};
struct SourceExprCommand : virtual Args, EvalCommand, MixFlakeOptions
diff --git a/src/nix/flake.cc b/src/nix/flake.cc
index 7836f0cfe..872ec2849 100644
--- a/src/nix/flake.cc
+++ b/src/nix/flake.cc
@@ -3,13 +3,17 @@
#include "shared.hh"
#include "progress-bar.hh"
#include "eval.hh"
+#include "eval-inline.hh"
#include "primops/flake.hh"
+#include "get-drvs.hh"
+#include "store-api.hh"
#include <nlohmann/json.hpp>
#include <queue>
#include <iomanip>
using namespace nix;
+using namespace nix::flake;
class FlakeCommand : virtual Args, public EvalCommand, public MixFlakeOptions
{
@@ -33,12 +37,12 @@ public:
Flake getFlake()
{
auto evalState = getEvalState();
- return nix::getFlake(*evalState, getFlakeRef(), useRegistries);
+ return flake::getFlake(*evalState, getFlakeRef(), useRegistries);
}
ResolvedFlake resolveFlake()
{
- return nix::resolveFlake(*getEvalState(), getFlakeRef(), getLockFileMode());
+ return flake::resolveFlake(*getEvalState(), getFlakeRef(), getLockFileMode());
}
};
@@ -195,6 +199,19 @@ struct CmdFlakeUpdate : FlakeCommand
}
};
+static void enumerateProvides(EvalState & state, Value & vFlake,
+ std::function<void(const std::string & name, Value & vProvide)> callback)
+{
+ state.forceAttrs(vFlake);
+
+ auto vProvides = (*vFlake.attrs->get(state.symbols.create("provides")))->value;
+
+ state.forceAttrs(*vProvides);
+
+ for (auto & attr : *vProvides->attrs)
+ callback(attr.name, *attr.value);
+}
+
struct CmdFlakeInfo : FlakeCommand, MixJSON
{
std::string name() override
@@ -207,19 +224,133 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON
return "list info about a given flake";
}
- CmdFlakeInfo () { }
-
void run(nix::ref<nix::Store> store) override
{
auto flake = getFlake();
stopProgressBar();
- if (json)
- std::cout << flakeToJson(flake).dump() << std::endl;
- else
+
+ if (json) {
+ auto json = flakeToJson(flake);
+
+ auto state = getEvalState();
+
+ auto vFlake = state->allocValue();
+ flake::callFlake(*state, flake, *vFlake);
+
+ auto provides = nlohmann::json::object();
+
+ enumerateProvides(*state,
+ *vFlake,
+ [&](const std::string & name, Value & vProvide) {
+ auto provide = nlohmann::json::object();
+
+ if (name == "checks" || name == "packages") {
+ state->forceAttrs(vProvide);
+ for (auto & aCheck : *vProvide.attrs)
+ provide[aCheck.name] = nlohmann::json::object();
+ }
+
+ provides[name] = provide;
+ });
+
+ json["provides"] = std::move(provides);
+
+ std::cout << json.dump() << std::endl;
+ } else
printFlakeInfo(flake);
}
};
+struct CmdFlakeCheck : FlakeCommand, MixJSON
+{
+ bool build = true;
+
+ CmdFlakeCheck()
+ {
+ mkFlag()
+ .longName("no-build")
+ .description("do not build checks")
+ .set(&build, false);
+ }
+
+ std::string name() override
+ {
+ return "check";
+ }
+
+ std::string description() override
+ {
+ return "check whether the flake evaluates and run its tests";
+ }
+
+ void run(nix::ref<nix::Store> store) override
+ {
+ settings.readOnlyMode = !build;
+
+ auto state = getEvalState();
+ auto flake = resolveFlake();
+
+ auto checkDerivation = [&](const std::string & attrPath, Value & v) {
+ try {
+ auto drvInfo = getDerivation(*state, v, false);
+ if (!drvInfo)
+ throw Error("flake attribute '%s' is not a derivation", attrPath);
+ // FIXME: check meta attributes
+ return drvInfo->queryDrvPath();
+ } catch (Error & e) {
+ e.addPrefix(fmt("while checking flake attribute '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath));
+ throw;
+ }
+ };
+
+ PathSet drvPaths;
+
+ {
+ Activity act(*logger, lvlInfo, actUnknown, "evaluating flake");
+
+ auto vFlake = state->allocValue();
+ flake::callFlake(*state, flake, *vFlake);
+
+ enumerateProvides(*state,
+ *vFlake,
+ [&](const std::string & name, Value & vProvide) {
+ Activity act(*logger, lvlChatty, actUnknown,
+ fmt("checking flake output '%s'", name));
+
+ try {
+ state->forceValue(vProvide);
+
+ if (name == "checks") {
+ state->forceAttrs(vProvide);
+ for (auto & aCheck : *vProvide.attrs)
+ drvPaths.insert(checkDerivation(
+ name + "." + (std::string) aCheck.name, *aCheck.value));
+ }
+
+ else if (name == "packages") {
+ state->forceAttrs(vProvide);
+ for (auto & aCheck : *vProvide.attrs)
+ checkDerivation(
+ name + "." + (std::string) aCheck.name, *aCheck.value);
+ }
+
+ else if (name == "defaultPackage" || name == "devShell")
+ checkDerivation(name, vProvide);
+
+ } catch (Error & e) {
+ e.addPrefix(fmt("while checking flake output '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", name));
+ throw;
+ }
+ });
+ }
+
+ if (build) {
+ Activity act(*logger, lvlInfo, actUnknown, "running flake checks");
+ store->buildPaths(drvPaths);
+ }
+ }
+};
+
struct CmdFlakeAdd : MixEvalArgs, Command
{
FlakeUri alias;
@@ -386,6 +517,7 @@ struct CmdFlake : virtual MultiCommand, virtual Command
: MultiCommand({make_ref<CmdFlakeList>()
, make_ref<CmdFlakeUpdate>()
, make_ref<CmdFlakeInfo>()
+ , make_ref<CmdFlakeCheck>()
, make_ref<CmdFlakeDeps>()
, make_ref<CmdFlakeAdd>()
, make_ref<CmdFlakeRemove>()
diff --git a/src/nix/installables.cc b/src/nix/installables.cc
index 4f9161666..ea12cd79c 100644
--- a/src/nix/installables.cc
+++ b/src/nix/installables.cc
@@ -32,8 +32,9 @@ MixFlakeOptions::MixFlakeOptions()
.set(&useRegistries, false);
}
-HandleLockFile MixFlakeOptions::getLockFileMode()
+flake::HandleLockFile MixFlakeOptions::getLockFileMode()
{
+ using namespace flake;
return
useRegistries
? recreateLockFile
@@ -163,18 +164,20 @@ struct InstallableAttrPath : InstallableValue
}
};
-void makeFlakeClosureGCRoot(Store & store, const FlakeRef & origFlakeRef, const ResolvedFlake & resFlake)
+void makeFlakeClosureGCRoot(Store & store,
+ const FlakeRef & origFlakeRef,
+ const flake::ResolvedFlake & resFlake)
{
if (std::get_if<FlakeRef::IsPath>(&origFlakeRef.data)) return;
/* Get the store paths of all non-local flakes. */
PathSet closure;
- std::queue<std::reference_wrapper<const ResolvedFlake>> queue;
+ std::queue<std::reference_wrapper<const flake::ResolvedFlake>> queue;
queue.push(resFlake);
while (!queue.empty()) {
- const ResolvedFlake & flake = queue.front();
+ const flake::ResolvedFlake & flake = queue.front();
queue.pop();
if (!std::get_if<FlakeRef::IsPath>(&flake.flake.sourceInfo.resolvedRef.data))
closure.insert(flake.flake.sourceInfo.storePath);
@@ -233,9 +236,9 @@ struct InstallableFlake : InstallableValue
auto emptyArgs = state.allocBindings(0);
- // As a convenience, look for the attribute in
- // 'provides.packages'.
if (searchPackages) {
+ // As a convenience, look for the attribute in
+ // 'provides.packages'.
if (auto aPackages = *vProvides->attrs->get(state.symbols.create("packages"))) {
try {
auto * v = findAlongAttrPath(state, *attrPaths.begin(), *emptyArgs, *aPackages->value);
@@ -244,6 +247,17 @@ struct InstallableFlake : InstallableValue
} catch (AttrPathNotFound & e) {
}
}
+
+ // As a temporary hack until Nixpkgs is properly converted
+ // to provide a clean 'packages' set, look in 'legacyPackages'.
+ if (auto aPackages = *vProvides->attrs->get(state.symbols.create("legacyPackages"))) {
+ try {
+ auto * v = findAlongAttrPath(state, *attrPaths.begin(), *emptyArgs, *aPackages->value);
+ state.forceValue(*v);
+ return v;
+ } catch (AttrPathNotFound & e) {
+ }
+ }
}
// Otherwise, look for it in 'provides'.