diff --git a/corepkgs/nar/unnar.fix b/corepkgs/nar/unnar.fix index db97750aa..315be5873 100644 --- a/corepkgs/nar/unnar.fix +++ b/corepkgs/nar/unnar.fix @@ -3,6 +3,7 @@ Function(["nar", "name"], [ ("name", Var("name")) , ("build", Relative("nar/unnar.sh")) , ("nar", Var("nar")) + , ("id", Var("id")) ] ) ) \ No newline at end of file diff --git a/scripts/nix-pull.in b/scripts/nix-pull.in index 9a1c1b6b5..f584b6abd 100644 --- a/scripts/nix-pull.in +++ b/scripts/nix-pull.in @@ -28,10 +28,18 @@ while () { my $fn = $1; next if $fn =~ /\.\./; next if $fn =~ /\//; - next unless $fn =~ /^([0-9a-z]{32})-([0-9a-z]{32})(-s-([0-9a-z]{32}))?.*\.nar\.bz2$/; + next unless $fn =~ /^([0-9a-z]{32})-([0-9a-z]{32})(.*)\.nar\.bz2$/; my $hash = $1; - my $id = $2; - my $fsid = $4; + my $id = $2; + my $outname = $3; + my $fsid; + if ($outname =~ /^-/) { + next unless $outname =~ /^-((s-([0-9a-z]{32}))?.*)$/; + $outname = $1; + $fsid = $3; + } else { + $outname = ""; + } print "registering $id -> $url/$fn\n"; @@ -43,7 +51,8 @@ while () { my $fixexpr = "App(IncludeFix(\"nar/unnar.fix\"), " . "[ (\"nar\", $fetch)" . - ", (\"name\", \"fetched-$id\")" . + ", (\"name\", \"$outname\")" . + ", (\"id\", \"$id\")" . "])"; my $fixfile = "/tmp/nix-pull-tmp.fix"; diff --git a/src/fix.cc b/src/fix.cc index 93cc27cfc..afa0167ec 100644 --- a/src/fix.cc +++ b/src/fix.cc @@ -9,11 +9,13 @@ typedef ATerm Expr; typedef map NormalForms; +typedef map PkgHashes; struct EvalState { Strings searchDirs; NormalForms normalForms; + PkgHashes pkgHashes; /* normalised package hashes */ }; @@ -104,6 +106,23 @@ static Expr substExprMany(ATermList formals, ATermList args, Expr body) } +Hash hashPackage(EvalState & state, FState fs) +{ + if (fs.type == FState::fsDerive) { + for (FSIds::iterator i = fs.derive.inputs.begin(); + i != fs.derive.inputs.end(); i++) + { + PkgHashes::iterator j = state.pkgHashes.find(*i); + if (j == state.pkgHashes.end()) + throw Error(format("unknown package id %1%") % (string) *i); + *i = j->second; + } + } + debug(printTerm(unparseFState(fs))); + return hashTerm(unparseFState(fs)); +} + + static Expr evalExpr2(EvalState & state, Expr e) { char * s1; @@ -117,9 +136,10 @@ static Expr evalExpr2(EvalState & state, Expr e) return e; try { - parseFState(e); - return ATmake("FSId()", - ((string) writeTerm(e, "")).c_str()); + Hash pkgHash = hashPackage(state, parseFState(e)); + FSId pkgId = writeTerm(e, ""); + state.pkgHashes[pkgId] = pkgHash; + return ATmake("FSId()", ((string) pkgId).c_str()); } catch (...) { /* !!! catch parse errors only */ } @@ -153,10 +173,10 @@ static Expr evalExpr2(EvalState & state, Expr e) fs.slice.roots.push_back(id); fs.slice.elems.push_back(elem); - FSId termId = hashString("producer-" + (string) id - + "-" + dstPath); - writeTerm(unparseFState(fs), "", termId); - return ATmake("FSId()", ((string) termId).c_str()); + Hash pkgHash = hashPackage(state, fs); + FSId pkgId = writeTerm(unparseFState(fs), ""); + state.pkgHashes[pkgId] = pkgHash; + return ATmake("FSId()", ((string) pkgId).c_str()); } /* Packages are transformed into Derive fstate expressions. */ @@ -179,6 +199,7 @@ static Expr evalExpr2(EvalState & state, Expr e) fs.derive.platform = SYSTEM; string name; FSId outId; + bool outIdGiven = false; bnds = ATempty; for (map::iterator it = bndMap.begin(); @@ -198,7 +219,10 @@ static Expr evalExpr2(EvalState & state, Expr e) } else if (ATmatch(value, "", &s1)) { if (key == "name") name = s1; - if (key == "id") outId = parseHash(s1); + if (key == "id") { + outId = parseHash(s1); + outIdGiven = true; + } fs.derive.env.push_back(StringPair(key, s1)); } else throw badTerm("invalid package argument", value); @@ -215,8 +239,7 @@ static Expr evalExpr2(EvalState & state, Expr e) /* Hash the fstate-expression with no outputs to produce a unique but deterministic path name for this package. */ - if (outId == FSId()) - outId = hashTerm(unparseFState(fs)); + if (!outIdGiven) outId = hashPackage(state, fs); string outPath = canonPath(nixStore + "/" + ((string) outId).c_str() + "-" + name); fs.derive.env.push_back(StringPair("out", outPath)); @@ -224,10 +247,12 @@ static Expr evalExpr2(EvalState & state, Expr e) debug(format("%1%: %2%") % (string) outId % name); /* Write the resulting term into the Nix store directory. */ - FSId termId = hashString("producer-" + (string) outId - + "-" + outPath); - writeTerm(unparseFState(fs), "-d-" + name, termId); - return ATmake("FSId()", ((string) termId).c_str()); + Hash pkgHash = outIdGiven + ? hashString((string) outId + outPath) + : hashPackage(state, fs); + FSId pkgId = writeTerm(unparseFState(fs), "-d-" + name); + state.pkgHashes[pkgId] = pkgHash; + return ATmake("FSId()", ((string) pkgId).c_str()); } /* BaseName primitive function. */ diff --git a/src/fstate.cc b/src/fstate.cc index 1c8c6776f..5da3d8358 100644 --- a/src/fstate.cc +++ b/src/fstate.cc @@ -62,9 +62,6 @@ static void parseIds(ATermList ids, FSIds & out) } -typedef set FSIdSet; - - static void checkSlice(const Slice & slice) { if (slice.elems.size() == 0) diff --git a/src/normalise.cc b/src/normalise.cc index 8da940aa6..f463457e4 100644 --- a/src/normalise.cc +++ b/src/normalise.cc @@ -24,7 +24,7 @@ static FSId storeSuccessor(const FSId & id1, ATerm sc) typedef set FSIdSet; -Slice normaliseFState(FSId id) +Slice normaliseFState(FSId id, FSIdSet pending) { debug(format("normalising fstate %1%") % (string) id); Nest nest(true); @@ -57,8 +57,8 @@ Slice normaliseFState(FSId id) for (FSIds::iterator i = fs.derive.inputs.begin(); i != fs.derive.inputs.end(); i++) { - Slice slice = normaliseFState(*i); - realiseSlice(slice); + Slice slice = normaliseFState(*i, pending); + realiseSlice(slice, pending); for (SliceElems::iterator j = slice.elems.begin(); j != slice.elems.end(); j++) @@ -93,7 +93,7 @@ Slice normaliseFState(FSId id) i != outPaths.end(); i++) { try { - expandId(i->second, i->first); + expandId(i->second, i->first, "/", pending); } catch (Error & e) { debug(format("fast build failed: %1%") % e.what()); fastBuild = false; @@ -175,7 +175,7 @@ Slice normaliseFState(FSId id) } -void realiseSlice(const Slice & slice) +void realiseSlice(const Slice & slice, FSIdSet pending) { debug(format("realising slice")); Nest nest(true); @@ -209,7 +209,7 @@ void realiseSlice(const Slice & slice) { SliceElem elem = *i; debug(format("expanding %1% in %2%") % (string) elem.id % elem.path); - expandId(elem.id, elem.path); + expandId(elem.id, elem.path, "/", pending); } } diff --git a/src/normalise.hh b/src/normalise.hh index 49f9e68ee..72ee1d089 100644 --- a/src/normalise.hh +++ b/src/normalise.hh @@ -5,11 +5,11 @@ /* Normalise an fstate-expression, that is, return an equivalent - Slice. */ -Slice normaliseFState(FSId id); + Slice. (For the meaning of `pending', see expandId()). */ +Slice normaliseFState(FSId id, FSIdSet pending = FSIdSet()); /* Realise a Slice in the file system. */ -void realiseSlice(const Slice & slice); +void realiseSlice(const Slice & slice, FSIdSet pending = FSIdSet()); /* Get the list of root (output) paths of the given fstate-expression. */ diff --git a/src/store.cc b/src/store.cc index 65c44ca37..013bd2e2a 100644 --- a/src/store.cc +++ b/src/store.cc @@ -165,8 +165,11 @@ bool isInPrefix(const string & path, const string & _prefix) string expandId(const FSId & id, const string & target, - const string & prefix) + const string & prefix, FSIdSet pending) { + debug(format("expanding %1%") % (string) id); + Nest nest(true); + Strings paths; if (!target.empty() && !isInPrefix(target, prefix)) @@ -203,30 +206,24 @@ string expandId(const FSId & id, const string & target, } } - /* Try to realise the substitutes. */ + if (pending.find(id) != pending.end()) + throw Error(format("id %1% already being expanded") % (string) id); + pending.insert(id); + /* Try to realise the substitutes, but only if this id is not + already being realised by a substitute. */ Strings subs; queryListDB(nixDB, dbSubstitutes, id, subs); /* non-existence = ok */ for (Strings::iterator it = subs.begin(); it != subs.end(); it++) { FSId subId = parseHash(*it); - Slice slice = normaliseFState(subId); - realiseSlice(slice); - - Strings paths = fstatePaths(subId, true); - if (paths.size() != 1) - throw Error("substitute created more than 1 path"); - string path = *(paths.begin()); - if (target.empty()) - return path; /* !!! prefix */ - else { - if (path != target) { - copyPath(path, target); - registerPath(target, id); - } - return target; - } + debug(format("trying substitute %1%") % (string) subId); + + Slice slice = normaliseFState(subId, pending); + realiseSlice(slice, pending); + + return expandId(id, target, prefix, pending); } throw Error(format("cannot expand id `%1%'") % (string) id); diff --git a/src/store.hh b/src/store.hh index faac76009..b2cdc41f1 100644 --- a/src/store.hh +++ b/src/store.hh @@ -10,6 +10,8 @@ using namespace std; typedef Hash FSId; +typedef set FSIdSet; + /* Copy a path recursively. */ void copyPath(string src, string dst); @@ -26,9 +28,14 @@ bool queryPathId(const string & path, FSId & id); /* Return a path whose contents have the given hash. If target is not empty, ensure that such a path is realised in target (if necessary by copying from another location). If prefix is not - empty, only return a path that is an descendent of prefix. */ + empty, only return a path that is an descendent of prefix. + + The list of pending ids are those that already being expanded. + This prevents infinite recursion for ids realised through a + substitute (since when we build the substitute, we would first try + to expand the id... kaboom!). */ string expandId(const FSId & id, const string & target = "", - const string & prefix = "/"); + const string & prefix = "/", FSIdSet pending = FSIdSet()); /* Copy a file to the nixStore directory and register it in dbRefs. Return the hash code of the value. */