diff options
author | Eelco Dolstra <edolstra@gmail.com> | 2017-04-25T09·20+0200 |
---|---|---|
committer | Eelco Dolstra <edolstra@gmail.com> | 2017-04-25T09·20+0200 |
commit | bcecc990071fd36bb88c8fd29cb009ed4c04d6a2 (patch) | |
tree | c3f2fa86b2d751418efe7727bc345d0557c3786c | |
parent | 1bb87c0487ba2a10f20c07dfd828b5d043249e31 (diff) |
Restructure installables handling in the "nix" command
-rw-r--r-- | src/libexpr/value.hh | 8 | ||||
-rw-r--r-- | src/nix/build.cc | 19 | ||||
-rw-r--r-- | src/nix/command.cc | 7 | ||||
-rw-r--r-- | src/nix/command.hh | 4 | ||||
-rw-r--r-- | src/nix/installables.cc | 233 | ||||
-rw-r--r-- | src/nix/installables.hh | 55 | ||||
-rw-r--r-- | src/nix/log.cc | 35 | ||||
-rw-r--r-- | src/nix/path-info.cc | 1 | ||||
-rw-r--r-- | src/nix/run.cc | 19 |
9 files changed, 257 insertions, 124 deletions
diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 802e8ed2ee75..9df516f062ef 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -220,6 +220,14 @@ static inline void mkApp(Value & v, Value & left, Value & right) } +static inline void mkPrimOpApp(Value & v, Value & left, Value & right) +{ + v.type = tPrimOpApp; + v.app.left = &left; + v.app.right = &right; +} + + static inline void mkStringNoCopy(Value & v, const char * s) { v.type = tString; diff --git a/src/nix/build.cc b/src/nix/build.cc index 812464d7582b..0a34c68f8819 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -6,7 +6,7 @@ using namespace nix; -struct CmdBuild : StoreCommand, MixDryRun, MixInstallables +struct CmdBuild : MixDryRun, MixInstallables { CmdBuild() { @@ -24,22 +24,9 @@ struct CmdBuild : StoreCommand, MixDryRun, MixInstallables void run(ref<Store> store) override { - auto elems = evalInstallables(store); + auto paths = buildInstallables(store, dryRun); - PathSet pathsToBuild; - - for (auto & elem : elems) { - if (elem.isDrv) - pathsToBuild.insert(elem.drvPath); - else - pathsToBuild.insert(elem.outPaths.begin(), elem.outPaths.end()); - } - - printMissing(store, pathsToBuild); - - if (dryRun) return; - - store->buildPaths(pathsToBuild); + printInfo("build result: %s", showPaths(paths)); } }; diff --git a/src/nix/command.cc b/src/nix/command.cc index a1b2c120a5d9..4034de96c162 100644 --- a/src/nix/command.cc +++ b/src/nix/command.cc @@ -79,6 +79,13 @@ StoreCommand::StoreCommand() mkFlag(0, "store", "store-uri", "URI of the Nix store to use", &storeUri); } +ref<Store> StoreCommand::getStore() +{ + if (!_store) + _store = createStore(); + return ref<Store>(_store); +} + ref<Store> StoreCommand::createStore() { return openStore(storeUri); diff --git a/src/nix/command.hh b/src/nix/command.hh index fa6c21abf8ad..bb667ee325ca 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -33,8 +33,12 @@ struct StoreCommand : virtual Command std::string storeUri; StoreCommand(); void run() override; + ref<Store> getStore(); virtual ref<Store> createStore(); virtual void run(ref<Store>) = 0; + +private: + std::shared_ptr<Store> _store; }; /* A command that operates on zero or more store paths. */ diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 8341bbc5a3a4..70007d62a290 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -6,16 +6,21 @@ #include "get-drvs.hh" #include "installables.hh" #include "store-api.hh" +#include "shared.hh" + +#include <regex> namespace nix { -Value * MixInstallables::buildSourceExpr(EvalState & state) +Value * MixInstallables::getSourceExpr(EvalState & state) { - Value * vRoot = state.allocValue(); + if (vSourceExpr) return vSourceExpr; + + vSourceExpr = state.allocValue(); if (file != "") { Expr * e = state.parseExprFromFile(resolveExprPath(lookupFileArg(state, file))); - state.eval(e, *vRoot); + state.eval(e, *vSourceExpr); } else { @@ -24,7 +29,7 @@ Value * MixInstallables::buildSourceExpr(EvalState & state) auto searchPath = state.getSearchPath(); - state.mkAttrs(*vRoot, searchPath.size()); + state.mkAttrs(*vSourceExpr, searchPath.size()); std::unordered_set<std::string> seen; @@ -32,76 +37,208 @@ Value * MixInstallables::buildSourceExpr(EvalState & state) if (i.first == "") continue; if (seen.count(i.first)) continue; seen.insert(i.first); - if (!pathExists(i.second)) continue; - mkApp(*state.allocAttr(*vRoot, state.symbols.create(i.first)), +#if 0 + auto res = state.resolveSearchPathElem(i); + if (!res.first) continue; + if (!pathExists(res.second)) continue; + mkApp(*state.allocAttr(*vSourceExpr, state.symbols.create(i.first)), state.getBuiltin("import"), - mkString(*state.allocValue(), i.second)); + mkString(*state.allocValue(), res.second)); +#endif + Value * v1 = state.allocValue(); + mkPrimOpApp(*v1, state.getBuiltin("findFile"), state.getBuiltin("nixPath")); + Value * v2 = state.allocValue(); + mkApp(*v2, *v1, mkString(*state.allocValue(), i.first)); + mkApp(*state.allocAttr(*vSourceExpr, state.symbols.create(i.first)), + state.getBuiltin("import"), *v2); } - vRoot->attrs->sort(); + vSourceExpr->attrs->sort(); } - return vRoot; + return vSourceExpr; } -UserEnvElems MixInstallables::evalInstallables(ref<Store> store) +struct InstallableStoreDrv : Installable +{ + Path storePath; + + InstallableStoreDrv(const Path & storePath) : storePath(storePath) { } + + std::string what() override { return storePath; } + + PathSet toBuildable() override + { + return {storePath}; + } +}; + +struct InstallableStorePath : Installable { - UserEnvElems res; + Path storePath; + + InstallableStorePath(const Path & storePath) : storePath(storePath) { } + + std::string what() override { return storePath; } + + PathSet toBuildable() override + { + return {storePath}; + } +}; + +struct InstallableExpr : Installable +{ + MixInstallables & installables; + std::string text; + + InstallableExpr(MixInstallables & installables, const std::string & text) + : installables(installables), text(text) { } + + std::string what() override { return text; } + + PathSet toBuildable() override + { + auto state = installables.getEvalState(); + + auto v = toValue(*state); + + // FIXME + std::map<string, string> autoArgs_; + Bindings & autoArgs(*evalAutoArgs(*state, autoArgs_)); + + DrvInfos drvs; + getDerivations(*state, *v, "", autoArgs, drvs, false); + + PathSet res; + + for (auto & i : drvs) + res.insert(i.queryDrvPath()); + + return res; + } + + Value * toValue(EvalState & state) override + { + auto v = state.allocValue(); + state.eval(state.parseExprFromString(text, absPath(".")), *v); + return v; + } +}; + +struct InstallableAttrPath : Installable +{ + MixInstallables & installables; + std::string attrPath; + + InstallableAttrPath(MixInstallables & installables, const std::string & attrPath) + : installables(installables), attrPath(attrPath) + { } + + std::string what() override { return attrPath; } + + PathSet toBuildable() override + { + auto state = installables.getEvalState(); + + auto v = toValue(*state); + + // FIXME + std::map<string, string> autoArgs_; + Bindings & autoArgs(*evalAutoArgs(*state, autoArgs_)); + + DrvInfos drvs; + getDerivations(*state, *v, "", autoArgs, drvs, false); + + PathSet res; + + for (auto & i : drvs) + res.insert(i.queryDrvPath()); + + return res; + } + + Value * toValue(EvalState & state) override + { + auto source = installables.getSourceExpr(state); + + // FIXME + std::map<string, string> autoArgs_; + Bindings & autoArgs(*evalAutoArgs(state, autoArgs_)); + + Value * v = findAlongAttrPath(state, attrPath, autoArgs, *source); + state.forceValue(*v); + + return v; + } +}; + +// FIXME: extend +std::string attrRegex = R"([A-Za-z_][A-Za-z0-9-_+]*)"; +static std::regex attrPathRegex(fmt(R"(%1%(\.%1%)*)", attrRegex)); + +std::vector<std::shared_ptr<Installable>> MixInstallables::parseInstallables(ref<Store> store, Strings installables) +{ + std::vector<std::shared_ptr<Installable>> result; for (auto & installable : installables) { if (std::string(installable, 0, 1) == "/") { if (store->isStorePath(installable)) { + if (isDerivation(installable)) + result.push_back(std::make_shared<InstallableStoreDrv>(installable)); + else + result.push_back(std::make_shared<InstallableStorePath>(installable)); + } - if (isDerivation(installable)) { - UserEnvElem elem; - // FIXME: handle empty case, drop version - elem.attrPath = {storePathToName(installable)}; - elem.isDrv = true; - elem.drvPath = installable; - res.push_back(elem); - } - - else { - UserEnvElem elem; - // FIXME: handle empty case, drop version - elem.attrPath = {storePathToName(installable)}; - elem.isDrv = false; - elem.outPaths = {installable}; - res.push_back(elem); - } + else { + result.push_back(std::make_shared<InstallableStorePath>( + store->toStorePath(store->followLinksToStore(installable)))); } - else - throw UsageError(format("don't know what to do with ‘%1%’") % installable); } - else { - - EvalState state({}, store); + else if (installable.compare(0, 1, "(") == 0) + result.push_back(std::make_shared<InstallableExpr>(*this, installable)); - auto vRoot = buildSourceExpr(state); + else if (std::regex_match(installable, attrPathRegex)) + result.push_back(std::make_shared<InstallableAttrPath>(*this, installable)); - std::map<string, string> autoArgs_; - Bindings & autoArgs(*evalAutoArgs(state, autoArgs_)); + else + throw UsageError("don't know what to do with argument ‘%s’", installable); + } - Value & v(*findAlongAttrPath(state, installable, autoArgs, *vRoot)); - state.forceValue(v); + return result; +} - DrvInfos drvs; - getDerivations(state, v, "", autoArgs, drvs, false); +PathSet MixInstallables::buildInstallables(ref<Store> store, bool dryRun) +{ + PathSet buildables; - for (auto & i : drvs) { - UserEnvElem elem; - elem.isDrv = true; - elem.drvPath = i.queryDrvPath(); - res.push_back(elem); - } - } + for (auto & i : installables) { + auto b = i->toBuildable(); + buildables.insert(b.begin(), b.end()); } - return res; + printMissing(store, buildables); + + if (!dryRun) + store->buildPaths(buildables); + + return buildables; +} + +ref<EvalState> MixInstallables::getEvalState() +{ + if (!evalState) + evalState = std::make_shared<EvalState>(Strings{}, getStore()); + return ref<EvalState>(evalState); +} + +void MixInstallables::prepare() +{ + installables = parseInstallables(getStore(), _installables); } } diff --git a/src/nix/installables.hh b/src/nix/installables.hh index a58f7dc59bb4..5f0b0a292242 100644 --- a/src/nix/installables.hh +++ b/src/nix/installables.hh @@ -1,48 +1,61 @@ #pragma once #include "args.hh" +#include "command.hh" namespace nix { -struct UserEnvElem -{ - Strings attrPath; +struct Value; +class EvalState; +class Expr; - // FIXME: should use boost::variant or so. - bool isDrv; +struct Installable +{ + virtual std::string what() = 0; - // Derivation case: - Path drvPath; - StringSet outputNames; + virtual PathSet toBuildable() + { + throw Error("argument ‘%s’ cannot be built", what()); + } - // Non-derivation case: - PathSet outPaths; + virtual Value * toValue(EvalState & state) + { + throw Error("argument ‘%s’ cannot be evaluated", what()); + } }; -typedef std::vector<UserEnvElem> UserEnvElems; - -struct Value; -class EvalState; - -struct MixInstallables : virtual Args +struct MixInstallables : virtual Args, StoreCommand { - Strings installables; + std::vector<std::shared_ptr<Installable>> installables; Path file; MixInstallables() { mkFlag('f', "file", "file", "evaluate FILE rather than the default", &file); - expectArgs("installables", &installables); + expectArgs("installables", &_installables); } - UserEnvElems evalInstallables(ref<Store> store); - /* Return a value representing the Nix expression from which we are installing. This is either the file specified by ‘--file’, or an attribute set constructed from $NIX_PATH, e.g. ‘{ nixpkgs = import ...; bla = import ...; }’. */ - Value * buildSourceExpr(EvalState & state); + Value * getSourceExpr(EvalState & state); + + std::vector<std::shared_ptr<Installable>> parseInstallables(ref<Store> store, Strings installables); + + PathSet buildInstallables(ref<Store> store, bool dryRun); + + ref<EvalState> getEvalState(); + + void prepare() override; + +private: + + Strings _installables; + + std::shared_ptr<EvalState> evalState; + Value * vSourceExpr = 0; }; } diff --git a/src/nix/log.cc b/src/nix/log.cc index d8a3830e91c8..75f3c1ab0d63 100644 --- a/src/nix/log.cc +++ b/src/nix/log.cc @@ -6,7 +6,7 @@ using namespace nix; -struct CmdLog : StoreCommand, MixInstallables +struct CmdLog : MixInstallables { CmdLog() { @@ -24,32 +24,23 @@ struct CmdLog : StoreCommand, MixInstallables void run(ref<Store> store) override { - auto elems = evalInstallables(store); - - PathSet paths; - - for (auto & elem : elems) { - if (elem.isDrv) - paths.insert(elem.drvPath); - else - paths.insert(elem.outPaths.begin(), elem.outPaths.end()); - } - auto subs = getDefaultSubstituters(); subs.push_front(store); - for (auto & path : paths) { - bool found = false; - for (auto & sub : subs) { - auto log = sub->getBuildLog(path); - if (!log) continue; - std::cout << *log; - found = true; - break; + for (auto & inst : installables) { + for (auto & path : inst->toBuildable()) { + bool found = false; + for (auto & sub : subs) { + auto log = sub->getBuildLog(path); + if (!log) continue; + std::cout << *log; + found = true; + break; + } + if (!found) + throw Error("build log of path ‘%s’ is not available", path); } - if (!found) - throw Error("build log of path ‘%s’ is not available", path); } } }; diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc index 30b193798f69..f16209238610 100644 --- a/src/nix/path-info.cc +++ b/src/nix/path-info.cc @@ -99,7 +99,6 @@ struct CmdPathInfo : StorePathsCommand, MixJSON } } - } }; diff --git a/src/nix/run.cc b/src/nix/run.cc index a30031ad07b4..f3333b777805 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -13,7 +13,7 @@ using namespace nix; -struct CmdRun : StoreCommand, MixInstallables +struct CmdRun : MixInstallables { CmdRun() { @@ -31,20 +31,7 @@ struct CmdRun : StoreCommand, MixInstallables void run(ref<Store> store) override { - auto elems = evalInstallables(store); - - PathSet pathsToBuild; - - for (auto & elem : elems) { - if (elem.isDrv) - pathsToBuild.insert(elem.drvPath); - else - pathsToBuild.insert(elem.outPaths.begin(), elem.outPaths.end()); - } - - printMissing(store, pathsToBuild); - - store->buildPaths(pathsToBuild); + auto paths = buildInstallables(store, false); auto store2 = store.dynamic_pointer_cast<LocalStore>(); @@ -104,7 +91,7 @@ struct CmdRun : StoreCommand, MixInstallables } PathSet outPaths; - for (auto & path : pathsToBuild) + for (auto & path : paths) if (isDerivation(path)) { Derivation drv = store->derivationFromPath(path); for (auto & output : drv.outputs) |