about summary refs log tree commit diff
path: root/src/libstore
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2004-06-28T10·42+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2004-06-28T10·42+0000
commit91dc023665e22eb5637bf08c405e91ac9060c357 (patch)
tree566e96b4670653bc9ab68f042e94808dcdcb5b29 /src/libstore
parentb113edeab780216b0590045b932be685d1399e9b (diff)
* Added a switch `--fallback'. From the manual:
  Whenever Nix attempts to realise a derivation for which a closure is
  already known, but this closure cannot be realised, fall back on
  normalising the derivation.

  The most common scenario in which this is useful is when we have
  registered substitutes in order to perform binary distribution from,
  say, a network repository.  If the repository is down, the
  realisation of the derivation will fail.  When this option is
  specified, Nix will build the derivation instead.  Thus, binary
  installation falls back on a source installation.  This option is
  not the default since it is generally not desirable for a transient
  failure in obtaining the substitutes to lead to a full build from
  source (with the related consumption of resources).


Diffstat (limited to 'src/libstore')
-rw-r--r--src/libstore/db.cc7
-rw-r--r--src/libstore/db.hh3
-rw-r--r--src/libstore/globals.cc2
-rw-r--r--src/libstore/globals.hh4
-rw-r--r--src/libstore/normalise.cc135
-rw-r--r--src/libstore/normalise.hh14
-rw-r--r--src/libstore/store.cc29
-rw-r--r--src/libstore/store.hh3
8 files changed, 149 insertions, 48 deletions
diff --git a/src/libstore/db.cc b/src/libstore/db.cc
index e792a371b803..5c8e7edecc27 100644
--- a/src/libstore/db.cc
+++ b/src/libstore/db.cc
@@ -366,9 +366,12 @@ void Database::setString(const Transaction & txn, TableId table,
 
 
 void Database::setStrings(const Transaction & txn, TableId table,
-    const string & key, const Strings & data)
+    const string & key, const Strings & data, bool deleteEmpty)
 {
-    setString(txn, table, key, packStrings(data));
+    if (deleteEmpty && data.size() == 0)
+        delPair(txn, table, key);
+    else
+        setString(txn, table, key, packStrings(data));
 }
 
 
diff --git a/src/libstore/db.hh b/src/libstore/db.hh
index 1c681b9b5419..bbeabfc7dfa4 100644
--- a/src/libstore/db.hh
+++ b/src/libstore/db.hh
@@ -76,7 +76,8 @@ public:
         const string & key, const string & data);
 
     void setStrings(const Transaction & txn, TableId table,
-        const string & key, const Strings & data);
+        const string & key, const Strings & data,
+        bool deleteEmpty = true);
 
     void delPair(const Transaction & txn, TableId table,
         const string & key);
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index e7b32244b388..aad26501b735 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -10,6 +10,8 @@ bool keepFailed = false;
 
 bool keepGoing = false;
 
+bool tryFallback = false;
+
 Verbosity buildVerbosity = lvlDebug;
 
 unsigned int maxBuildJobs = 1;
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index cef4f704e691..7f88d5c53b27 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -33,6 +33,10 @@ extern bool keepFailed;
    of the same goal) fails. */
 extern bool keepGoing;
 
+/* Whether, if we cannot realise the known closure corresponding to a
+   derivation, we should try to normalise the derivation instead. */
+extern bool tryFallback;
+
 /* Verbosity level for build output. */
 extern Verbosity buildVerbosity;
 
diff --git a/src/libstore/normalise.cc b/src/libstore/normalise.cc
index a38bee60f384..6fc3bdfc337a 100644
--- a/src/libstore/normalise.cc
+++ b/src/libstore/normalise.cc
@@ -201,10 +201,27 @@ void Goal::waiteeDone(GoalPtr waitee, bool success)
 {
     assert(waitees.find(waitee) != waitees.end());
     waitees.erase(waitee);
-    assert(nrWaitees > 0);
+    
     if (!success) ++nrFailed;
-    if (!--nrWaitees || (!success && !keepGoing))
+    
+    assert(nrWaitees > 0);
+    if (!--nrWaitees || (!success && !keepGoing)) {
+
+        /* If we failed and keepGoing is not set, we remove all
+           remaining waitees. */
+        for (Goals::iterator i = waitees.begin(); i != waitees.end(); ++i) {
+            GoalPtr goal = *i;
+            WeakGoals waiters2;
+            for (WeakGoals::iterator j = goal->waiters.begin();
+                 j != goal->waiters.end(); ++j)
+                if (j->lock() != shared_from_this())
+                    waiters2.insert(*j);
+            goal->waiters = waiters2;
+        }
+        waitees.clear();
+        
         worker.wakeUp(shared_from_this());
+    }
 }
 
 
@@ -271,6 +288,17 @@ const char * * strings2CharPtrs(const Strings & ss)
 }
 
 
+/* Should only be called after an expression has been normalised. */
+Path queryNormalForm(const Path & nePath)
+{
+    StoreExpr ne = storeExprFromPath(nePath);
+    if (ne.type == StoreExpr::neClosure) return nePath;
+    Path nfPath;
+    if (!querySuccessor(nePath, nfPath)) abort();
+    return nfPath;
+}
+
+
 
 //////////////////////////////////////////////////////////////////////
 
@@ -472,13 +500,7 @@ void NormalisationGoal::inputNormalised()
     /* Inputs must also be realised before we can build this goal. */
     for (PathSet::iterator i = expr.derivation.inputs.begin();
          i != expr.derivation.inputs.end(); ++i)
-    {
-        Path neInput = *i, nfInput;
-        if (querySuccessor(neInput, nfInput))
-            neInput = nfInput;
-        /* Otherwise the input must be a closure. */
-        addWaitee(worker.makeRealisationGoal(neInput));
-    }
+        addWaitee(worker.makeRealisationGoal(queryNormalForm(*i)));
     
     resetWaitees(expr.derivation.inputs.size());
 
@@ -829,8 +851,8 @@ bool NormalisationGoal::prepareBuild()
          i != expr.derivation.inputs.end(); ++i)
     {
         checkInterrupt();
-        Path nePath = *i, nfPath;
-        if (!querySuccessor(nePath, nfPath)) nfPath = nePath;
+        Path nePath = *i;
+        Path nfPath = queryNormalForm(nePath);
         inputNFs.insert(nfPath);
         if (nfPath != nePath) inputSucs[nePath] = nfPath;
         /* !!! nfPath should be a root of the garbage collector while
@@ -1174,9 +1196,15 @@ string NormalisationGoal::name()
 class RealisationGoal : public Goal
 {
 private:
-    /* The path of the closure store expression. */
+    /* The path of the store expression. */
     Path nePath;
 
+    /* The normal form. */
+    Path nfPath;
+
+    /* Whether we should try to delete a broken successor mapping. */
+    bool tryFallback;
+
     /* The store expression stored at nePath. */
     StoreExpr expr;
     
@@ -1191,9 +1219,12 @@ public:
     
     /* The states. */
     void init();
+    void isNormalised();
     void haveStoreExpr();
     void elemFinished();
 
+    void fallBack(const format & error);
+    
     string name();
 };
 
@@ -1202,6 +1233,7 @@ RealisationGoal::RealisationGoal(const Path & _nePath, Worker & _worker)
     : Goal(_worker)
 {
     nePath = _nePath;
+    tryFallback = ::tryFallback;
     state = &RealisationGoal::init;
 }
 
@@ -1221,11 +1253,36 @@ void RealisationGoal::init()
 {
     trace("init");
 
-    /* The first thing to do is to make sure that the store expression
-       exists.  If it doesn't, it may be created through a
-       substitute. */
+    if (querySuccessor(nePath, nfPath)) {
+        isNormalised();
+        return;
+    }
+
+    /* First normalise the expression (which is a no-op if the
+       expression is already a closure). */
     resetWaitees(1);
-    addWaitee(worker.makeSubstitutionGoal(nePath));
+    addWaitee(worker.makeNormalisationGoal(nePath));
+
+    /* Since there is no successor right now, the normalisation goal
+       will perform an actual build.  So there is no sense in trying a
+       fallback if the realisation of the closure fails (it can't
+       really fail). */
+    tryFallback = false;
+
+    state = &RealisationGoal::isNormalised;
+}
+
+
+void RealisationGoal::isNormalised()
+{
+    trace("has been normalised");
+
+    nfPath = queryNormalForm(nePath);
+
+    /* Now make sure that the store expression exists.  If it doesn't,
+       it may be created through a substitute. */
+    resetWaitees(1);
+    addWaitee(worker.makeSubstitutionGoal(nfPath));
 
     state = &RealisationGoal::haveStoreExpr;
 }
@@ -1236,21 +1293,18 @@ void RealisationGoal::haveStoreExpr()
     trace("loading store expression");
 
     if (nrFailed != 0) {
-        printMsg(lvlError,
-            format("cannot realise missing store expression `%1%'")
-            % nePath);
-        amDone(false);
+        fallBack(format("cannot realise closure `%1%' since that file is missing") % nfPath);
         return;
     }
 
