aboutsummaryrefslogtreecommitdiff
path: root/src/libstore
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2005-01-17 16:55:19 +0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2005-01-17 16:55:19 +0000
commitf3dc2312501358e29b70bec977fd96f626c46392 (patch)
tree256dc28c596b8d4639c96c4fe66491b8704639d0 /src/libstore
parentd58a11e019813902b6c4547ca61a127938b2cc20 (diff)
* Removed the `id' attribute hack.
* Formalise the notion of fixed-output derivations, i.e., derivations for which a cryptographic hash of the output is known in advance. Changes to such derivations should not propagate upwards through the dependency graph. Previously this was done by specifying the hash component of the output path through the `id' attribute, but this is insecure since you can lie about it (i.e., you can specify any hash and then produce a completely different output). Now the responsibility for checking the output is moved from the builder to Nix itself. A fixed-output derivation can be created by specifying the `outputHash' and `outputHashAlgo' attributes, the latter taking values `md5', `sha1', and `sha256', and the former specifying the actual hash in hexadecimal or in base-32 (auto-detected by looking at the length of the attribute value). MD5 is included for compatibility but should be considered deprecated. * Removed the `drvPath' pseudo-attribute in derivation results. It's no longer necessary. * Cleaned up the support for multiple output paths in derivation store expressions. Each output now has a unique identifier (e.g., `out', `devel', `docs'). Previously there was no way to tell output paths apart at the store expression level. * `nix-hash' now has a flag `--base32' to specify that the hash should be printed in base-32 notation. * `fetchurl' accepts parameters `sha256' and `sha1' in addition to `md5'. * `nix-prefetch-url' now prints out a SHA-1 hash in base-32. (TODO: a flag to specify the hash.)
Diffstat (limited to 'src/libstore')
-rw-r--r--src/libstore/misc.cc5
-rw-r--r--src/libstore/normalise.cc59
-rw-r--r--src/libstore/store.cc10
-rw-r--r--src/libstore/store.hh2
-rw-r--r--src/libstore/storeexpr-ast.def1
-rw-r--r--src/libstore/storeexpr.cc37
-rw-r--r--src/libstore/storeexpr.hh22
7 files changed, 103 insertions, 33 deletions
diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc
index d7c32336e..6dc054fb4 100644
--- a/src/libstore/misc.cc
+++ b/src/libstore/misc.cc
@@ -20,8 +20,9 @@ PathSet storeExprRoots(const Path & nePath)
if (ne.type == StoreExpr::neClosure)
paths.insert(ne.closure.roots.begin(), ne.closure.roots.end());
else if (ne.type == StoreExpr::neDerivation)
- paths.insert(ne.derivation.outputs.begin(),
- ne.derivation.outputs.end());
+ for (DerivationOutputs::iterator i = ne.derivation.outputs.begin();
+ i != ne.derivation.outputs.end(); ++i)
+ paths.insert(i->second.path);
else abort();
return paths;
diff --git a/src/libstore/normalise.cc b/src/libstore/normalise.cc
index 7907325a9..b016d8d35 100644
--- a/src/libstore/normalise.cc
+++ b/src/libstore/normalise.cc
@@ -702,6 +702,29 @@ static void drain(int fd)
}
+PathSet outputPaths(const DerivationOutputs & outputs)
+{
+ PathSet paths;
+ for (DerivationOutputs::const_iterator i = outputs.begin();
+ i != outputs.end(); ++i)
+ paths.insert(i->second.path);
+ return paths;
+}
+
+
+string showPaths(const PathSet & paths)
+{
+ string s;
+ for (PathSet::const_iterator i = paths.begin();
+ i != paths.end(); ++i)
+ {
+ if (s.size() != 0) s += ", ";
+ s += *i;
+ }
+ return s;
+}
+
+
NormalisationGoal::HookReply NormalisationGoal::tryBuildHook()
{
Path buildHook = getEnv("NIX_BUILD_HOOK");
@@ -786,7 +809,7 @@ NormalisationGoal::HookReply NormalisationGoal::tryBuildHook()
}
printMsg(lvlInfo, format("running hook to build path `%1%'")
- % *expr.derivation.outputs.begin());
+ % showPaths(outputPaths(expr.derivation.outputs)));
/* Write the information that the hook needs to perform the
build, i.e., the set of input paths (including closure
@@ -807,9 +830,9 @@ NormalisationGoal::HookReply NormalisationGoal::tryBuildHook()
writeStringToFile(inputListFN, s);
s = "";
- for (PathSet::iterator i = expr.derivation.outputs.begin();
+ for (DerivationOutputs::iterator i = expr.derivation.outputs.begin();
i != expr.derivation.outputs.end(); ++i)
- s += *i + "\n";
+ s += i->second.path + "\n";
writeStringToFile(outputListFN, s);
s = "";
@@ -848,7 +871,7 @@ bool NormalisationGoal::prepareBuild()
/* Obtain locks on all output paths. The locks are automatically
released when we exit this function or Nix crashes. */
/* !!! BUG: this could block, which is not allowed. */
- outputLocks.lockPaths(expr.derivation.outputs);
+ outputLocks.lockPaths(outputPaths(expr.derivation.outputs));
/* Now check again whether there is a successor. This is because
another process may have started building in parallel. After
@@ -870,11 +893,11 @@ bool NormalisationGoal::prepareBuild()
running the build hook. */
/* The outputs are referenceable paths. */
- for (PathSet::iterator i = expr.derivation.outputs.begin();
+ for (DerivationOutputs::iterator i = expr.derivation.outputs.begin();
i != expr.derivation.outputs.end(); ++i)
{
- debug(format("building path `%1%'") % *i);
- allPaths.insert(*i);
+ debug(format("building path `%1%'") % i->second.path);
+ allPaths.insert(i->second.path);
}
/* Get information about the inputs (these all exist now). */
@@ -901,9 +924,9 @@ bool NormalisationGoal::prepareBuild()
/* We can skip running the builder if all output paths are already
valid. */
bool fastBuild = true;
- for (PathSet::iterator i = expr.derivation.outputs.begin();
+ for (DerivationOutputs::iterator i = expr.derivation.outputs.begin();
i != expr.derivation.outputs.end(); ++i)
- if (!isValidPath(*i)) {
+ if (!isValidPath(i->second.path)) {
fastBuild = false;
break;
}
@@ -921,7 +944,7 @@ bool NormalisationGoal::prepareBuild()
void NormalisationGoal::startBuilder()
{
startNest(nest, lvlInfo,
- format("building path `%1%'") % *expr.derivation.outputs.begin());
+ format("building path `%1%'") % showPaths(outputPaths(expr.derivation.outputs)))
/* Right platform? */
if (expr.derivation.platform != thisSystem)
@@ -931,10 +954,10 @@ void NormalisationGoal::startBuilder()
/* If any of the outputs already exist but are not registered,
delete them. */
- for (PathSet::iterator i = expr.derivation.outputs.begin();
+ for (DerivationOutputs::iterator i = expr.derivation.outputs.begin();
i != expr.derivation.outputs.end(); ++i)
{
- Path path = *i;
+ Path path = i->second.path;
if (isValidPath(path))
throw Error(format("obstructed build: path `%1%' exists") % path);
if (pathExists(path)) {
@@ -1054,10 +1077,10 @@ void NormalisationGoal::createClosure()
output path to determine what other paths it references. Also make all
output paths read-only. */
PathSet usedPaths;
- for (PathSet::iterator i = expr.derivation.outputs.begin();
+ for (DerivationOutputs::iterator i = expr.derivation.outputs.begin();
i != expr.derivation.outputs.end(); ++i)
{
- Path path = *i;
+ Path path = i->second.path;
if (!pathExists(path)) {
throw BuildError(
format("builder for `%1%' failed to produce output path `%2%'")
@@ -1084,6 +1107,7 @@ void NormalisationGoal::createClosure()
/* For each path referenced by this output path, add its id to the
closure element and add the id to the `usedPaths' set (so that the
elements referenced by *its* closure are added below). */
+ PathSet outputPaths = ::outputPaths(expr.derivation.outputs);
for (Paths::iterator j = refPaths.begin();
j != refPaths.end(); ++j)
{
@@ -1092,8 +1116,7 @@ void NormalisationGoal::createClosure()
elem.refs.insert(path);
if (inClosures.find(path) != inClosures.end())
usedPaths.insert(path);
- else if (expr.derivation.outputs.find(path) ==
- expr.derivation.outputs.end())
+ else if (outputPaths.find(path) == outputPaths.end())
abort();
}
@@ -1147,9 +1170,9 @@ void NormalisationGoal::createClosure()
by running the garbage collector. */
Transaction txn;
createStoreTransaction(txn);
- for (PathSet::iterator i = expr.derivation.outputs.begin();
+ for (DerivationOutputs::iterator i = expr.derivation.outputs.begin();
i != expr.derivation.outputs.end(); ++i)
- registerValidPath(txn, *i);
+ registerValidPath(txn, i->second.path);
registerSuccessor(txn, nePath, nfPath);
txn.commit();
diff --git a/src/libstore/store.cc b/src/libstore/store.cc
index e490bf258..0d89f7a5d 100644
--- a/src/libstore/store.cc
+++ b/src/libstore/store.cc
@@ -412,14 +412,14 @@ static void invalidatePath(const Path & path, Transaction & txn)
Path makeStorePath(const string & type,
- Hash & hash, const string & suffix)
+ const Hash & hash, const string & suffix)
{
/* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
string s = type + ":sha256:" + printHash(hash) + ":"
+ nixStore + ":" + suffix;
return nixStore + "/"
- + printHash32(compressHash(hashString(s, htSHA256), 20))
+ + printHash32(compressHash(hashString(htSHA256, s), 20))
+ "-" + suffix;
}
@@ -432,7 +432,7 @@ Path addToStore(const Path & _srcPath)
Hash h(htSHA256);
{
SwitchToOriginalUser sw;
- h = hashPath(srcPath, htSHA256);
+ h = hashPath(htSHA256, srcPath);
}
string baseName = baseNameOf(srcPath);
@@ -456,7 +456,7 @@ Path addToStore(const Path & _srcPath)
copyPath(srcPath, dstPath);
- Hash h2 = hashPath(dstPath, htSHA256);
+ Hash h2 = hashPath(htSHA256, dstPath);
if (h != h2)
throw Error(format("contents of `%1%' changed while copying it to `%2%' (%3% -> %4%)")
% srcPath % dstPath % printHash(h) % printHash(h2));
@@ -477,7 +477,7 @@ Path addToStore(const Path & _srcPath)
Path addTextToStore(const string & suffix, const string & s)
{
- Hash hash = hashString(s, htSHA256);
+ Hash hash = hashString(htSHA256, s);
Path dstPath = makeStorePath("text", hash, suffix);
diff --git a/src/libstore/store.hh b/src/libstore/store.hh
index 25a6bc8b9..c27918cbb 100644
--- a/src/libstore/store.hh
+++ b/src/libstore/store.hh
@@ -83,7 +83,7 @@ bool isValidPath(const Path & path);
/* Constructs a unique store path name. */
Path makeStorePath(const string & type,
- Hash & hash, const string & suffix);
+ const Hash & hash, const string & suffix);
/* Copy the contents of a path to the store and register the validity
the resulting path. The resulting path is returned. */
diff --git a/src/libstore/storeexpr-ast.def b/src/libstore/storeexpr-ast.def
index 9d2433dbe..0c70948d4 100644
--- a/src/libstore/storeexpr-ast.def
+++ b/src/libstore/storeexpr-ast.def
@@ -5,3 +5,4 @@ Derive | ATermList ATermList string string ATermList ATermList | ATerm |
| string string | ATerm | EnvBinding |
| string ATermList | ATerm | ClosureElem |
+| string string string string | ATerm | DerivationOutput |
diff --git a/src/libstore/storeexpr.cc b/src/libstore/storeexpr.cc
index de29959ed..d8300a066 100644
--- a/src/libstore/storeexpr.cc
+++ b/src/libstore/storeexpr.cc
@@ -8,7 +8,7 @@
Hash hashTerm(ATerm t)
{
- return hashString(atPrint(t), htMD5);
+ return hashString(htSHA256, atPrint(t));
}
@@ -20,14 +20,20 @@ Path writeTerm(ATerm t, const string & suffix)
}
+void checkPath(const string & s)
+{
+ if (s.size() == 0 || s[0] != '/')
+ throw Error(format("bad path `%1%' in store expression") % s);
+}
+
+
static void parsePaths(ATermList paths, PathSet & out)
{
for (ATermIterator i(paths); i; ++i) {
if (ATgetType(*i) != AT_APPL)
throw badTerm("not a path", *i);
string s = aterm2String(*i);
- if (s.size() == 0 || s[0] != '/')
- throw badTerm("not a path", *i);
+ checkPath(s);
out.insert(s);
}
}
@@ -92,7 +98,18 @@ static bool parseDerivation(ATerm t, Derivation & derivation)
if (!matchDerive(t, outs, ins, platform, builder, args, bnds))
return false;
- parsePaths(outs, derivation.outputs);
+ for (ATermIterator i(outs); i; ++i) {
+ ATerm id, path, hashAlgo, hash;
+ if (!matchDerivationOutput(*i, id, path, hashAlgo, hash))
+ return false;
+ DerivationOutput out;
+ out.path = aterm2String(path);
+ checkPath(out.path);
+ out.hashAlgo = aterm2String(hashAlgo);
+ out.hash = aterm2String(hash);
+ derivation.outputs[aterm2String(id)] = out;
+ }
+
parsePaths(ins, derivation.inputs);
derivation.builder = aterm2String(builder);
@@ -155,6 +172,16 @@ static ATerm unparseClosure(const Closure & closure)
static ATerm unparseDerivation(const Derivation & derivation)
{
+ ATermList outputs = ATempty;
+ for (DerivationOutputs::const_iterator i = derivation.outputs.begin();
+ i != derivation.outputs.end(); i++)
+ outputs = ATinsert(outputs,
+ makeDerivationOutput(
+ toATerm(i->first),
+ toATerm(i->second.path),
+ toATerm(i->second.hashAlgo),
+ toATerm(i->second.hash)));
+
ATermList args = ATempty;
for (Strings::const_iterator i = derivation.args.begin();
i != derivation.args.end(); i++)
@@ -169,7 +196,7 @@ static ATerm unparseDerivation(const Derivation & derivation)
toATerm(i->second)));
return makeDerive(
- unparsePaths(derivation.outputs),
+ ATreverse(outputs),
unparsePaths(derivation.inputs),
toATerm(derivation.platform),
toATerm(derivation.builder),
diff --git a/src/libstore/storeexpr.hh b/src/libstore/storeexpr.hh
index 07676c3cc..d8b8b2a96 100644
--- a/src/libstore/storeexpr.hh
+++ b/src/libstore/storeexpr.hh
@@ -20,12 +20,30 @@ struct Closure
ClosureElems elems;
};
+
+struct DerivationOutput
+{
+ Path path;
+ string hashAlgo; /* hash used for expected hash computation */
+ string hash; /* expected hash, may be null */
+ DerivationOutput()
+ {
+ }
+ DerivationOutput(Path path, string hashAlgo, string hash)
+ {
+ this->path = path;
+ this->hashAlgo = hashAlgo;
+ this->hash = hash;
+ }
+};
+
+typedef map<string, DerivationOutput> DerivationOutputs;
typedef map<string, string> StringPairs;
struct Derivation
{
- PathSet outputs;
- PathSet inputs; /* Store expressions, not actual inputs */
+ DerivationOutputs outputs; /* keyed on symbolic IDs */
+ PathSet inputs; /* store expressions, not actual inputs */
string platform;
Path builder;
Strings args;