about summary refs log tree commit diff
path: root/src/libstore
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstore')
-rw-r--r--src/libstore/build.cc7
-rw-r--r--src/libstore/gc.cc30
-rw-r--r--src/libstore/local-store.cc30
-rw-r--r--src/libstore/local-store.hh2
-rw-r--r--src/libstore/misc.cc36
-rw-r--r--src/libstore/remote-store.cc10
-rw-r--r--src/libstore/remote-store.hh2
-rw-r--r--src/libstore/store-api.hh7
-rw-r--r--src/libstore/worker-protocol.hh1
9 files changed, 90 insertions, 35 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 149cd8b09783..d8f8826e1990 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -278,10 +278,6 @@ public:
 };
 
 
-MakeError(SubstError, Error)
-MakeError(BuildError, Error) /* denotes a permanent build failure */
-
-
 //////////////////////////////////////////////////////////////////////
 
 
@@ -1982,7 +1978,8 @@ void DerivationGoal::computeClosure()
     }
 
     /* Register each output path as valid, and register the sets of
-       paths referenced by each of them. */
+       paths referenced by each of them.  If there are cycles in the
+       outputs, this will fail. */
     ValidPathInfos infos;
     foreach (DerivationOutputs::iterator, i, drv.outputs) {
         ValidPathInfo info;
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index feaab573ef30..14c8ba0bfe90 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -371,36 +371,6 @@ static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
 }
 
 
-static void dfsVisit(StoreAPI & store, const PathSet & paths,
-    const Path & path, PathSet & visited, Paths & sorted)
-{
-    if (visited.find(path) != visited.end()) return;
-    visited.insert(path);
-    
-    PathSet references;
-    if (store.isValidPath(path))
-        store.queryReferences(path, references);
-    
-    foreach (PathSet::iterator, i, references)
-        /* Don't traverse into paths that don't exist.  That can
-           happen due to substitutes for non-existent paths. */
-        if (*i != path && paths.find(*i) != paths.end())
-            dfsVisit(store, paths, *i, visited, sorted);
-
-    sorted.push_front(path);
-}
-
-
-Paths topoSortPaths(StoreAPI & store, const PathSet & paths)
-{
-    Paths sorted;
-    PathSet visited;
-    foreach (PathSet::const_iterator, i, paths)
-        dfsVisit(store, paths, *i, visited, sorted);
-    return sorted;
-}
-
-
 struct GCLimitReached { };
 
 
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index cf0e2ad1b13b..771776f6a2c7 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -820,6 +820,28 @@ PathSet LocalStore::queryDerivationOutputs(const Path & path)
 }
 
 