-    assert(isValidPath(nePath));
+    assert(isValidPath(nfPath));
 
     /* Get the store expression. */
-    expr = storeExprFromPath(nePath);
+    expr = storeExprFromPath(nfPath);
 
     /* If this is a normal form (i.e., a closure) we are also done. */
     if (expr.type != StoreExpr::neClosure)
-        throw Error(format("expected closure in `%1%'") % nePath);
+        throw Error(format("expected closure in `%1%'") % nfPath);
 
     /* Each path in the closure should exist, or should be creatable
        through a substitute. */
@@ -1269,12 +1323,11 @@ void RealisationGoal::elemFinished()
     trace("all closure elements present");
 
     if (nrFailed != 0) {
-        printMsg(lvlError,
+        fallBack(
             format("cannot realise closure `%1%': "
                 "%2% closure element(s) are not present "
                 "and could not be substituted")
-            % nePath % nrFailed);
-        amDone(false);
+            % nfPath % nrFailed);
         return;
     }
 
@@ -1282,6 +1335,21 @@ void RealisationGoal::elemFinished()
 }
 
 
+void RealisationGoal::fallBack(const format & error)
+{
+    if (tryFallback && nePath != nfPath) {
+        printMsg(lvlError, format("%1%; trying to normalise derivation instead")
+            % error);
+        tryFallback = false;
+        unregisterSuccessor(nePath);
+        init();
+    } else {
+        printMsg(lvlError, format("%1%; maybe `--fallback' will help") % error);
+        amDone(false);
+    }
+}
+
+
 string RealisationGoal::name()
 {
     return (format("realisation of `%1%'") % nePath).str();
@@ -1409,8 +1477,7 @@ void SubstitutionGoal::exprNormalised()
     }
 
     /* Realise the substitute store expression. */
