diff --git a/.gitignore b/.gitignore index d4e8d17de..5e1561829 100644 --- a/.gitignore +++ b/.gitignore @@ -61,23 +61,11 @@ # /externals/ /externals/Makefile /externals/Makefile.in -/externals/aterm-* -/externals/have-aterm -/externals/build-aterm -/externals/inst-aterm /externals/bzip2-* -/externals/have-bzip2 /externals/build-bzip2 /externals/inst-bzip2 - -# /make/examples/aterm/ -/make/examples/aterm/result* - -# /make/examples/aterm/aterm/ -/make/examples/aterm/aterm/* - -# /make/examples/aterm/test/ -/make/examples/aterm/test/* +/externals/sqlite-* +/externals/build-sqlite # /misc/ /misc/Makefile.in @@ -100,13 +88,16 @@ /scripts/nix-channel /scripts/nix-build /scripts/nix-copy-closure -/scripts/readmanifest.pm -/scripts/readconfig.pm +/scripts/nix-generate-patches +/scripts/NixConfig.pm +/scripts/NixManifest.pm +/scripts/GeneratePatches.pm /scripts/download-using-manifests.pl /scripts/copy-from-other-stores.pl -/scripts/generate-patches.pl /scripts/find-runtime-roots.pl /scripts/build-remote.pl +/scripts/nix-reduce-build +/scripts/nix-http-export.cgi # /src/ /src/Makefile @@ -168,6 +159,7 @@ /src/libstore/derivations-ast.cc /src/libstore/derivations-ast.hh /src/libstore/.libs +/src/libstore/schema.sql.hh # /src/libutil/ /src/libutil/Makefile @@ -242,6 +234,7 @@ /tests/config.nix /tests/common.sh /tests/dummy +/tests/result* # /tests/lang/ /tests/lang/*.out diff --git a/configure.ac b/configure.ac index 52c92b76b..1e8665992 100644 --- a/configure.ac +++ b/configure.ac @@ -274,8 +274,8 @@ AC_SUBST(sqlite_bin) # Whether to use the Boehm garbage collector. AC_ARG_ENABLE(gc, AC_HELP_STRING([--enable-gc], [enable garbage collection in the Nix expression evaluator (requires Boehm GC)]), - gc=$enableval, gc=) -if test -n "$gc"; then + gc=$enableval, gc=no) +if test "$gc" = yes; then PKG_CHECK_MODULES([BDW_GC], [bdw-gc]) CXXFLAGS="$BDW_GC_CFLAGS $CXXFLAGS" AC_DEFINE(HAVE_BOEHMGC, 1, [Whether to use the Boehm garbage collector.]) diff --git a/corepkgs/Makefile.am b/corepkgs/Makefile.am index 86d7027ed..a8de60165 100644 --- a/corepkgs/Makefile.am +++ b/corepkgs/Makefile.am @@ -1,6 +1,6 @@ all-local: config.nix -files = nar.nix buildenv.nix buildenv.pl unpack-channel.nix unpack-channel.sh +files = nar.nix buildenv.nix buildenv.pl unpack-channel.nix unpack-channel.sh derivation.nix install-exec-local: $(INSTALL) -d $(DESTDIR)$(datadir)/nix/corepkgs diff --git a/corepkgs/derivation.nix b/corepkgs/derivation.nix new file mode 100644 index 000000000..757108be3 --- /dev/null +++ b/corepkgs/derivation.nix @@ -0,0 +1,27 @@ +/* This is the implementation of the ‘derivation’ builtin function. + It's actually a wrapper around the ‘derivationStrict’ primop. */ + +drvAttrs @ { outputs ? [ "out" ], ... }: + +let + + strict = derivationStrict drvAttrs; + + commonAttrs = drvAttrs // (builtins.listToAttrs outputsList) // + { all = map (x: x.value) outputsList; + inherit drvAttrs; + }; + + outputToAttrListElement = outputName: + { name = outputName; + value = commonAttrs // { + outPath = builtins.getAttr outputName strict; + drvPath = strict.drvPath; + type = "derivation"; + inherit outputName; + }; + }; + + outputsList = map outputToAttrListElement outputs; + +in (builtins.head outputsList).value diff --git a/perl/lib/Nix/Manifest.pm b/perl/lib/Nix/Manifest.pm index d1717a0a8..909604a44 100644 --- a/perl/lib/Nix/Manifest.pm +++ b/perl/lib/Nix/Manifest.pm @@ -307,6 +307,7 @@ EOF for my $manifestLink (glob "$manifestDir/*.nixmanifest") { my $manifest = Cwd::abs_path($manifestLink); + next unless -f $manifest; my $timestamp = lstat($manifest)->mtime; $seen{$manifest} = 1; diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 2b97b76fb..bba14bc35 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -148,8 +148,6 @@ EvalState::EvalState() nrAttrsets = nrOpUpdates = nrOpUpdateValuesCopied = 0; deepestStack = (char *) -1; - createBaseEnv(); - allowUnsafeEquality = getEnv("NIX_NO_UNSAFE_EQ", "") == ""; #if HAVE_BOEHMGC @@ -188,6 +186,8 @@ EvalState::EvalState() foreach (Strings::iterator, i, paths) addToSearchPath(*i); addToSearchPath("nix=" + nixDataDir + "/nix/corepkgs"); searchPathInsertionPoint = searchPath.begin(); + + createBaseEnv(); } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 66173cdaf..21c9fb351 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -356,27 +356,31 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) inputs to ensure that they are available when the builder runs. */ if (path.at(0) == '=') { - path = string(path, 1); - PathSet refs; computeFSClosure(*store, path, refs); + /* !!! This doesn't work if readOnlyMode is set. */ + PathSet refs; computeFSClosure(*store, string(path, 1), refs); foreach (PathSet::iterator, j, refs) { drv.inputSrcs.insert(*j); if (isDerivation(*j)) - drv.inputDrvs[*j] = singleton("out"); + drv.inputDrvs[*j] = store->queryDerivationOutputNames(*j); } } /* See prim_unsafeDiscardOutputDependency. */ - bool useDrvAsSrc = false; - if (path.at(0) == '~') { - path = string(path, 1); - useDrvAsSrc = true; + else if (path.at(0) == '~') + drv.inputSrcs.insert(string(path, 1)); + + /* Handle derivation outputs of the form ‘!!’. */ + else if (path.at(0) == '!') { + size_t index = path.find("!", 1); + drv.inputDrvs[string(path, index + 1)].insert(string(path, 1, index - 1)); } - assert(isStorePath(path)); + /* Handle derivation contexts returned by + ‘builtins.storePath’. */ + else if (isDerivation(path)) + drv.inputDrvs[path] = store->queryDerivationOutputNames(path); - debug(format("derivation uses `%1%'") % path); - if (!useDrvAsSrc && isDerivation(path)) - drv.inputDrvs[path] = singleton("out"); + /* Otherwise it's a source file. */ else drv.inputSrcs.insert(path); } @@ -447,10 +451,8 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) state.mkAttrs(v, 1 + drv.outputs.size()); mkString(*state.allocAttr(v, state.sDrvPath), drvPath, singleton("=" + drvPath)); foreach (DerivationOutputs::iterator, i, drv.outputs) { - /* The output path of an output X is ‘Path’, - e.g. ‘outPath’. */ - mkString(*state.allocAttr(v, state.symbols.create(i->first + "Path")), - i->second.path, singleton(drvPath)); + mkString(*state.allocAttr(v, state.symbols.create(i->first)), + i->second.path, singleton("!" + i->first + "!" + drvPath)); } v.attrs->sort(); } @@ -1042,15 +1044,6 @@ void EvalState::createBaseEnv() addPrimOp("__getEnv", 1, prim_getEnv); addPrimOp("__trace", 2, prim_trace); - // Derivations - addPrimOp("derivationStrict", 1, prim_derivationStrict); - - /* Add a wrapper around the derivation primop that computes the - `drvPath' and `outPath' attributes lazily. */ - string s = "attrs: let res = derivationStrict attrs; in attrs // { drvPath = res.drvPath; outPath = res.outPath; type = \"derivation\"; }"; - mkThunk_(v, parseExprFromString(s, "/")); - addConstant("derivation", v); - // Paths addPrimOp("__toPath", 1, prim_toPath); addPrimOp("__storePath", 1, prim_storePath); @@ -1099,6 +1092,14 @@ void EvalState::createBaseEnv() addPrimOp("__parseDrvName", 1, prim_parseDrvName); addPrimOp("__compareVersions", 2, prim_compareVersions); + // Derivations + addPrimOp("derivationStrict", 1, prim_derivationStrict); + + /* Add a wrapper around the derivation primop that computes the + `drvPath' and `outPath' attributes lazily. */ + mkThunk_(v, parseExprFromFile(findFile("nix/derivation.nix"))); + addConstant("derivation", v); + /* Now that we've added all primops, sort the `builtins' attribute set, because attribute lookups expect it to be sorted. */ baseEnv.values[0]->attrs->sort(); diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 149cd8b09..d8f8826e1 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 feaab573e..14c8ba0bf 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 cf0e2ad1b..771776f6a 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 4cb905f67..2739c4eea 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 d4bbccd11..4ac0afe84 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 942c5bcf1..6e9921ede 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(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 34a2d91df..c5853ef53 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 d4997c886..61bcaf505 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 760d08a74..ef1e0993d 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; diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index e92ccb153..b9f8d927a 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -50,26 +50,30 @@ static Path useDeriver(Path path) } -/* Realisation the given path. For a derivation that means build it; - for other paths it means ensure their validity. */ -static Path realisePath(const Path & path) +/* Realise the given path. For a derivation that means build it; for + other paths it means ensure their validity. */ +static PathSet realisePath(const Path & path) { if (isDerivation(path)) { - PathSet paths; - paths.insert(path); - store->buildDerivations(paths); - Path outPath = findOutput(derivationFromPath(*store, path), "out"); - - if (gcRoot == "") - printGCWarning(); - else - outPath = addPermRoot(*store, outPath, - makeRootName(gcRoot, rootNr), indirectRoot); - - return outPath; - } else { + store->buildDerivations(singleton(path)); + Derivation drv = derivationFromPath(*store, path); + + PathSet outputs; + foreach (DerivationOutputs::iterator, i, drv.outputs) { + Path outPath = i->second.path; + if (gcRoot == "") + printGCWarning(); + else + outPath = addPermRoot(*store, outPath, + makeRootName(gcRoot, rootNr), indirectRoot); + outputs.insert(outPath); + } + return outputs; + } + + else { store->ensurePath(path); - return path; + return singleton(path); } } @@ -96,8 +100,11 @@ static void opRealise(Strings opFlags, Strings opArgs) if (isDerivation(*i)) drvPaths.insert(*i); store->buildDerivations(drvPaths); - foreach (Strings::iterator, i, opArgs) - cout << format("%1%\n") % realisePath(*i); + foreach (Strings::iterator, i, opArgs) { + PathSet paths = realisePath(*i); + foreach (PathSet::iterator, j, paths) + cout << format("%1%\n") % *j; + } } @@ -157,14 +164,17 @@ static void opPrintFixedPath(Strings opFlags, Strings opArgs) } -static Path maybeUseOutput(const Path & storePath, bool useOutput, bool forceRealise) +static PathSet maybeUseOutputs(const Path & storePath, bool useOutput, bool forceRealise) { if (forceRealise) realisePath(storePath); if (useOutput && isDerivation(storePath)) { Derivation drv = derivationFromPath(*store, storePath); - return findOutput(drv, "out"); + PathSet outputs; + foreach (DerivationOutputs::iterator, i, drv.outputs) + outputs.insert(i->second.path); + return outputs; } - else return storePath; + else return singleton(storePath); } @@ -257,7 +267,8 @@ static void opQuery(Strings opFlags, Strings opArgs) *i = followLinksToStorePath(*i); if (forceRealise) realisePath(*i); Derivation drv = derivationFromPath(*store, *i); - cout << format("%1%\n") % findOutput(drv, "out"); + foreach (DerivationOutputs::iterator, j, drv.outputs) + cout << format("%1%\n") % j->second.path; } break; } @@ -268,11 +279,13 @@ static void opQuery(Strings opFlags, Strings opArgs) case qReferrersClosure: { PathSet paths; foreach (Strings::iterator, i, opArgs) { - Path path = maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise); - if (query == qRequisites) computeFSClosure(*store, path, paths, false, includeOutputs); - else if (query == qReferences) store->queryReferences(path, paths); - else if (query == qReferrers) store->queryReferrers(path, paths); - else if (query == qReferrersClosure) computeFSClosure(*store, path, paths, true); + PathSet ps = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise); + foreach (PathSet::iterator, j, ps) { + if (query == qRequisites) computeFSClosure(*store, *j, paths, false, includeOutputs); + else if (query == qReferences) store->queryReferences(*j, paths); + else if (query == qReferrers) store->queryReferrers(*j, paths); + else if (query == qReferrersClosure) computeFSClosure(*store, *j, paths, true); + } } Paths sorted = topoSortPaths(*store, paths); for (Paths::reverse_iterator i = sorted.rbegin(); @@ -304,13 +317,15 @@ static void opQuery(Strings opFlags, Strings opArgs) case qHash: case qSize: foreach (Strings::iterator, i, opArgs) { - Path path = maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise); - ValidPathInfo info = store->queryPathInfo(path); - if (query == qHash) { - assert(info.hash.type == htSHA256); - cout << format("sha256:%1%\n") % printHash32(info.hash); - } else if (query == qSize) - cout << format("%1%\n") % info.narSize; + PathSet paths = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise); + foreach (PathSet::iterator, j, paths) { + ValidPathInfo info = store->queryPathInfo(*j); + if (query == qHash) { + assert(info.hash.type == htSHA256); + cout << format("sha256:%1%\n") % printHash32(info.hash); + } else if (query == qSize) + cout << format("%1%\n") % info.narSize; + } } break; @@ -323,16 +338,20 @@ static void opQuery(Strings opFlags, Strings opArgs) case qGraph: { PathSet roots; - foreach (Strings::iterator, i, opArgs) - roots.insert(maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise)); + foreach (Strings::iterator, i, opArgs) { + PathSet paths = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise); + roots.insert(paths.begin(), paths.end()); + } printDotGraph(roots); break; } case qXml: { PathSet roots; - foreach (Strings::iterator, i, opArgs) - roots.insert(maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise)); + foreach (Strings::iterator, i, opArgs) { + PathSet paths = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise); + roots.insert(paths.begin(), paths.end()); + } printXmlGraph(roots); break; } @@ -345,10 +364,11 @@ static void opQuery(Strings opFlags, Strings opArgs) case qRoots: { PathSet referrers; - foreach (Strings::iterator, i, opArgs) - computeFSClosure(*store, - maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise), - referrers, true); + foreach (Strings::iterator, i, opArgs) { + PathSet paths = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise); + foreach (PathSet::iterator, j, paths) + computeFSClosure(*store, *j, referrers, true); + } Roots roots = store->findRoots(); foreach (Roots::iterator, i, roots) if (referrers.find(i->second) != referrers.end()) diff --git a/src/nix-worker/nix-worker.cc b/src/nix-worker/nix-worker.cc index 5f57b2981..f28905a24 100644 --- a/src/nix-worker/nix-worker.cc +++ b/src/nix-worker/nix-worker.cc @@ -331,6 +331,16 @@ static void performOp(unsigned int clientVersion, break; } + case wopQueryDerivationOutputNames: { + Path path = readStorePath(from); + startWork(); + StringSet names; + names = store->queryDerivationOutputNames(path); + stopWork(); + writeStrings(names, to); + break; + } + case wopQueryDeriver: { Path path = readStorePath(from); startWork(); diff --git a/tests/Makefile.am b/tests/Makefile.am index dbafd553a..15c103ec4 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -8,7 +8,8 @@ TESTS = init.sh hash.sh lang.sh add.sh simple.sh dependencies.sh \ referrers.sh user-envs.sh logging.sh nix-build.sh misc.sh fixed.sh \ gc-runtime.sh install-package.sh check-refs.sh filter-source.sh \ remote-store.sh export.sh export-graph.sh negative-caching.sh \ - binary-patching.sh timeout.sh secure-drv-outputs.sh nix-channel.sh + binary-patching.sh timeout.sh secure-drv-outputs.sh nix-channel.sh \ + multiple-outputs.sh XFAIL_TESTS = @@ -35,5 +36,6 @@ EXTRA_DIST = $(TESTS) \ binary-patching.nix \ timeout.nix timeout.builder.sh \ secure-drv-outputs.nix \ + multiple-outputs.nix \ $(wildcard lang/*.nix) $(wildcard lang/*.exp) $(wildcard lang/*.exp.xml) $(wildcard lang/*.flags) $(wildcard lang/dir*/*.nix) \ common.sh.in diff --git a/tests/multiple-outputs.nix b/tests/multiple-outputs.nix new file mode 100644 index 000000000..6add7dd6f --- /dev/null +++ b/tests/multiple-outputs.nix @@ -0,0 +1,67 @@ +with import ./config.nix; + +rec { + + a = mkDerivation { + name = "multiple-outputs-a"; + outputs = [ "first" "second" ]; + builder = builtins.toFile "builder.sh" + '' + mkdir $first $second + test -z $all + echo "second" > $first/file + echo "first" > $second/file + ''; + helloString = "Hello, world!"; + }; + + b = mkDerivation { + defaultOutput = assert a.second.helloString == "Hello, world!"; a; + firstOutput = assert a.outputName == "first"; a.first.first; + secondOutput = assert a.second.outputName == "second"; a.second.first.first.second.second.first.second; + allOutputs = a.all; + name = "multiple-outputs-b"; + builder = builtins.toFile "builder.sh" + '' + mkdir $out + test "$firstOutput $secondOutput" = "$allOutputs" + test "$defaultOutput" = "$firstOutput" + test "$(cat $firstOutput/file)" = "second" + test "$(cat $secondOutput/file)" = "first" + echo "success" > $out/file + ''; + }; + + c = mkDerivation { + name = "multiple-outputs-c"; + drv = b.drvPath; + builder = builtins.toFile "builder.sh" + '' + mkdir $out + ln -s $drv $out/drv + ''; + }; + + d = mkDerivation { + name = "multiple-outputs-d"; + drv = builtins.unsafeDiscardOutputDependency b.drvPath; + builder = builtins.toFile "builder.sh" + '' + mkdir $out + echo $drv > $out/drv + ''; + }; + + cyclic = (mkDerivation { + name = "cyclic-outputs"; + outputs = [ "a" "b" "c" ]; + builder = builtins.toFile "builder.sh" + '' + mkdir $a $b $c + echo $a > $b/foo + echo $b > $c/bar + echo $c > $a/baz + ''; + }).a; + +} diff --git a/tests/multiple-outputs.sh b/tests/multiple-outputs.sh new file mode 100644 index 000000000..20f3380a1 --- /dev/null +++ b/tests/multiple-outputs.sh @@ -0,0 +1,42 @@ +source common.sh + +clearStore + +# Test whether read-only evaluation works when referring to the +# ‘drvPath’ attribute. +echo "evaluating c..." +#drvPath=$(nix-instantiate multiple-outputs.nix -A c --readonly-mode) + +# And check whether the resulting derivation explicitly depends on all +# outputs. +drvPath=$(nix-instantiate multiple-outputs.nix -A c) +#[ "$drvPath" = "$drvPath2" ] +grep -q 'multiple-outputs-a.drv",\["first","second"\]' $drvPath +grep -q 'multiple-outputs-b.drv",\["out"\]' $drvPath + +# While we're at it, test the ‘unsafeDiscardOutputDependency’ primop. +outPath=$(nix-build multiple-outputs.nix -A d) +drvPath=$(cat $outPath/drv) +outPath=$(nix-store -q $drvPath) +! [ -e "$outPath" ] + +# Do a build of something that depends on a derivation with multiple +# outputs. +echo "building b..." +outPath=$(nix-build multiple-outputs.nix -A b) +echo "output path is $outPath" +[ "$(cat "$outPath"/file)" = "success" ] + +# Make sure that nix-build works on derivations with multiple outputs. +echo "building a.first..." +nix-build multiple-outputs.nix -A a.first + +# Cyclic outputs should be rejected. +echo "building cyclic..." +if nix-build multiple-outputs.nix -A cyclic; then + echo "Cyclic outputs incorrectly accepted!" + exit 1 +fi + +echo "collecting garbage..." +nix-store --gc