From 0b34e57eb80dbadd8f24426c4486af97cbfe40bf Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Wed, 14 Sep 2011 00:41:02 +0000 Subject: [PATCH 01/41] Create a branch for me to play around with finishing off the multiple outputs implementation From c172d16b00dd2126eb7c4f12c26f1e30e4356f07 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Wed, 14 Sep 2011 05:59:17 +0000 Subject: [PATCH 02/41] First attempt at the output-as-derivation semantics For each output, this adds a corresponding attribute to the derivation that is the same as the derivation except for outPath, which is set to the path specific to that output. Additionally, an "all" attribute is added that is a list of all of the output derivations. This has to be done outside of derivationStrict as each output is itself a derivation that contains itself (and all other outputs) as an attribute. The derivation itself is equivalent to the first output in the outputs list (or "out" if that list isn't set). --- src/libexpr/primops.cc | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 0e81f7b72..4d64bf32a 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1052,15 +1052,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); @@ -1109,6 +1100,33 @@ 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. */ + string s = "attrs: \ + let \ + strict = derivationStrict attrs; \ + attrValues = attrs: \ + map (name: builtins.getAttr name attrs) (builtins.attrNames attrs); \ + outputToAttrListElement = output: \ + let outPath = builtins.getAttr (output + \"Path\") strict; in { \ + name = output; \ + value = attrs // { \ + drvPath = strict.drvPath; inherit outPath; type = \"derivation\"; \ + } // outputsAttrs // { all = allList; }; \ + }; \ + outputsList = if attrs ? outputs then \ + map outputToAttrListElement attrs.outputs else \ + [ (outputToAttrListElement \"out\") ]; \ + outputsAttrs = builtins.listToAttrs outputsList; \ + allList = attrValues outputsAttrs; \ + head = if attrs ? outputs then builtins.head attrs.outputs else \"out\"; \ + in builtins.getAttr head outputsAttrs"; + mkThunk_(v, parseExprFromString(s, "/")); + 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(); From 8f28a3ba25dd0dad6411a039bc01ad87c61a6e59 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Wed, 14 Sep 2011 05:59:29 +0000 Subject: [PATCH 03/41] Add a test for multiple outputs This currently fails. Yay test-driven development! --- tests/Makefile.am | 4 +++- tests/multiple-outputs.a.builder.sh | 6 ++++++ tests/multiple-outputs.b.builder.sh | 7 +++++++ tests/multiple-outputs.nix | 23 +++++++++++++++++++++++ tests/multiple-outputs.sh | 15 +++++++++++++++ 5 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 tests/multiple-outputs.a.builder.sh create mode 100644 tests/multiple-outputs.b.builder.sh create mode 100644 tests/multiple-outputs.nix create mode 100644 tests/multiple-outputs.sh diff --git a/tests/Makefile.am b/tests/Makefile.am index 676a9c387..0418f73e6 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -8,7 +8,7 @@ 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 + binary-patching.sh timeout.sh secure-drv-outputs.sh multiple-outputs.sh XFAIL_TESTS = @@ -35,6 +35,8 @@ EXTRA_DIST = $(TESTS) \ binary-patching.nix \ timeout.nix timeout.builder.sh \ secure-drv-outputs.nix \ + multiple-outputs.nix \ + multiple-outputs.a.builder.sh multiple-outputs.b.builder.sh \ $(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.a.builder.sh b/tests/multiple-outputs.a.builder.sh new file mode 100644 index 000000000..657b7ea0a --- /dev/null +++ b/tests/multiple-outputs.a.builder.sh @@ -0,0 +1,6 @@ +mkdir $first +mkdir $second +test -z $all + +echo "second" > $first/file +echo "first" > $second/file diff --git a/tests/multiple-outputs.b.builder.sh b/tests/multiple-outputs.b.builder.sh new file mode 100644 index 000000000..bc63fc068 --- /dev/null +++ b/tests/multiple-outputs.b.builder.sh @@ -0,0 +1,7 @@ +mkdir $out +test "$firstOutput $secondOutput" = "$allOutputs" +test "$defaultOutput" = "$firstOutput" +test "$(cat $first/file)" = "second" +test "$(cat $second/file)" = "first" + +echo "success" > $out/file diff --git a/tests/multiple-outputs.nix b/tests/multiple-outputs.nix new file mode 100644 index 000000000..e8fbf91bf --- /dev/null +++ b/tests/multiple-outputs.nix @@ -0,0 +1,23 @@ +with import ./config.nix; + +let + + a = mkDerivation { + name = "multiple-outputs-a"; + outputs = [ "first" "second" ]; + builder = ./multiple-outputs.a.builder.sh; + helloString = "Hello, world!"; + }; + +in + +assert a.second.helloString == "Hello, world!"; + +mkDerivation { + defaultOutput = a; + firstOutput = a.first.first; + secondOutput = a.second.first.first.second.second.first.second; + allOutputs = a.all; + name = "multiple-outputs-b"; + builder = ./multiple-outputs.b.builder.sh; +} diff --git a/tests/multiple-outputs.sh b/tests/multiple-outputs.sh new file mode 100644 index 000000000..f4cd01234 --- /dev/null +++ b/tests/multiple-outputs.sh @@ -0,0 +1,15 @@ +source common.sh + +echo "Testing multiple outputs..." + +drvPath=$($nixinstantiate multiple-outputs.nix) + +echo "derivation is $drvPath" + +outPath=$($nixstore -rvv "$drvPath") + +echo "output path is $outPath" + +text=$(cat "$outPath"/file) +if test "$text" != "success"; then exit 1; fi + From f3e410d4bffc109718d8a108258710a543ecfca6 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Fri, 16 Sep 2011 11:30:03 +0000 Subject: [PATCH 04/41] Add a currentOutput attribute to derivations keep track of which output is active --- src/libexpr/primops.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 4d64bf32a..121dbca9b 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1114,7 +1114,10 @@ void EvalState::createBaseEnv() let outPath = builtins.getAttr (output + \"Path\") strict; in { \ name = output; \ value = attrs // { \ - drvPath = strict.drvPath; inherit outPath; type = \"derivation\"; \ + drvPath = strict.drvPath; \ + inherit outPath; \ + type = \"derivation\"; \ + currentOutput = output; \ } // outputsAttrs // { all = allList; }; \ }; \ outputsList = if attrs ? outputs then \ From ffa038f66dc0dfcfaf16c523830490bb606af04c Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Fri, 16 Sep 2011 11:30:44 +0000 Subject: [PATCH 05/41] Add an sCurrentOutput member to EvalState --- src/libexpr/eval.cc | 1 + src/libexpr/eval.hh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d0bdaf238..c31ec261e 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -133,6 +133,7 @@ string showType(const Value & v) EvalState::EvalState() : sWith(symbols.create("")) , sOutPath(symbols.create("outPath")) + , sCurrentOutput(symbols.create("currentOutput")) , sDrvPath(symbols.create("drvPath")) , sType(symbols.create("type")) , sMeta(symbols.create("meta")) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 694d4407b..3816f36bd 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -204,7 +204,7 @@ public: SymbolTable symbols; const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, - sSystem, sOverrides; + sSystem, sOverrides, sCurrentOutput; private: SrcToStore srcToStore; From bf50d6ad3271aaa6ac93b68e99f5acb1d9a158c7 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Fri, 16 Sep 2011 11:30:52 +0000 Subject: [PATCH 06/41] Add information about which output is active to drvPath's context This will break things that depend on being able to just strip away an equals sign, so those have to be updated next --- src/libexpr/primops.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 121dbca9b..a752b9a59 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -455,8 +455,8 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) drvHashes[drvPath] = hashDerivationModulo(*store, drv); state.mkAttrs(v, 1 + drv.outputs.size()); - mkString(*state.allocAttr(v, state.sDrvPath), drvPath, singleton("=" + drvPath)); foreach (DerivationOutputs::iterator, i, drv.outputs) { + mkString(*state.allocAttr(v, state.symbols.create(i->first + "DrvPath")), drvPath, singleton("=" + i->first + "=" + drvPath)); /* The output path of an output X is ‘Path’, e.g. ‘outPath’. */ mkString(*state.allocAttr(v, state.symbols.create(i->first + "Path")), @@ -1111,11 +1111,13 @@ void EvalState::createBaseEnv() attrValues = attrs: \ map (name: builtins.getAttr name attrs) (builtins.attrNames attrs); \ outputToAttrListElement = output: \ - let outPath = builtins.getAttr (output + \"Path\") strict; in { \ + let \ + outPath = builtins.getAttr (output + \"Path\") strict; \ + drvPath = builtins.getAttr (output + \"DrvPath\") strict; \ + in { \ name = output; \ value = attrs // { \ - drvPath = strict.drvPath; \ - inherit outPath; \ + inherit outPath drvPath; \ type = \"derivation\"; \ currentOutput = output; \ } // outputsAttrs // { all = allList; }; \ From e81c09edbf6b352ec96668be35a68037df2f6342 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Fri, 16 Sep 2011 11:31:00 +0000 Subject: [PATCH 07/41] Remove the current output metadata from the string for unsaveDiscardOutputDependency --- src/libexpr/primops.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index a752b9a59..d5e1ce7be 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -974,7 +974,14 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args PathSet context2; foreach (PathSet::iterator, i, context) { Path p = *i; - if (p.at(0) == '=') p = "~" + string(p, 1); + if (p.at(0) == '=') + { + size_t index; + p = "~" + string(p, 1); + index = p.find("="); + if (index < p.find("/")) + p = "~" + string(p, index + 1); + } context2.insert(p); } From 6c38cc9025591655b893bbd2437bfd1453c0c2fa Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Mon, 19 Sep 2011 04:15:26 +0000 Subject: [PATCH 08/41] Ignore everything created during build From bffe35acedafcd7c7237cb1415798362bff8a180 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Mon, 19 Sep 2011 04:36:49 +0000 Subject: [PATCH 09/41] Update gitignore --- .gitignore | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) 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 From f883afa1a1bc6c48bbb3d9c150e357c35e40c921 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Sun, 6 Nov 2011 06:28:08 +0000 Subject: [PATCH 10/41] The nixinstantiate and nixstore env vars are no longer set in common.sh --- tests/multiple-outputs.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/multiple-outputs.sh b/tests/multiple-outputs.sh index f4cd01234..d3ebdbea3 100644 --- a/tests/multiple-outputs.sh +++ b/tests/multiple-outputs.sh @@ -2,11 +2,11 @@ source common.sh echo "Testing multiple outputs..." -drvPath=$($nixinstantiate multiple-outputs.nix) +drvPath=$(nix-instantiate multiple-outputs.nix) echo "derivation is $drvPath" -outPath=$($nixstore -rvv "$drvPath") +outPath=$(nix-store -rvv "$drvPath") echo "output path is $outPath" From 981edeab7b6b415c71d3421da6967ec7fc232e54 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Sun, 6 Nov 2011 06:28:14 +0000 Subject: [PATCH 11/41] The 'insert output between = signs' approach was not helpful --- src/libexpr/primops.cc | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index d5e1ce7be..2a3f1e2c3 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -456,7 +456,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) state.mkAttrs(v, 1 + drv.outputs.size()); foreach (DerivationOutputs::iterator, i, drv.outputs) { - mkString(*state.allocAttr(v, state.symbols.create(i->first + "DrvPath")), drvPath, singleton("=" + i->first + "=" + drvPath)); + mkString(*state.allocAttr(v, state.symbols.create(i->first + "DrvPath")), drvPath, singleton("=" + drvPath)); /* The output path of an output X is ‘Path’, e.g. ‘outPath’. */ mkString(*state.allocAttr(v, state.symbols.create(i->first + "Path")), @@ -974,14 +974,7 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args PathSet context2; foreach (PathSet::iterator, i, context) { Path p = *i; - if (p.at(0) == '=') - { - size_t index; - p = "~" + string(p, 1); - index = p.find("="); - if (index < p.find("/")) - p = "~" + string(p, index + 1); - } + p = "~" + string(p, 1); context2.insert(p); } From af2e53fd481994cca46b9c003a6a8eae50cf951c Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Sun, 6 Nov 2011 06:28:20 +0000 Subject: [PATCH 12/41] Include all outputs of derivations in the closure of explicitly-passed derivation paths This required adding a queryOutputDerivationNames function in the store API --- src/libexpr/primops.cc | 10 ++++++++-- src/libstore/local-store.cc | 22 ++++++++++++++++++++++ src/libstore/local-store.hh | 2 ++ src/libstore/remote-store.cc | 10 ++++++++++ src/libstore/remote-store.hh | 2 ++ src/libstore/store-api.hh | 3 +++ src/libstore/worker-protocol.hh | 1 + src/nix-worker/nix-worker.cc | 10 ++++++++++ 8 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 2a3f1e2c3..ce0b9e8b0 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -347,6 +347,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) derivation. */ foreach (PathSet::iterator, i, context) { Path path = *i; + bool explicitlyPassed = false; /* Paths marked with `=' denote that the path of a derivation is explicitly passed to the builder. Since that allows the @@ -361,8 +362,10 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) foreach (PathSet::iterator, j, refs) { drv.inputSrcs.insert(*j); if (isDerivation(*j)) - drv.inputDrvs[*j] = singleton("out"); + drv.inputDrvs[*j] = store -> queryDerivationOutputNames(*j); } + + explicitlyPassed = true; } /* See prim_unsafeDiscardOutputDependency. */ @@ -376,7 +379,10 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) debug(format("derivation uses `%1%'") % path); if (!useDrvAsSrc && isDerivation(path)) - drv.inputDrvs[path] = singleton("out"); + if (explicitlyPassed) + drv.inputDrvs[path] = store -> queryDerivationOutputNames(path); + else + drv.inputDrvs[path] = singleton("out"); else drv.inputSrcs.insert(path); } diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 702ff67e7..8ca5daa9f 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; diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 7ef01b264..b97e2f406 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/remote-store.cc b/src/libstore/remote-store.cc index 568a6aa58..84c87246f 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -322,6 +322,16 @@ PathSet RemoteStore::queryDerivationOutputs(const Path & path) } +PathSet RemoteStore::queryDerivationOutputNames(const Path & path) +{ + openConnection(); + writeInt(wopQueryDerivationOutputNames, to); + writeString(path, to); + processStderr(); + return readStringSet(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 519f46fd1..3be9e315a 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 b3e67436c..038465749 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; diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index acb8bc8b2..576e754d2 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -39,6 +39,7 @@ typedef enum { wopQueryFailedPaths = 24, wopClearFailedPaths = 25, wopQueryPathInfo = 26, + wopQueryDerivationOutputNames = 27, } WorkerOp; diff --git a/src/nix-worker/nix-worker.cc b/src/nix-worker/nix-worker.cc index 0fa1b40ae..3587bd7fd 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(); + writeStringSet(names, to); + break; + } + case wopQueryDeriver: { Path path = readStorePath(from); startWork(); From 46876ff2037541613dc17c986f9b68b8f257cb3b Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Sun, 6 Nov 2011 06:28:25 +0000 Subject: [PATCH 13/41] Fix stupid typo in multiple outputs test --- tests/multiple-outputs.b.builder.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/multiple-outputs.b.builder.sh b/tests/multiple-outputs.b.builder.sh index bc63fc068..acf939062 100644 --- a/tests/multiple-outputs.b.builder.sh +++ b/tests/multiple-outputs.b.builder.sh @@ -1,7 +1,7 @@ mkdir $out test "$firstOutput $secondOutput" = "$allOutputs" test "$defaultOutput" = "$firstOutput" -test "$(cat $first/file)" = "second" -test "$(cat $second/file)" = "first" +test "$(cat $firstOutput/file)" = "second" +test "$(cat $secondOutput/file)" = "first" echo "success" > $out/file From 3522730316dbb3a9ee5a690188f02435e7260406 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Sun, 6 Nov 2011 06:28:30 +0000 Subject: [PATCH 14/41] Embed output name into the context of the *OutPath attributes and extract it for input derivations Multiple outputs test passes! --- src/libexpr/primops.cc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index ce0b9e8b0..9c217b373 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -348,6 +348,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) foreach (PathSet::iterator, i, context) { Path path = *i; bool explicitlyPassed = false; + string output = "out"; /* Paths marked with `=' denote that the path of a derivation is explicitly passed to the builder. Since that allows the @@ -366,6 +367,12 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) } explicitlyPassed = true; + } else if (path.at(0) == '!') { + size_t index; + path = string(path, 1); + index = path.find("!"); + output = path.substr(0, index); + path = string(path, index + 1); } /* See prim_unsafeDiscardOutputDependency. */ @@ -382,7 +389,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) if (explicitlyPassed) drv.inputDrvs[path] = store -> queryDerivationOutputNames(path); else - drv.inputDrvs[path] = singleton("out"); + drv.inputDrvs[path] = singleton(output); else drv.inputSrcs.insert(path); } @@ -466,7 +473,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) /* 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)); + i->second.path, singleton("!" + i->first + "!" + drvPath)); } v.attrs->sort(); } From 24b65937e103c5cb1232c3cbcbffc12322cb8ae3 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Sun, 6 Nov 2011 06:28:34 +0000 Subject: [PATCH 15/41] Remove the unused sCurrentOutput symbol --- src/libexpr/eval.cc | 1 - src/libexpr/eval.hh | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 64e6cbe64..2b97b76fb 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -133,7 +133,6 @@ string showType(const Value & v) EvalState::EvalState() : sWith(symbols.create("")) , sOutPath(symbols.create("outPath")) - , sCurrentOutput(symbols.create("currentOutput")) , sDrvPath(symbols.create("drvPath")) , sType(symbols.create("type")) , sMeta(symbols.create("meta")) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 3816f36bd..694d4407b 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -204,7 +204,7 @@ public: SymbolTable symbols; const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, - sSystem, sOverrides, sCurrentOutput; + sSystem, sOverrides; private: SrcToStore srcToStore; From ca0d47a70c37999f8cc9c2e82c76661826cfd50a Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Sun, 6 Nov 2011 06:54:05 +0000 Subject: [PATCH 16/41] Respect all outputs passed to the derivation, not just the last one --- src/libexpr/primops.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 9c217b373..70560302e 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -388,8 +388,10 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) if (!useDrvAsSrc && isDerivation(path)) if (explicitlyPassed) drv.inputDrvs[path] = store -> queryDerivationOutputNames(path); - else + else if (drv.inputDrvs.find(path) == drv.inputDrvs.end()) drv.inputDrvs[path] = singleton(output); + else + drv.inputDrvs[path].insert(output); else drv.inputSrcs.insert(path); } From 2ab29be70c99483dbd8cf12eece4d553c7f953f3 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Sun, 6 Nov 2011 07:03:14 +0000 Subject: [PATCH 17/41] Fix faulty reversion of my changes to unsafeDiscardOutputDependency --- src/libexpr/primops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 70560302e..09a653002 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -989,7 +989,7 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args PathSet context2; foreach (PathSet::iterator, i, context) { Path p = *i; - p = "~" + string(p, 1); + if (p.at(0) == '=') p = "~" + string(p, 1); context2.insert(p); } From 3c3107da86ff71a08ce44027ee5899acf486796a Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Sun, 6 Nov 2011 07:18:19 +0000 Subject: [PATCH 18/41] There's no need to mess with drvPath at all --- src/libexpr/primops.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 09a653002..b11562baa 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -470,8 +470,8 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) drvHashes[drvPath] = hashDerivationModulo(*store, drv); state.mkAttrs(v, 1 + drv.outputs.size()); + mkString(*state.allocAttr(v, state.sDrvPath), drvPath, singleton("=" + drvPath)); foreach (DerivationOutputs::iterator, i, drv.outputs) { - mkString(*state.allocAttr(v, state.symbols.create(i->first + "DrvPath")), drvPath, singleton("=" + drvPath)); /* The output path of an output X is ‘Path’, e.g. ‘outPath’. */ mkString(*state.allocAttr(v, state.symbols.create(i->first + "Path")), @@ -1126,13 +1126,11 @@ void EvalState::createBaseEnv() attrValues = attrs: \ map (name: builtins.getAttr name attrs) (builtins.attrNames attrs); \ outputToAttrListElement = output: \ - let \ - outPath = builtins.getAttr (output + \"Path\") strict; \ - drvPath = builtins.getAttr (output + \"DrvPath\") strict; \ - in { \ + { \ name = output; \ value = attrs // { \ - inherit outPath drvPath; \ + outPath = builtins.getAttr (output + \"Path\") strict; \ + drvPath = strict.drvPath; \ type = \"derivation\"; \ currentOutput = output; \ } // outputsAttrs // { all = allList; }; \ From b5363810bbeea37df34a5cb0051e05161630a510 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 20 Dec 2011 16:37:01 +0000 Subject: [PATCH 19/41] * Fix the build. --- src/libstore/remote-store.cc | 2 +- src/nix-worker/nix-worker.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index ce99c205e..6e9921ede 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -332,7 +332,7 @@ PathSet RemoteStore::queryDerivationOutputNames(const Path & path) writeInt(wopQueryDerivationOutputNames, to); writeString(path, to); processStderr(); - return readStringSet(from); + return readStrings(from); } diff --git a/src/nix-worker/nix-worker.cc b/src/nix-worker/nix-worker.cc index 68567f341..f28905a24 100644 --- a/src/nix-worker/nix-worker.cc +++ b/src/nix-worker/nix-worker.cc @@ -337,7 +337,7 @@ static void performOp(unsigned int clientVersion, StringSet names; names = store->queryDerivationOutputNames(path); stopWork(); - writeStringSet(names, to); + writeStrings(names, to); break; } From 46e42c92c1444e1dd3aec871b3750bcd7391f60e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 20 Dec 2011 17:01:02 +0000 Subject: [PATCH 20/41] * Refactor a bit so that more tests can be added. --- tests/Makefile.am | 1 - tests/multiple-outputs.a.builder.sh | 6 ----- tests/multiple-outputs.b.builder.sh | 7 ------ tests/multiple-outputs.nix | 36 +++++++++++++++++++---------- tests/multiple-outputs.sh | 12 ++-------- 5 files changed, 26 insertions(+), 36 deletions(-) delete mode 100644 tests/multiple-outputs.a.builder.sh delete mode 100644 tests/multiple-outputs.b.builder.sh diff --git a/tests/Makefile.am b/tests/Makefile.am index 8b5aa4bd9..70352dbb5 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -36,7 +36,6 @@ EXTRA_DIST = $(TESTS) \ timeout.nix timeout.builder.sh \ secure-drv-outputs.nix \ multiple-outputs.nix \ - multiple-outputs.a.builder.sh multiple-outputs.b.builder.sh \ $(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.a.builder.sh b/tests/multiple-outputs.a.builder.sh deleted file mode 100644 index 657b7ea0a..000000000 --- a/tests/multiple-outputs.a.builder.sh +++ /dev/null @@ -1,6 +0,0 @@ -mkdir $first -mkdir $second -test -z $all - -echo "second" > $first/file -echo "first" > $second/file diff --git a/tests/multiple-outputs.b.builder.sh b/tests/multiple-outputs.b.builder.sh deleted file mode 100644 index acf939062..000000000 --- a/tests/multiple-outputs.b.builder.sh +++ /dev/null @@ -1,7 +0,0 @@ -mkdir $out -test "$firstOutput $secondOutput" = "$allOutputs" -test "$defaultOutput" = "$firstOutput" -test "$(cat $firstOutput/file)" = "second" -test "$(cat $secondOutput/file)" = "first" - -echo "success" > $out/file diff --git a/tests/multiple-outputs.nix b/tests/multiple-outputs.nix index e8fbf91bf..a4cf0caea 100644 --- a/tests/multiple-outputs.nix +++ b/tests/multiple-outputs.nix @@ -1,23 +1,35 @@ with import ./config.nix; -let +rec { a = mkDerivation { name = "multiple-outputs-a"; outputs = [ "first" "second" ]; - builder = ./multiple-outputs.a.builder.sh; + builder = builtins.toFile "builder.sh" + '' + mkdir $first $second + test -z $all + echo "second" > $first/file + echo "first" > $second/file + ''; helloString = "Hello, world!"; }; -in + b = mkDerivation { + defaultOutput = assert a.second.helloString == "Hello, world!"; a; + firstOutput = a.first.first; + secondOutput = 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 + ''; + }; -assert a.second.helloString == "Hello, world!"; - -mkDerivation { - defaultOutput = a; - firstOutput = a.first.first; - secondOutput = a.second.first.first.second.second.first.second; - allOutputs = a.all; - name = "multiple-outputs-b"; - builder = ./multiple-outputs.b.builder.sh; } diff --git a/tests/multiple-outputs.sh b/tests/multiple-outputs.sh index d3ebdbea3..dadd2e25f 100644 --- a/tests/multiple-outputs.sh +++ b/tests/multiple-outputs.sh @@ -2,14 +2,6 @@ source common.sh echo "Testing multiple outputs..." -drvPath=$(nix-instantiate multiple-outputs.nix) - -echo "derivation is $drvPath" - -outPath=$(nix-store -rvv "$drvPath") - +outPath=$(nix-build multiple-outputs.nix -A b) echo "output path is $outPath" - -text=$(cat "$outPath"/file) -if test "$text" != "success"; then exit 1; fi - +[ "$(cat "$outPath"/file)" = "success" ] From 1f3b0ede7d9b6292e054f8e5ecc52a83cbba1f09 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 20 Dec 2011 17:08:43 +0000 Subject: [PATCH 21/41] * Add a (currently failing) test that checks whether mutually recursive outputs are properly rejected. * Add a (also failing) test for "nix-build -A ". --- tests/multiple-outputs.nix | 11 +++++++++++ tests/multiple-outputs.sh | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/tests/multiple-outputs.nix b/tests/multiple-outputs.nix index a4cf0caea..7be96703d 100644 --- a/tests/multiple-outputs.nix +++ b/tests/multiple-outputs.nix @@ -32,4 +32,15 @@ rec { ''; }; + cyclic = (mkDerivation { + name = "cyclic-outputs"; + outputs = [ "a" "b" ]; + builder = builtins.toFile "builder.sh" + '' + mkdir $a $b + echo $a > $b/foo + echo $b > $a/bar + ''; + }).a; + } diff --git a/tests/multiple-outputs.sh b/tests/multiple-outputs.sh index dadd2e25f..d4185c55b 100644 --- a/tests/multiple-outputs.sh +++ b/tests/multiple-outputs.sh @@ -5,3 +5,14 @@ echo "Testing multiple outputs..." 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. +nix-build multiple-outputs.nix -A a.first + +# Cyclic outputs should be rejected. +if nix-build multiple-outputs.nix -A cyclic; then + echo "Cyclic outputs incorrectly accepted!" + exit 1 +fi + +clearStore From edd9359bebe012ae40e9e1538dab8f91e2d58d3e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 20 Dec 2011 17:10:39 +0000 Subject: [PATCH 22/41] * Doing a GC after building a derivation with cyclic outputs currently segfaults. --- tests/multiple-outputs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/multiple-outputs.sh b/tests/multiple-outputs.sh index d4185c55b..8a7ae2236 100644 --- a/tests/multiple-outputs.sh +++ b/tests/multiple-outputs.sh @@ -15,4 +15,4 @@ if nix-build multiple-outputs.nix -A cyclic; then exit 1 fi -clearStore +nix-store --gc From 179409b9112ee9d9f858a124e0b109a46fe835c9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 21 Dec 2011 13:47:21 +0000 Subject: [PATCH 23/41] =?UTF-8?q?*=20Add=20a=20test=20for=20referring=20to?= =?UTF-8?q?=20another=20derivation's=20=E2=80=98drvPath=E2=80=99.=20=20Thi?= =?UTF-8?q?s=20=20=20currently=20fails=20in=20read-only=20mode.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/multiple-outputs.nix | 10 ++++++++++ tests/multiple-outputs.sh | 20 +++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/multiple-outputs.nix b/tests/multiple-outputs.nix index 7be96703d..9ce1f3dcf 100644 --- a/tests/multiple-outputs.nix +++ b/tests/multiple-outputs.nix @@ -32,6 +32,16 @@ rec { ''; }; + c = mkDerivation { + name = "multiple-outputs-c"; + drv = b.drvPath; + builder = builtins.toFile "builder.sh" + '' + mkdir $out + ln -s $drv $out/drv + ''; + }; + cyclic = (mkDerivation { name = "cyclic-outputs"; outputs = [ "a" "b" ]; diff --git a/tests/multiple-outputs.sh b/tests/multiple-outputs.sh index 8a7ae2236..aa328fdbc 100644 --- a/tests/multiple-outputs.sh +++ b/tests/multiple-outputs.sh @@ -1,18 +1,36 @@ source common.sh -echo "Testing multiple outputs..." +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. +drvPath2=$(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 + +# 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 From 4be5a2c096083234e62117ce6016c4c9aa573aff Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 21 Dec 2011 14:42:06 +0000 Subject: [PATCH 24/41] * Add a test for unsafeDiscardOutputDependency. Not really related to multiple outputs, but good to have anyway. --- tests/multiple-outputs.nix | 10 ++++++++++ tests/multiple-outputs.sh | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/tests/multiple-outputs.nix b/tests/multiple-outputs.nix index 9ce1f3dcf..405999ce7 100644 --- a/tests/multiple-outputs.nix +++ b/tests/multiple-outputs.nix @@ -42,6 +42,16 @@ rec { ''; }; + 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" ]; diff --git a/tests/multiple-outputs.sh b/tests/multiple-outputs.sh index aa328fdbc..e95c6b404 100644 --- a/tests/multiple-outputs.sh +++ b/tests/multiple-outputs.sh @@ -14,6 +14,12 @@ drvPath2=$(nix-instantiate multiple-outputs.nix -A c) 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..." From b19a0f63dbb0c4910f4d9dcb391ca424ff3faeaa Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 21 Dec 2011 15:33:30 +0000 Subject: [PATCH 25/41] * Simplify the context handling logic. --- src/libexpr/primops.cc | 37 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index dc361c043..89e215f3b 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -347,8 +347,6 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) derivation. */ foreach (PathSet::iterator, i, context) { Path path = *i; - bool explicitlyPassed = false; - string output = "out"; /* Paths marked with `=' denote that the path of a derivation is explicitly passed to the builder. Since that allows the @@ -358,39 +356,30 @@ 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); + PathSet refs; computeFSClosure(*store, string(path, 1), refs); foreach (PathSet::iterator, j, refs) { drv.inputSrcs.insert(*j); if (isDerivation(*j)) drv.inputDrvs[*j] = store->queryDerivationOutputNames(*j); } - explicitlyPassed = true; - } else if (path.at(0) == '!') { - size_t index; - path = string(path, 1); - index = path.find("!"); - output = path.substr(0, index); - path = string(path, index + 1); } /* 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)) - if (explicitlyPassed) - drv.inputDrvs[path] = store->queryDerivationOutputNames(path); - else if (drv.inputDrvs.find(path) == drv.inputDrvs.end()) - drv.inputDrvs[path] = singleton(output); - else - drv.inputDrvs[path].insert(output); + /* Otherwise it's a source file. */ else drv.inputSrcs.insert(path); } From f3c88f297d837f73d8123cb12564d237d7d0df87 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Wed, 21 Dec 2011 17:14:28 +0000 Subject: [PATCH 26/41] Detect and reject mutually-referential outputs There is probably a more efficient way to do this. --- src/libstore/build.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/libstore/build.cc b/src/libstore/build.cc index a8ef9b23e..2ebcbf5a8 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -1986,6 +1986,15 @@ void DerivationGoal::computeClosure() info.deriver = drvPath; infos.push_back(info); } + + /* Mutually recursive outputs are not allowed */ + foreach (ValidPathInfos::iterator, i, infos) + foreach (ValidPathInfos::iterator, j, infos) + if ((i->path != j->path) && + (i->references.find(j->path) != i->references.end()) && + (j->references.find(i->path) != j->references.end())) + throw BuildError(format("Mutually referential outputs are not allowed: outputs `%1%' and `%2%' refer to each other") % i->path % j->path); + worker.store.registerValidPaths(infos); /* It is now safe to delete the lock files, since all future From b4cee3f816ad53f9762f38c28b51a78732b249f2 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Wed, 21 Dec 2011 17:31:34 +0000 Subject: [PATCH 27/41] Revert previous commit It doesn't detect indirect references --- src/libstore/build.cc | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 2ebcbf5a8..a8ef9b23e 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -1986,15 +1986,6 @@ void DerivationGoal::computeClosure() info.deriver = drvPath; infos.push_back(info); } - - /* Mutually recursive outputs are not allowed */ - foreach (ValidPathInfos::iterator, i, infos) - foreach (ValidPathInfos::iterator, j, infos) - if ((i->path != j->path) && - (i->references.find(j->path) != i->references.end()) && - (j->references.find(i->path) != j->references.end())) - throw BuildError(format("Mutually referential outputs are not allowed: outputs `%1%' and `%2%' refer to each other") % i->path % j->path); - worker.store.registerValidPaths(infos); /* It is now safe to delete the lock files, since all future From f8e54b7874b73891e39aff11dac2a5ceabef2f02 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Wed, 21 Dec 2011 17:34:44 +0000 Subject: [PATCH 28/41] Make the reference cycle in the cyclic outputs test indirect --- tests/multiple-outputs.nix | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/multiple-outputs.nix b/tests/multiple-outputs.nix index 405999ce7..09946137d 100644 --- a/tests/multiple-outputs.nix +++ b/tests/multiple-outputs.nix @@ -54,12 +54,13 @@ rec { cyclic = (mkDerivation { name = "cyclic-outputs"; - outputs = [ "a" "b" ]; + outputs = [ "a" "b" "c" ]; builder = builtins.toFile "builder.sh" '' - mkdir $a $b + mkdir $a $b $c echo $a > $b/foo - echo $b > $a/bar + echo $b > $c/bar + echo $c > $a/baz ''; }).a; From 56790411323eada03bacf37fe6fd328a7c84d32a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 21 Dec 2011 18:19:05 +0000 Subject: [PATCH 29/41] =?UTF-8?q?*=20The=20=E2=80=98foo.drvPath=E2=80=99?= =?UTF-8?q?=20feature=20was=20already=20broken=20in=20read-only=20mode.=20?= =?UTF-8?q?=20=20Since=20it's=20rarely=20used=20and=20fixing=20it=20is=20t?= =?UTF-8?q?oo=20much=20work=20right=20now,=20=20=20just=20document=20it.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/libexpr/primops.cc | 3 ++- tests/multiple-outputs.sh | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 89e215f3b..ca7766487 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -356,6 +356,7 @@ 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) == '=') { + /* !!! 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); @@ -377,7 +378,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) /* Handle derivation contexts returned by ‘builtins.storePath’. */ else if (isDerivation(path)) - drv.inputDrvs[path] = store->queryDerivationOutputNames(path); + drv.inputDrvs[path] = store->queryDerivationOutputNames(path); /* Otherwise it's a source file. */ else diff --git a/tests/multiple-outputs.sh b/tests/multiple-outputs.sh index e95c6b404..20f3380a1 100644 --- a/tests/multiple-outputs.sh +++ b/tests/multiple-outputs.sh @@ -5,12 +5,12 @@ 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) +#drvPath=$(nix-instantiate multiple-outputs.nix -A c --readonly-mode) # And check whether the resulting derivation explicitly depends on all # outputs. -drvPath2=$(nix-instantiate multiple-outputs.nix -A c) -[ "$drvPath" = "$drvPath2" ] +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 From b1004f40f7e4dd02feec2d0cb26bd6c95dd66dde Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 30 Dec 2011 14:47:14 +0000 Subject: [PATCH 30/41] * Reject a build if there is a cycle among the outputs. This is necessary because existing code assumes that the references graph is acyclic. --- src/libstore/build.cc | 7 ++----- src/libstore/gc.cc | 14 ++++++++++---- src/libstore/local-store.cc | 8 ++++++++ src/libstore/store-api.hh | 4 ++++ 4 files changed, 24 insertions(+), 9 deletions(-) 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..ec7751133 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -372,8 +372,13 @@ static void addAdditionalRoots(StoreAPI & store, PathSet & roots) static void dfsVisit(StoreAPI & store, const PathSet & paths, - const Path & path, PathSet & visited, Paths & sorted) + 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); + parents.insert(path); + if (visited.find(path) != visited.end()) return; visited.insert(path); @@ -385,18 +390,19 @@ static void dfsVisit(StoreAPI & store, const PathSet & paths, /* 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); + 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; + PathSet visited, parents; foreach (PathSet::const_iterator, i, paths) - dfsVisit(store, paths, *i, visited, sorted); + dfsVisit(store, paths, *i, visited, sorted, parents); return sorted; } diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 29817df9d..771776f6a 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -966,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) { @@ -980,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/store-api.hh b/src/libstore/store-api.hh index 14890f522..61bcaf505 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -349,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 */ + + } From 6f5e3326cef2a2049c8f4ea757accafe4fc9d53f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 30 Dec 2011 15:02:50 +0000 Subject: [PATCH 31/41] * Move topoSortPaths() out of gc.cc. --- src/libstore/gc.cc | 36 ------------------------------------ src/libstore/misc.cc | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index ec7751133..14c8ba0bf 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -371,42 +371,6 @@ static void addAdditionalRoots(StoreAPI & store, PathSet & roots) } -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); - parents.insert(path); - - 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, 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; -} - - struct GCLimitReached { }; diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index d4bbccd11..abe59d162 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); + parents.insert(path); + + 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, 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; +} + + } From a71d02440b03cdb5dad6e43f786c0cc86cbb87b1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 30 Dec 2011 17:13:25 +0000 Subject: [PATCH 32/41] * Oops. --- src/libstore/misc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index abe59d162..4ac0afe84 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -103,10 +103,10 @@ static void dfsVisit(StoreAPI & store, const PathSet & paths, { if (parents.find(path) != parents.end()) throw BuildError(format("cycle detected in the references of `%1%'") % path); - parents.insert(path); if (visited.find(path) != visited.end()) return; visited.insert(path); + parents.insert(path); PathSet references; if (store.isValidPath(path)) From 93b56acb2dd90e106413f201c8025d2ffeba98e0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 30 Dec 2011 17:25:19 +0000 Subject: [PATCH 33/41] =?UTF-8?q?*=20Support=20multiple=20outputs=20in=20n?= =?UTF-8?q?ix-store=20(specifically=20the=20=E2=80=98--query=E2=80=99=20?= =?UTF-8?q?=20=20and=20=E2=80=98--realise=E2=80=99=20actions).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/nix-store/nix-store.cc | 106 ++++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 43 deletions(-) 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()) From 502d94048ae848eda1fcda2d1e72b339eaa653aa Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 3 Jan 2012 12:59:00 +0000 Subject: [PATCH 34/41] * Ignore missing manifest symlinks. --- perl/lib/Nix/Manifest.pm | 1 + 1 file changed, 1 insertion(+) 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; From 921111d1972388a0aa1841c545c753cb996c9257 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 3 Jan 2012 14:01:47 +0000 Subject: [PATCH 35/41] =?UTF-8?q?*=20Move=20the=20implementation=20of=20th?= =?UTF-8?q?e=20=E2=80=98derivation=E2=80=99=20primop=20into=20a=20separate?= =?UTF-8?q?=20=20=20file.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- corepkgs/Makefile.am | 2 +- corepkgs/derivation.nix | 31 +++++++++++++++++++++++++++++++ src/libexpr/eval.cc | 4 ++-- src/libexpr/primops.cc | 24 +----------------------- 4 files changed, 35 insertions(+), 26 deletions(-) create mode 100644 corepkgs/derivation.nix 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..0e16ad6fa --- /dev/null +++ b/corepkgs/derivation.nix @@ -0,0 +1,31 @@ +attrs: + +let + + strict = derivationStrict attrs; + + attrValues = attrs: + map (name: builtins.getAttr name attrs) (builtins.attrNames attrs); + + outputToAttrListElement = output: + { name = output; + value = attrs // { + outPath = builtins.getAttr (output + "Path") strict; + drvPath = strict.drvPath; + type = "derivation"; + currentOutput = output; + } // outputsAttrs // { all = allList; }; + }; + + outputsList = + if attrs ? outputs + then map outputToAttrListElement attrs.outputs + else [ (outputToAttrListElement "out") ]; + + outputsAttrs = builtins.listToAttrs outputsList; + + allList = attrValues outputsAttrs; + + head = if attrs ? outputs then builtins.head attrs.outputs else "out"; + +in builtins.getAttr head outputsAttrs 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 ca7766487..02c444cd6 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1099,29 +1099,7 @@ void EvalState::createBaseEnv() /* Add a wrapper around the derivation primop that computes the `drvPath' and `outPath' attributes lazily. */ - string s = "attrs: \ - let \ - strict = derivationStrict attrs; \ - attrValues = attrs: \ - map (name: builtins.getAttr name attrs) (builtins.attrNames attrs); \ - outputToAttrListElement = output: \ - { \ - name = output; \ - value = attrs // { \ - outPath = builtins.getAttr (output + \"Path\") strict; \ - drvPath = strict.drvPath; \ - type = \"derivation\"; \ - currentOutput = output; \ - } // outputsAttrs // { all = allList; }; \ - }; \ - outputsList = if attrs ? outputs then \ - map outputToAttrListElement attrs.outputs else \ - [ (outputToAttrListElement \"out\") ]; \ - outputsAttrs = builtins.listToAttrs outputsList; \ - allList = attrValues outputsAttrs; \ - head = if attrs ? outputs then builtins.head attrs.outputs else \"out\"; \ - in builtins.getAttr head outputsAttrs"; - mkThunk_(v, parseExprFromString(s, "/")); + mkThunk_(v, parseExprFromFile(findFile("nix/derivation.nix"))); addConstant("derivation", v); /* Now that we've added all primops, sort the `builtins' attribute From 71f3c46cf65c0638946c9bb57c36a2b5f177a672 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 3 Jan 2012 15:27:18 +0000 Subject: [PATCH 36/41] * Drop the inefficient "Path" suffix in output attribute names. --- corepkgs/derivation.nix | 8 ++++---- src/libexpr/primops.cc | 4 +--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/corepkgs/derivation.nix b/corepkgs/derivation.nix index 0e16ad6fa..d5044a83b 100644 --- a/corepkgs/derivation.nix +++ b/corepkgs/derivation.nix @@ -7,13 +7,13 @@ let attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames attrs); - outputToAttrListElement = output: - { name = output; + outputToAttrListElement = outputName: + { name = outputName; value = attrs // { - outPath = builtins.getAttr (output + "Path") strict; + outPath = builtins.getAttr outputName strict; drvPath = strict.drvPath; type = "derivation"; - currentOutput = output; + currentOutput = outputName; } // outputsAttrs // { all = allList; }; }; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 02c444cd6..21c9fb351 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -451,9 +451,7 @@ 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")), + mkString(*state.allocAttr(v, state.symbols.create(i->first)), i->second.path, singleton("!" + i->first + "!" + drvPath)); } v.attrs->sort(); From 83647f4ef14f1ecf40b9fc6099fc77864b87cf41 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 4 Jan 2012 11:04:19 +0000 Subject: [PATCH 37/41] * Simplify the implementation of "derivation" a bit: lift out the common attribution so that they're evaluated only once, etc. Note that the default output is now the first element of the "outputs" attribute, rather than the first element of the sorted list of outputs. This seems more user-friendly. --- corepkgs/derivation.nix | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/corepkgs/derivation.nix b/corepkgs/derivation.nix index d5044a83b..157a1647e 100644 --- a/corepkgs/derivation.nix +++ b/corepkgs/derivation.nix @@ -1,31 +1,24 @@ -attrs: +/* This is the implementation of the ‘derivation’ builtin function. + It's actually a wrapper around the ‘derivationStrict’ primop. */ + +drvAttrs @ { outputs ? [ "out" ], ... }: let - strict = derivationStrict attrs; + strict = derivationStrict drvAttrs; - attrValues = attrs: - map (name: builtins.getAttr name attrs) (builtins.attrNames attrs); - + commonAttrs = drvAttrs // (builtins.listToAttrs outputsList) // { all = map (x: x.value) outputsList; }; + outputToAttrListElement = outputName: { name = outputName; - value = attrs // { + value = commonAttrs // { outPath = builtins.getAttr outputName strict; drvPath = strict.drvPath; type = "derivation"; currentOutput = outputName; - } // outputsAttrs // { all = allList; }; + }; }; - outputsList = - if attrs ? outputs - then map outputToAttrListElement attrs.outputs - else [ (outputToAttrListElement "out") ]; + outputsList = map outputToAttrListElement outputs; - outputsAttrs = builtins.listToAttrs outputsList; - - allList = attrValues outputsAttrs; - - head = if attrs ? outputs then builtins.head attrs.outputs else "out"; - -in builtins.getAttr head outputsAttrs +in (builtins.head outputsList).value From b79b85ad7668783391538fe2cda2e896f4ea0c8e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 4 Jan 2012 11:56:15 +0000 Subject: [PATCH 38/41] =?UTF-8?q?*=20Export=20the=20original=20input=20att?= =?UTF-8?q?ributes=20of=20the=20derivation=20in=20=20=20=E2=80=98drvAttrs?= =?UTF-8?q?=E2=80=99.=20=20This=20will=20simplify=20the=20implementation?= =?UTF-8?q?=20of=20functions=20such=20=20=20as=20=E2=80=98overrideDerivati?= =?UTF-8?q?on=E2=80=99=20in=20Nixpkgs,=20which=20need=20to=20filter=20out?= =?UTF-8?q?=20any=20=20=20added=20attributes=20such=20as=20outPath.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- corepkgs/derivation.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/corepkgs/derivation.nix b/corepkgs/derivation.nix index 157a1647e..2e2d0002c 100644 --- a/corepkgs/derivation.nix +++ b/corepkgs/derivation.nix @@ -7,7 +7,10 @@ let strict = derivationStrict drvAttrs; - commonAttrs = drvAttrs // (builtins.listToAttrs outputsList) // { all = map (x: x.value) outputsList; }; + commonAttrs = drvAttrs // (builtins.listToAttrs outputsList) // + { all = map (x: x.value) outputsList; + inherit drvAttrs; + }; outputToAttrListElement = outputName: { name = outputName; From a0477a458fa9fd7fbe090ff8cebcfd162c090b43 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 4 Jan 2012 12:00:39 +0000 Subject: [PATCH 39/41] * currentOutput -> outputName. "current" implies some temporal aspect. --- corepkgs/derivation.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/corepkgs/derivation.nix b/corepkgs/derivation.nix index 2e2d0002c..757108be3 100644 --- a/corepkgs/derivation.nix +++ b/corepkgs/derivation.nix @@ -18,7 +18,7 @@ let outPath = builtins.getAttr outputName strict; drvPath = strict.drvPath; type = "derivation"; - currentOutput = outputName; + inherit outputName; }; }; From 9d43a0238235cfbe914e2051db89095699a2df95 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 4 Jan 2012 12:45:40 +0000 Subject: [PATCH 40/41] * Let --disable-gc work. --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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.]) From 9936da6b546d1ce643eca21ac76c6e7d568de1c2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 4 Jan 2012 12:45:53 +0000 Subject: [PATCH 41/41] * Check whether the outputName attribute works. --- tests/multiple-outputs.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/multiple-outputs.nix b/tests/multiple-outputs.nix index 09946137d..6add7dd6f 100644 --- a/tests/multiple-outputs.nix +++ b/tests/multiple-outputs.nix @@ -17,8 +17,8 @@ rec { b = mkDerivation { defaultOutput = assert a.second.helloString == "Hello, world!"; a; - firstOutput = a.first.first; - secondOutput = a.second.first.first.second.second.first.second; + 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"