-    if (!querySuccessor(sub.storeExpr, nfSub))
-        nfSub = sub.storeExpr;
+    nfSub = queryNormalForm(sub.storeExpr);
     addWaitee(worker.makeRealisationGoal(nfSub));
 
     resetWaitees(1);
@@ -1869,19 +1936,19 @@ Path normaliseStoreExpr(const Path & nePath)
     if (!worker.run(worker.makeNormalisationGoal(nePath)))
         throw Error(format("normalisation of store expression `%1%' failed") % nePath);
 
-    Path nfPath;
-    if (!querySuccessor(nePath, nfPath)) abort();
-    return nfPath;
+    return queryNormalForm(nePath);
 }
 
 
-void realiseClosure(const Path & nePath)
+Path realiseStoreExpr(const Path & nePath)
 {
-    startNest(nest, lvlDebug, format("realising closure `%1%'") % nePath);
+    startNest(nest, lvlDebug, format("realising `%1%'") % nePath);
 
     Worker worker;
     if (!worker.run(worker.makeRealisationGoal(nePath)))
-        throw Error(format("realisation of closure `%1%' failed") % nePath);
+        throw Error(format("realisation of store expressions `%1%' failed") % nePath);
+
+    return queryNormalForm(nePath);
 }
 
 
diff --git a/src/libstore/normalise.hh b/src/libstore/normalise.hh
index bbde545c40ec..43be136e5b75 100644
--- a/src/libstore/normalise.hh
+++ b/src/libstore/normalise.hh
@@ -10,13 +10,13 @@
    successor is known. */
 Path normaliseStoreExpr(const Path & nePath);
 
