about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2014-05-21T15·19+0200
committerEelco Dolstra <eelco.dolstra@logicblox.com>2014-05-21T15·19+0200
commit9f9080e2c019f188ba679a7a89284d7eaf629710 (patch)
treedac5d9672e2b4c139c2e97d7766911b62f8b069b /src
parenteac5841970737b799c55ec78f6ace6d80762ff04 (diff)
nix-store -l: Fetch build logs from the Internet
If a build log is not available locally, then ‘nix-store -l’ will now
try to download it from the servers listed in the ‘log-servers’ option
in nix.conf. For instance, if you have:

  log-servers = http://hydra.nixos.org/log

then it will try to get logs from http://hydra.nixos.org/log/<base
name of the store path>. So you can do things like:

  $ nix-store -l $(which xterm)

and get a log even if xterm wasn't built locally.
Diffstat (limited to 'src')
-rw-r--r--src/libstore/globals.cc1
-rw-r--r--src/libstore/globals.hh3
-rw-r--r--src/libutil/util.cc2
-rw-r--r--src/libutil/util.hh2
-rw-r--r--src/nix-store/local.mk2
-rw-r--r--src/nix-store/nix-store.cc29
6 files changed, 35 insertions, 4 deletions
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index 739199d48ee7..180344e336b0 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -147,6 +147,7 @@ void Settings::update()
     get(envKeepDerivations, "env-keep-derivations");
     get(sshSubstituterHosts, "ssh-substituter-hosts");
     get(useSshSubstituter, "use-ssh-substituter");
+    get(logServers, "log-servers");
 
     string subs = getEnv("NIX_SUBSTITUTERS", "default");
     if (subs == "default") {
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 711c3652947f..65a6c388b8a6 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -197,6 +197,9 @@ struct Settings {
     /* Whether to show a stack trace if Nix evaluation fails. */
     bool showTrace;
 
+    /* A list of URL prefixes that can return Nix build logs. */
+    Strings logServers;
+
 private:
     SettingsMap settings, overrides;
 
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index 846674a29d95..8fc78b1463de 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -901,7 +901,7 @@ string runProgram(Path program, bool searchPath, const Strings & args)
     /* Wait for the child to finish. */
     int status = pid.wait(true);
     if (!statusOk(status))
-        throw Error(format("program `%1%' %2%")
+        throw ExecError(format("program `%1%' %2%")
             % program % statusToString(status));
 
     return result;
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index ce2d77c19a42..1e9ffcf51b57 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -257,6 +257,8 @@ void killUser(uid_t uid);
 string runProgram(Path program, bool searchPath = false,
     const Strings & args = Strings());
 
+MakeError(ExecError, Error)
+
 /* Close all file descriptors except stdin, stdout, stderr, and those
    listed in the given set.  Good practice in child processes. */
 void closeMostFDs(const set<int> & exceptions);
diff --git a/src/nix-store/local.mk b/src/nix-store/local.mk
index 7f93e4c19196..dc049f348ff7 100644
--- a/src/nix-store/local.mk
+++ b/src/nix-store/local.mk
@@ -7,3 +7,5 @@ nix-store_SOURCES := $(wildcard $(d)/*.cc)
 nix-store_LIBS = libmain libstore libutil libformat
 
 nix-store_LDFLAGS = -lbz2
+
+nix-store_CXXFLAGS = -DCURL=\"$(curl)\"
diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc
index 5da401c77fd2..4fee7258cb94 100644
--- a/src/nix-store/nix-store.cc
+++ b/src/nix-store/nix-store.cc
@@ -467,10 +467,11 @@ static void opReadLog(Strings opFlags, Strings opArgs)
     foreach (Strings::iterator, i, opArgs) {
         Path path = useDeriver(followLinksToStorePath(*i));
 
-        for (int j = 0; j <= 2; j++) {
-            if (j == 2) throw Error(format("build log of derivation `%1%' is not available") % path);
+        string baseName = baseNameOf(path);
+        bool found = false;
+
+        for (int j = 0; j < 2; j++) {
 
-            string baseName = baseNameOf(path);
             Path logPath =
                 j == 0
                 ? (format("%1%/%2%/%3%/%4%") % settings.nixLogDir % drvsLogDir % string(baseName, 0, 2) % string(baseName, 2)).str()
@@ -481,6 +482,7 @@ static void opReadLog(Strings opFlags, Strings opArgs)
                 /* !!! Make this run in O(1) memory. */
                 string log = readFile(logPath);
                 writeFull(STDOUT_FILENO, (const unsigned char *) log.data(), log.size());
+                found = true;
                 break;
             }
 
@@ -500,9 +502,30 @@ static void opReadLog(Strings opFlags, Strings opArgs)
                     writeFull(STDOUT_FILENO, buf, n);
                 } while (err != BZ_STREAM_END);
                 BZ2_bzReadClose(&err, bz);
+                found = true;
                 break;
             }
         }
+
+        if (!found) {
+            for (auto & i : settings.logServers) {
+                string prefix = i;
+                if (!prefix.empty() && prefix.back() != '/') prefix += '/';
+                string url = prefix + baseName;
+                try {
+                    string log = runProgram(CURL, true, {"--fail", "--location", "--silent", "--", url});
+                    std::cout << "(using build log from " << url << ")" << std::endl;
+                    std::cout << log;
+                    found = true;
+                    break;
+                } catch (ExecError & e) {
+                    /* Ignore errors from curl. FIXME: actually, might be
+                       nice to print a warning on HTTP status != 404. */
+                }
+            }
+        }
+
+        if (!found) throw Error(format("build log of derivation `%1%' is not available") % path);
     }
 }