+StringSet LocalStore::queryDerivationOutputNames(const Path & path)
+{
+    SQLiteTxn txn(db);
+    
+    SQLiteStmtUse use(stmtQueryDerivationOutputs);
+    stmtQueryDerivationOutputs.bind(queryValidPathId(path));
+    
+    StringSet outputNames;
+    int r;
+    while ((r = sqlite3_step(stmtQueryDerivationOutputs)) == SQLITE_ROW) {
+        const char * s = (const char *) sqlite3_column_text(stmtQueryDerivationOutputs, 0);
+        assert(s);
+        outputNames.insert(s);
+    }
+    
+    if (r != SQLITE_DONE)
+        throwSQLiteError(db, format("error getting output names of `%1%'") % path);
+
+    return outputNames;
+}
+
+
 void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter & run)
 {
     if (run.pid != -1) return;
@@ -944,12 +966,14 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
     while (1) {
         try {
             SQLiteTxn txn(db);
+            PathSet paths;
     
             foreach (ValidPathInfos::const_iterator, i, infos) {
                 assert(i->hash.type == htSHA256);
                 /* !!! Maybe the registration info should be updated if the
                    path is already valid. */
                 if (!isValidPath(i->path)) addValidPath(*i);
+                paths.insert(i->path);
             }
 
             foreach (ValidPathInfos::const_iterator, i, infos) {
@@ -958,6 +982,12 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
                     addReference(referrer, queryValidPathId(*j));
             }
 
+            /* Do a topological sort of the paths.  This will throw an
+               error if a cycle is detected and roll back the
+               transaction.  Cycles can only occur when a derivation
+               has multiple outputs. */
+            topoSortPaths(*this, paths);
+
             txn.commit();
             break;
         } catch (SQLiteBusy & e) {
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index 4cb905f67231..2739c4eea69d 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -118,6 +118,8 @@ public:
     PathSet queryValidDerivers(const Path & path);
 
     PathSet queryDerivationOutputs(const Path & path);
+
+    StringSet queryDerivationOutputNames(const Path & path);
     
     PathSet querySubstitutablePaths();
     
diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc
index d4bbccd111ba..4ac0afe844b6 100644
--- a/src/libstore/misc.cc
+++ b/src/libstore/misc.cc
@@ -97,4 +97,40 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
 }
 
  
+static void dfsVisit(StoreAPI & store, const PathSet & paths,
+    const Path & path, PathSet & visited, Paths & sorted,
+    PathSet & parents)
+{
+    if (parents.find(path) != parents.end())
+        throw BuildError(format("cycle detected in the references of `%1%'") % path);
+    
+    if (visited.find(path) != visited.end()) return;
+    visited.insert(path);
+    parents.insert(path);
+    
+    PathSet references;
+    if (store.isValidPath(path))
+        store.queryReferences(path, references);
+    
+    foreach (PathSet::iterator, i, references)
+        /* Don't traverse into paths that don't exist.  That can
+           happen due to substitutes for non-existent paths. */
+        if (*i != path && paths.find(*i) != paths.end())
+            dfsVisit(store, paths, *i, visited, sorted, parents);
+
+    sorted.push_front(path);
+    parents.erase(path);
+}
+
+
+Paths topoSortPaths(StoreAPI & store, const PathSet & paths)
+{
+    Paths sorted;
+    PathSet visited, parents;
+    foreach (PathSet::const_iterator, i, paths)
+        dfsVisit(store, paths, *i, visited, sorted, parents);
+    return sorted;
+}
+
+
 }
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index 942c5bcf1c7c..6e9921ede79f 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -326,6 +326,16 @@ PathSet RemoteStore::queryDerivationOutputs(const Path & path)
 }
 
 
+PathSet RemoteStore::queryDerivationOutputNames(const Path & path)
+{
+    openConnection();
+    writeInt(wopQueryDerivationOutputNames, to);
+    writeString(path, to);
+    processStderr();
+    return readStrings<PathSet>(from);
+}
+
+
 Path RemoteStore::addToStore(const Path & _srcPath,
     bool recursive, HashType hashAlgo, PathFilter & filter)
 {
diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh
index 34a2d91dfcd1..c5853ef53646 100644
--- a/src/libstore/remote-store.hh
+++ b/src/libstore/remote-store.hh
@@ -41,6 +41,8 @@ public:
     
     PathSet queryDerivationOutputs(const Path & path);
     
+    StringSet queryDerivationOutputNames(const Path & path);
+
     bool hasSubstitutes(const Path & path);
     
     bool querySubstitutablePathInfo(const Path & path,
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index d4997c886207..61bcaf50507f 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -140,6 +140,9 @@ public:
 
     /* Query the outputs of the derivation denoted by `path'. */
     virtual PathSet queryDerivationOutputs(const Path & path) = 0;
+
+    /* Query the output names of the derivation denoted by `path'. */
+    virtual StringSet queryDerivationOutputNames(const Path & path) = 0;
     
     /* Query whether a path has substitutes. */
     virtual bool hasSubstitutes(const Path & path) = 0;
@@ -346,6 +349,10 @@ void exportPaths(StoreAPI & store, const Paths & paths,
     bool sign, Sink & sink);
 
 
+MakeError(SubstError, Error)
+MakeError(BuildError, Error) /* denotes a permanent build failure */
+
+
 }
 
 
diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh
index 760d08a747ae..ef1e0993df28 100644
--- a/src/libstore/worker-protocol.hh
+++ b/src/libstore/worker-protocol.hh
@@ -39,6 +39,7 @@ typedef enum {
     wopClearFailedPaths = 25,
     wopQueryPathInfo = 26,
     wopImportPaths = 27,
+    wopQueryDerivationOutputNames = 28,
 } WorkerOp;