about summary refs log tree commit diff
path: root/src/nix
diff options
context:
space:
mode:
Diffstat (limited to 'src/nix')
-rw-r--r--src/nix/build.cc4
-rw-r--r--src/nix/command.hh4
-rw-r--r--src/nix/copy.cc6
-rw-r--r--src/nix/installables.cc4
-rw-r--r--src/nix/local.mk2
-rw-r--r--src/nix/ls.cc20
-rw-r--r--src/nix/main.cc7
-rw-r--r--src/nix/ping-store.cc35
-rw-r--r--src/nix/progress-bar.cc93
-rw-r--r--src/nix/run.cc4
-rw-r--r--src/nix/upgrade-nix.cc131
11 files changed, 248 insertions, 62 deletions
diff --git a/src/nix/build.cc b/src/nix/build.cc
index f7c99f12dbbf..b329ac38ac2b 100644
--- a/src/nix/build.cc
+++ b/src/nix/build.cc
@@ -50,7 +50,9 @@ struct CmdBuild : MixDryRun, InstallablesCommand
 
     void run(ref<Store> store) override
     {
-        auto buildables = toBuildables(store, dryRun ? DryRun : Build, installables);
+        auto buildables = build(store, dryRun ? DryRun : Build, installables);
+
+        if (dryRun) return;
 
         for (size_t i = 0; i < buildables.size(); ++i) {
             auto & b(buildables[i]);
diff --git a/src/nix/command.hh b/src/nix/command.hh
index 6b34e3881e79..97a6fee7fd27 100644
--- a/src/nix/command.hh
+++ b/src/nix/command.hh
@@ -5,6 +5,8 @@
 
 namespace nix {
 
+extern std::string programPath;
+
 struct Value;
 class Bindings;
 class EvalState;
@@ -196,7 +198,7 @@ std::shared_ptr<Installable> parseInstallable(
     SourceExprCommand & cmd, ref<Store> store, const std::string & installable,
     bool useDefaultInstallables);
 
-Buildables toBuildables(ref<Store> store, RealiseMode mode,
+Buildables build(ref<Store> store, RealiseMode mode,
     std::vector<std::shared_ptr<Installable>> installables);
 
 PathSet toStorePaths(ref<Store> store, RealiseMode mode,
diff --git a/src/nix/copy.cc b/src/nix/copy.cc
index 2ddea9e70a6a..f29429c1ac49 100644
--- a/src/nix/copy.cc
+++ b/src/nix/copy.cc
@@ -57,15 +57,15 @@ struct CmdCopy : StorePathsCommand
         return {
             Example{
                 "To copy Firefox from the local store to a binary cache in file:///tmp/cache:",
-                "nix copy --to file:///tmp/cache -r $(type -p firefox)"
+                "nix copy --to file:///tmp/cache $(type -p firefox)"
             },
             Example{
                 "To copy the entire current NixOS system closure to another machine via SSH:",
-                "nix copy --to ssh://server -r /run/current-system"
+                "nix copy --to ssh://server /run/current-system"
             },
             Example{
                 "To copy a closure from another machine via SSH:",
-                "nix copy --from ssh://server -r /nix/store/a6cnl93nk1wxnq84brbbwr6hxw9gp2w9-blender-2.79-rc2"
+                "nix copy --from ssh://server /nix/store/a6cnl93nk1wxnq84brbbwr6hxw9gp2w9-blender-2.79-rc2"
             },
         };
     }
diff --git a/src/nix/installables.cc b/src/nix/installables.cc
index c3b06c22eba8..a3fdd8a2808d 100644
--- a/src/nix/installables.cc
+++ b/src/nix/installables.cc
@@ -253,7 +253,7 @@ std::shared_ptr<Installable> parseInstallable(
     return installables.front();
 }
 
-Buildables toBuildables(ref<Store> store, RealiseMode mode,
+Buildables build(ref<Store> store, RealiseMode mode,
     std::vector<std::shared_ptr<Installable>> installables)
 {
     if (mode != Build)
@@ -291,7 +291,7 @@ PathSet toStorePaths(ref<Store> store, RealiseMode mode,
 {
     PathSet outPaths;
 
-    for (auto & b : toBuildables(store, mode, installables))
+    for (auto & b : build(store, mode, installables))
         for (auto & output : b.outputs)
             outPaths.insert(output.second);
 
diff --git a/src/nix/local.mk b/src/nix/local.mk
index bddd53b168d3..f76da194467c 100644
--- a/src/nix/local.mk
+++ b/src/nix/local.mk
@@ -6,4 +6,6 @@ nix_SOURCES := $(wildcard $(d)/*.cc) $(wildcard src/linenoise/*.cpp)
 
 nix_LIBS = libexpr libmain libstore libutil libformat
 
+nix_LDFLAGS = -pthread
+
 $(eval $(call install-symlink, nix, $(bindir)/nix-hash))
diff --git a/src/nix/ls.cc b/src/nix/ls.cc
index 69620595d8ca..e99622faf472 100644
--- a/src/nix/ls.cc
+++ b/src/nix/ls.cc
@@ -90,6 +90,16 @@ struct CmdLsStore : StoreCommand, MixLs
         expectArg("path", &path);
     }
 
+    Examples examples() override
+    {
+        return {
+            Example{
+                "To list the contents of a store path in a binary cache:",
+                "nix ls-store --store https://cache.nixos.org/ -lR /nix/store/0i2jd68mp5g6h2sa5k9c85rb80sn8hi9-hello-2.10"
+            },
+        };
+    }
+
     std::string name() override
     {
         return "ls-store";
@@ -116,6 +126,16 @@ struct CmdLsNar : Command, MixLs
         expectArg("path", &path);
     }
 
+    Examples examples() override
+    {
+        return {
+            Example{
+                "To list a specific file in a NAR:",
+                "nix ls-nar -l hello.nar /bin/hello"
+            },
+        };
+    }
+
     std::string name() override
     {
         return "ls-nar";
diff --git a/src/nix/main.cc b/src/nix/main.cc
index 06bb8a1c3043..bb107ec7d3f6 100644
--- a/src/nix/main.cc
+++ b/src/nix/main.cc
@@ -16,6 +16,8 @@ void chrootHelper(int argc, char * * argv);
 
 namespace nix {
 
+std::string programPath;
+
 struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
 {
     NixArgs() : MultiCommand(*RegisterCommand::commands), MixCommonArgs("nix")
@@ -78,7 +80,8 @@ void mainWrapped(int argc, char * * argv)
     initNix();
     initGC();
 
-    string programName = baseNameOf(argv[0]);
+    programPath = argv[0];
+    string programName = baseNameOf(programPath);
 
     {
         auto legacy = (*RegisterLegacyCommand::commands)[programName];
@@ -89,6 +92,8 @@ void mainWrapped(int argc, char * * argv)
 
     args.parseCmdline(argvToStrings(argc, argv));
 
+    initPlugins();
+
     if (!args.command) args.showHelpAndExit();
 
     Finally f([]() { stopProgressBar(); });
diff --git a/src/nix/ping-store.cc b/src/nix/ping-store.cc
new file mode 100644
index 000000000000..310942574a2a
--- /dev/null
+++ b/src/nix/ping-store.cc
@@ -0,0 +1,35 @@
+#include "command.hh"
+#include "shared.hh"
+#include "store-api.hh"
+
+using namespace nix;
+
+struct CmdPingStore : StoreCommand
+{
+    std::string name() override
+    {
+        return "ping-store";
+    }
+
+    std::string description() override
+    {
+        return "test whether a store can be opened";
+    }
+
+    Examples examples() override
+    {
+        return {
+            Example{
+                "To test whether connecting to a remote Nix store via SSH works:",
+                "nix ping-store --store ssh://mac1"
+            },
+        };
+    }
+
+    void run(ref<Store> store) override
+    {
+        store->connect();
+    }
+};
+
+static RegisterCommand r1(make_ref<CmdPingStore>());
diff --git a/src/nix/progress-bar.cc b/src/nix/progress-bar.cc
index fb9955190b40..e6553c06f4ae 100644
--- a/src/nix/progress-bar.cc
+++ b/src/nix/progress-bar.cc
@@ -3,8 +3,9 @@
 #include "sync.hh"
 #include "store-api.hh"
 
-#include <map>
 #include <atomic>
+#include <map>
+#include <thread>
 
 namespace nix {
 
@@ -22,44 +23,6 @@ static uint64_t getI(const std::vector<Logger::Field> & fields, size_t n)
     return fields[n].i;
 }
 
-/* Truncate a string to 'width' printable characters. ANSI escape
-   sequences are copied but not included in the character count. Also,
-   tabs are expanded to spaces. */
-static std::string ansiTruncate(const std::string & s, int width)
-{
-    if (width <= 0) return s;
-
-    std::string t;
-    size_t w = 0;
-    auto i = s.begin();
-
-    while (w < (size_t) width && i != s.end()) {
-        if (*i == '\e') {
-            t += *i++;
-            if (i != s.end() && *i == '[') {
-                t += *i++;
-                while (i != s.end() && (*i < 0x40 || *i > 0x7e)) {
-                    t += *i++;
-                }
-                if (i != s.end()) t += *i++;
-            }
-        }
-
-        else if (*i == '\t') {
-            t += ' '; w++;
-            while (w < (size_t) width && w & 8) {
-                t += ' '; w++;
-            }
-        }
-
-        else {
-            t += *i++; w++;
-        }
-    }
-
-    return t;
-}
-
 class ProgressBar : public Logger
 {
 private:
@@ -101,15 +64,28 @@ private:
 
     Sync<State> state_;
 
+    std::thread updateThread;
+
+    std::condition_variable quitCV, updateCV;
+
 public:
 
     ProgressBar()
     {
+        updateThread = std::thread([&]() {
+            auto state(state_.lock());
+            while (state->active) {
+                state.wait(updateCV);
+                draw(*state);
+                state.wait_for(quitCV, std::chrono::milliseconds(50));
+            }
+        });
     }
 
     ~ProgressBar()
     {
         stop();
+        updateThread.join();
     }
 
     void stop()
@@ -121,6 +97,8 @@ public:
         writeToStderr("\r\e[K");
         if (status != "")
             writeToStderr("[" + status + "]\n");
+        updateCV.notify_one();
+        quitCV.notify_one();
     }
 
     void log(Verbosity lvl, const FormatOrString & fs) override
@@ -132,7 +110,7 @@ public:
     void log(State & state, Verbosity lvl, const std::string & s)
     {
         writeToStderr("\r\e[K" + s + ANSI_NORMAL "\n");
-        update(state);
+        draw(state);
     }
 
     void startActivity(ActivityId act, Verbosity lvl, ActivityType type,
@@ -167,7 +145,12 @@ public:
 
         if (type == actSubstitute) {
             auto name = storePathToName(getS(fields, 0));
-            i->s = fmt("fetching " ANSI_BOLD "%s" ANSI_NORMAL " from %s", name, getS(fields, 1));
+            auto sub = getS(fields, 1);
+            i->s = fmt(
+                hasPrefix(sub, "local")
+                ? "copying " ANSI_BOLD "%s" ANSI_NORMAL " from %s"
+                : "fetching " ANSI_BOLD "%s" ANSI_NORMAL " from %s",
+                name, sub);
         }
 
         if (type == actQueryPathInfo) {
@@ -180,7 +163,7 @@ public:
             || (type == actCopyPath && hasAncestor(*state, actSubstitute, parent)))
             i->visible = false;
 
-        update(*state);
+        update();
     }
 
     /* Check whether an activity has an ancestore with the specified
@@ -215,7 +198,7 @@ public:
             state->its.erase(i);
         }
 
-        update(*state);
+        update();
     }
 
     void result(ActivityId act, ResultType type, const std::vector<Field> & fields) override
@@ -225,7 +208,7 @@ public:
         if (type == resFileLinked) {
             state->filesLinked++;
             state->bytesLinked += getI(fields, 0);
-            update(*state);
+            update();
         }
 
         else if (type == resBuildLogLine) {
@@ -238,25 +221,25 @@ public:
                 info.lastLine = lastLine;
                 state->activities.emplace_back(info);
                 i->second = std::prev(state->activities.end());
-                update(*state);
+                update();
             }
         }
 
         else if (type == resUntrustedPath) {
             state->untrustedPaths++;
-            update(*state);
+            update();
         }
 
         else if (type == resCorruptedPath) {
             state->corruptedPaths++;
-            update(*state);
+            update();
         }
 
         else if (type == resSetPhase) {
             auto i = state->its.find(act);
             assert(i != state->its.end());
             i->second->phase = getS(fields, 0);
-            update(*state);
+            update();
         }
 
         else if (type == resProgress) {
@@ -267,7 +250,7 @@ public:
             actInfo.expected = getI(fields, 1);
             actInfo.running = getI(fields, 2);
             actInfo.failed = getI(fields, 3);
-            update(*state);
+            update();
         }
 
         else if (type == resSetExpected) {
@@ -279,17 +262,16 @@ public:
             state->activitiesByType[type].expected -= j;
             j = getI(fields, 1);
             state->activitiesByType[type].expected += j;
-            update(*state);
+            update();
         }
     }
 
     void update()
     {
-        auto state(state_.lock());
-        update(*state);
+        updateCV.notify_one();
     }
 
-    void update(State & state)
+    void draw(State & state)
     {
         if (!state.active) return;
 
@@ -323,7 +305,10 @@ public:
             }
         }
 
-        writeToStderr("\r" + ansiTruncate(line, getWindowSize().second) + "\e[K");
+        auto width = getWindowSize().second;
+        if (width <= 0) std::numeric_limits<decltype(width)>::max();
+
+        writeToStderr("\r" + filterANSIEscapes(line, width) + "\e[K");
     }
 
     std::string getStatus(State & state)
diff --git a/src/nix/run.cc b/src/nix/run.cc
index ade87e63a49c..822654daf488 100644
--- a/src/nix/run.cc
+++ b/src/nix/run.cc
@@ -85,6 +85,10 @@ struct CmdRun : InstallablesCommand
                 "To run GNU Hello:",
                 "nix run nixpkgs.hello -c hello --greeting 'Hi everybody!'"
             },
+            Example{
+                "To run GNU Hello in a chroot store:",
+                "nix run --store ~/my-nix nixpkgs.hello -c hello"
+            },
         };
     }
 
diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc
new file mode 100644
index 000000000000..758bbbc688bc
--- /dev/null
+++ b/src/nix/upgrade-nix.cc
@@ -0,0 +1,131 @@
+#include "command.hh"
+#include "store-api.hh"
+#include "download.hh"
+#include "eval.hh"
+#include "attr-path.hh"
+
+using namespace nix;
+
+struct CmdUpgradeNix : StoreCommand
+{
+    Path profileDir;
+
+    CmdUpgradeNix()
+    {
+        mkFlag()
+            .longName("profile")
+            .shortName('p')
+            .labels({"profile-dir"})
+            .description("the Nix profile to upgrade")
+            .dest(&profileDir);
+    }
+
+    std::string name() override
+    {
+        return "upgrade-nix";
+    }
+
+    std::string description() override
+    {
+        return "upgrade Nix to the latest stable version";
+    }
+
+    Examples examples() override
+    {
+        return {
+            Example{
+                "To upgrade Nix to the latest stable version:",
+                "nix upgrade-nix"
+            },
+            Example{
+                "To upgrade Nix in a specific profile:",
+                "nix upgrade-nix -p /nix/var/nix/profiles/per-user/alice/profile"
+            },
+        };
+    }
+
+    void run(ref<Store> store) override
+    {
+        settings.pureEval = true;
+
+        if (profileDir == "")
+            profileDir = getProfileDir(store);
+
+        printInfo("upgrading Nix in profile '%s'", profileDir);
+
+        Path storePath;
+        {
+            Activity act(*logger, lvlInfo, actUnknown, "querying latest Nix version");
+            storePath = getLatestNix(store);
+        }
+
+        {
+            Activity act(*logger, lvlInfo, actUnknown, fmt("downloading '%s'...", storePath));
+            store->ensurePath(storePath);
+        }
+
+        {
+            Activity act(*logger, lvlInfo, actUnknown, fmt("verifying that '%s' works...", storePath));
+            auto program = storePath + "/bin/nix-env";
+            auto s = runProgram(program, false, {"--version"});
+            if (s.find("Nix") == std::string::npos)
+                throw Error("could not verify that '%s' works", program);
+        }
+
+        {
+            Activity act(*logger, lvlInfo, actUnknown, fmt("installing '%s' into profile '%s'...", storePath, profileDir));
+            runProgram(settings.nixBinDir + "/nix-env", false,
+                {"--profile", profileDir, "-i", storePath, "--no-sandbox"});
+        }
+    }
+
+    /* Return the profile in which Nix is installed. */
+    Path getProfileDir(ref<Store> store)
+    {
+        Path where;
+
+        for (auto & dir : tokenizeString<Strings>(getEnv("PATH"), ":"))
+            if (pathExists(dir + "/nix-env")) {
+                where = dir;
+                break;
+            }
+
+        if (where == "")
+            throw Error("couldn't figure out how Nix is installed, so I can't upgrade it");
+
+        printInfo("found Nix in '%s'", where);
+
+        if (hasPrefix(where, "/run/current-system"))
+            throw Error("Nix on NixOS must be upgraded via 'nixos-rebuild'");
+
+        Path profileDir;
+        Path userEnv;
+
+        if (baseNameOf(where) != "bin" ||
+            !hasSuffix(userEnv = canonPath(profileDir = dirOf(where), true), "user-environment"))
+            throw Error("directory '%s' does not appear to be part of a Nix profile", where);
+
+        if (!store->isValidPath(userEnv))
+            throw Error("directory '%s' is not in the Nix store", userEnv);
+
+        return profileDir;
+    }
+
+    /* Return the store path of the latest stable Nix. */
+    Path getLatestNix(ref<Store> store)
+    {
+        // FIXME: use nixos.org?
+        auto req = DownloadRequest("https://github.com/NixOS/nixpkgs/raw/master/nixos/modules/installer/tools/nix-fallback-paths.nix");
+        auto res = getDownloader()->download(req);
+
+        EvalState state(Strings(), store);
+        auto v = state.allocValue();
+        state.eval(state.parseExprFromString(*res.data, "/no-such-path"), *v);
+        Bindings & bindings(*state.allocBindings(0));
+        auto v2 = findAlongAttrPath(state, settings.thisSystem, bindings, *v);
+
+        return state.forceString(*v2);
+    }
+};
+
+static RegisterCommand r1(make_ref<CmdUpgradeNix>());