about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2017-04-25T09·20+0200
committerEelco Dolstra <edolstra@gmail.com>2017-04-25T09·20+0200
commitbcecc990071fd36bb88c8fd29cb009ed4c04d6a2 (patch)
treec3f2fa86b2d751418efe7727bc345d0557c3786c /src
parent1bb87c0487ba2a10f20c07dfd828b5d043249e31 (diff)
Restructure installables handling in the "nix" command
Diffstat (limited to 'src')
-rw-r--r--src/libexpr/value.hh8
-rw-r--r--src/nix/build.cc19
-rw-r--r--src/nix/command.cc7
-rw-r--r--src/nix/command.hh4
-rw-r--r--src/nix/installables.cc233
-rw-r--r--src/nix/installables.hh55
-rw-r--r--src/nix/log.cc35
-rw-r--r--src/nix/path-info.cc1
-rw-r--r--src/nix/run.cc19
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)