-/* Realise a closure store expression in the file system. 
-
-   The pending paths are those that are already being realised.  This
-   prevents infinite recursion for paths realised through a substitute
-   (since when we build the substitute, we would first try to realise
-   its output paths through substitutes... kaboom!). */
-void realiseClosure(const Path & nePath);
+/* Realise a store expression.  If the expression is a derivation, it
+   is first normalised into a closure.  The closure is then realised
+   in the file system (i.e., it is ensured that each path in the
+   closure exists in the file system, if necessary by using the
+   substitute mechanism).  Returns the normal form of the expression
+   (i.e., its closure expression). */
+Path realiseStoreExpr(const Path & nePath);
 
 /* Ensure that a path exists, possibly by instantiating it by
    realising a substitute. */
diff --git a/src/libstore/store.cc b/src/libstore/store.cc
index 9677f8422313..44b3a29e34d9 100644
--- a/src/libstore/store.cc
+++ b/src/libstore/store.cc
@@ -237,6 +237,30 @@ void registerSuccessor(const Transaction & txn,
 }
 
 
+void unregisterSuccessor(const Path & srcPath)
+{
+    assertStorePath(srcPath);
+
+    Transaction txn(nixDB);
+
+    Path sucPath;
+    if (!nixDB.queryString(txn, dbSuccessors, srcPath, sucPath)) {
+        txn.abort();
+        return;
+    }
+    nixDB.delPair(txn, dbSuccessors, srcPath);
+
+    Paths revs;
+    nixDB.queryStrings(txn, dbSuccessorsRev, sucPath, revs);
+    Paths::iterator i = find(revs.begin(), revs.end(), srcPath);
+    assert(i != revs.end());
+    revs.erase(i);
+    nixDB.setStrings(txn, dbSuccessorsRev, sucPath, revs);
+
+    txn.commit();
+}
+
+
 bool querySuccessor(const Path & srcPath, Path & sucPath)
 {
     return nixDB.queryString(noTxn, dbSuccessors, srcPath, sucPath);
@@ -294,10 +318,7 @@ static void writeSubstitutes(const Transaction & txn,
         ss.push_back(packStrings(ss2));
     }
 
-    if (ss.size() == 0)
-        nixDB.delPair(txn, dbSubstitutes, srcPath);
-    else
-        nixDB.setStrings(txn, dbSubstitutes, srcPath, ss);
+    nixDB.setStrings(txn, dbSubstitutes, srcPath, ss);
 }
 
 
diff --git a/src/libstore/store.hh b/src/libstore/store.hh
index 40d1859e533d..68f7d6190596 100644
--- a/src/libstore/store.hh
+++ b/src/libstore/store.hh
@@ -54,6 +54,9 @@ void copyPath(const Path & src, const Path & dst);
 void registerSuccessor(const Transaction & txn,
     const Path & srcPath, const Path & sucPath);
 
+/* Remove a successor mapping. */
+void unregisterSuccessor(const Path & srcPath);
+
 /* Return the predecessors of the Nix expression stored at the given
    path. */
 bool querySuccessor(const Path & srcPath, Path & sucPath);