* Store user environment manifests as a Nix expression in
$out/manifest.nix rather than as an ATerm. (Hm, I thought I committed this two days ago...)
This commit is contained in:
		
							parent
							
								
									f3b8833a48
								
							
						
					
					
						commit
						fe2d869e04
					
				
					 11 changed files with 205 additions and 183 deletions
				
			
		|  | @ -160,4 +160,4 @@ while (scalar(keys %postponed) > 0) { | ||||||
| print STDERR "created $symlinks symlinks in user environment\n"; | print STDERR "created $symlinks symlinks in user environment\n"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| symlink($ENV{"manifest"}, "$out/manifest") or die "cannot create manifest"; | symlink($ENV{"manifest"}, "$out/manifest.nix") or die "cannot create manifest"; | ||||||
|  |  | ||||||
|  | @ -98,6 +98,7 @@ EvalState::EvalState() | ||||||
|     , sType(symbols.create("type")) |     , sType(symbols.create("type")) | ||||||
|     , sMeta(symbols.create("meta")) |     , sMeta(symbols.create("meta")) | ||||||
|     , sName(symbols.create("name")) |     , sName(symbols.create("name")) | ||||||
|  |     , sSystem(symbols.create("system")) | ||||||
|     , baseEnv(allocEnv(128)) |     , baseEnv(allocEnv(128)) | ||||||
|     , baseEnvDispl(0) |     , baseEnvDispl(0) | ||||||
|     , staticBaseEnv(false, 0) |     , staticBaseEnv(false, 0) | ||||||
|  | @ -131,12 +132,13 @@ void EvalState::addPrimOp(const string & name, | ||||||
|     unsigned int arity, PrimOp primOp) |     unsigned int arity, PrimOp primOp) | ||||||
| { | { | ||||||
|     Value v; |     Value v; | ||||||
|  |     string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; | ||||||
|     v.type = tPrimOp; |     v.type = tPrimOp; | ||||||
|     v.primOp.arity = arity; |     v.primOp.arity = arity; | ||||||
|     v.primOp.fun = primOp; |     v.primOp.fun = primOp; | ||||||
|  |     v.primOp.name = strdup(name2.c_str()); | ||||||
|     staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; |     staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; | ||||||
|     baseEnv.values[baseEnvDispl++] = v; |     baseEnv.values[baseEnvDispl++] = v; | ||||||
|     string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; |  | ||||||
|     (*baseEnv.values[0].attrs)[symbols.create(name2)] = v; |     (*baseEnv.values[0].attrs)[symbols.create(name2)] = v; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -550,7 +552,12 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v) | ||||||
|                 vArgs[n--] = arg->primOpApp.right; |                 vArgs[n--] = arg->primOpApp.right; | ||||||
| 
 | 
 | ||||||
|             /* And call the primop. */ |             /* And call the primop. */ | ||||||
|  |             try { | ||||||
|                 primOp->primOp.fun(*this, vArgs, v); |                 primOp->primOp.fun(*this, vArgs, v); | ||||||
|  |             } catch (Error & e) { | ||||||
|  |                 addErrorPrefix(e, "while evaluating the builtin function `%1%':\n", primOp->primOp.name); | ||||||
|  |                 throw; | ||||||
|  |             } | ||||||
|         } else { |         } else { | ||||||
|             Value * v2 = allocValues(2); |             Value * v2 = allocValues(2); | ||||||
|             v2[0] = fun; |             v2[0] = fun; | ||||||
|  |  | ||||||
|  | @ -92,6 +92,7 @@ struct Value | ||||||
|         Value * val; |         Value * val; | ||||||
|         struct { |         struct { | ||||||
|             PrimOp fun; |             PrimOp fun; | ||||||
|  |             char * name; | ||||||
|             unsigned int arity; |             unsigned int arity; | ||||||
|         } primOp; |         } primOp; | ||||||
|         struct { |         struct { | ||||||
|  | @ -138,6 +139,14 @@ static inline void mkCopy(Value & v, Value & src) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | static inline void mkApp(Value & v, Value & left, Value & right) | ||||||
|  | { | ||||||
|  |     v.type = tApp; | ||||||
|  |     v.app.left = &left; | ||||||
|  |     v.app.right = &right; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| void mkString(Value & v, const char * s); | void mkString(Value & v, const char * s); | ||||||
| void mkString(Value & v, const string & s, const PathSet & context = PathSet()); | void mkString(Value & v, const string & s, const PathSet & context = PathSet()); | ||||||
| void mkPath(Value & v, const char * s); | void mkPath(Value & v, const char * s); | ||||||
|  | @ -162,7 +171,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     SymbolTable symbols; |     SymbolTable symbols; | ||||||
| 
 | 
 | ||||||
|     const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName; |     const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sSystem; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     SrcToStore srcToStore;  |     SrcToStore srcToStore;  | ||||||
|  |  | ||||||
|  | @ -70,27 +70,6 @@ void DrvInfo::setMetaInfo(const MetaInfo & meta) | ||||||
| { | { | ||||||
|     metaInfoRead = true; |     metaInfoRead = true; | ||||||
|     this->meta = meta; |     this->meta = meta; | ||||||
|      |  | ||||||
| #if 0 |  | ||||||
|     Value * metaAttrs = state.allocValues(1); |  | ||||||
|     foreach (MetaInfo::const_iterator, i, meta) { |  | ||||||
|         Expr e; |  | ||||||
|         switch (i->second.type) { |  | ||||||
|             case MetaValue::tpInt: e = makeInt(i->second.intValue); break; |  | ||||||
|             case MetaValue::tpString: e = makeStr(i->second.stringValue); break; |  | ||||||
|             case MetaValue::tpStrings: { |  | ||||||
|                 ATermList es = ATempty; |  | ||||||
|                 foreach (Strings::const_iterator, j, i->second.stringValues) |  | ||||||
|                     es = ATinsert(es, makeStr(*j)); |  | ||||||
|                 e = makeList(ATreverse(es)); |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             default: abort(); |  | ||||||
|         } |  | ||||||
|         metaAttrs.set(toATerm(i->first), makeAttrRHS(e, makeNoPos())); |  | ||||||
|     } |  | ||||||
|     attrs->set(toATerm("meta"), makeAttrs(metaAttrs)); |  | ||||||
| #endif |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -122,7 +101,7 @@ static bool getDerivation(EvalState & state, Value & v, | ||||||
|         if (i == v.attrs->end()) throw TypeError("derivation name missing"); |         if (i == v.attrs->end()) throw TypeError("derivation name missing"); | ||||||
|         drv.name = state.forceStringNoCtx(i->second); |         drv.name = state.forceStringNoCtx(i->second); | ||||||
| 
 | 
 | ||||||
|         i = v.attrs->find(state.symbols.create("system")); |         i = v.attrs->find(state.sSystem); | ||||||
|         if (i == v.attrs->end()) |         if (i == v.attrs->end()) | ||||||
|             drv.system = "unknown"; |             drv.system = "unknown"; | ||||||
|         else |         else | ||||||
|  |  | ||||||
|  | @ -41,7 +41,7 @@ public: | ||||||
|     /* !!! make this private */ |     /* !!! make this private */ | ||||||
|     Bindings * attrs; |     Bindings * attrs; | ||||||
| 
 | 
 | ||||||
|     DrvInfo() : metaInfoRead(false) { }; |     DrvInfo() : metaInfoRead(false), attrs(0) { }; | ||||||
| 
 | 
 | ||||||
|     string queryDrvPath(EvalState & state) const; |     string queryDrvPath(EvalState & state) const; | ||||||
|     string queryOutPath(EvalState & state) const; |     string queryOutPath(EvalState & state) const; | ||||||
|  |  | ||||||
|  | @ -89,24 +89,29 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v) | ||||||
| { | { | ||||||
|     startNest(nest, lvlDebug, "finding dependencies"); |     startNest(nest, lvlDebug, "finding dependencies"); | ||||||
| 
 | 
 | ||||||
|     Expr attrs = evalExpr(state, args[0]); |     state.forceAttrs(*args[0]); | ||||||
| 
 | 
 | ||||||
|     /* Get the start set. */ |     /* Get the start set. */ | ||||||
|     Expr startSet = queryAttr(attrs, "startSet"); |     Bindings::iterator startSet = | ||||||
|     if (!startSet) throw EvalError("attribute `startSet' required"); |         args[0]->attrs->find(state.symbols.create("startSet")); | ||||||
|     ATermList startSet2 = evalList(state, startSet); |     if (startSet == args[0]->attrs->end()) | ||||||
|  |         throw EvalError("attribute `startSet' required"); | ||||||
|  |     state.forceList(startSet->second); | ||||||
| 
 | 
 | ||||||
|     set<Expr> workSet; // !!! gc roots
 |     list<Value> workSet; | ||||||
|     for (ATermIterator i(startSet2); i; ++i) workSet.insert(*i); |     for (unsigned int n = 0; n < startSet->second.list.length; ++n) | ||||||
|  |         workSet.push_back(*startSet->second.list.elems[n]); | ||||||
| 
 | 
 | ||||||
|     /* Get the operator. */ |     /* Get the operator. */ | ||||||
|     Expr op = queryAttr(attrs, "operator"); |     Bindings::iterator op = | ||||||
|     if (!op) throw EvalError("attribute `operator' required"); |         args[0]->attrs->find(state.symbols.create("operator")); | ||||||
|  |     if (op == args[0]->attrs->end()) | ||||||
|  |         throw EvalError("attribute `operator' required"); | ||||||
|      |      | ||||||
|     /* Construct the closure by applying the operator to element of
 |     /* Construct the closure by applying the operator to element of
 | ||||||
|        `workSet', adding the result to `workSet', continuing until |        `workSet', adding the result to `workSet', continuing until | ||||||
|        no new elements are found. */ |        no new elements are found. */ | ||||||
|     ATermList res = ATempty; |     list<Value> res; | ||||||
|     set<Expr> doneKeys; // !!! gc roots
 |     set<Expr> doneKeys; // !!! gc roots
 | ||||||
|     while (!workSet.empty()) { |     while (!workSet.empty()) { | ||||||
| 	Expr e = *(workSet.begin()); | 	Expr e = *(workSet.begin()); | ||||||
|  | @ -322,8 +327,8 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) | ||||||
|                 string s = state.coerceToString(i->second, context, true); |                 string s = state.coerceToString(i->second, context, true); | ||||||
|                 drv.env[key] = s; |                 drv.env[key] = s; | ||||||
|                 if (key == "builder") drv.builder = s; |                 if (key == "builder") drv.builder = s; | ||||||
|                 else if (key == "system") drv.platform = s; |                 else if (i->first == state.sSystem) drv.platform = s; | ||||||
|                 else if (key == "name") drvName = s; |                 else if (i->first == state.sName) drvName = s; | ||||||
|                 else if (key == "outputHash") outputHash = s; |                 else if (key == "outputHash") outputHash = s; | ||||||
|                 else if (key == "outputHashAlgo") outputHashAlgo = s; |                 else if (key == "outputHashAlgo") outputHashAlgo = s; | ||||||
|                 else if (key == "outputHashMode") { |                 else if (key == "outputHashMode") { | ||||||
|  | @ -830,9 +835,7 @@ static void prim_map(EvalState & state, Value * * args, Value & v) | ||||||
| 
 | 
 | ||||||
|     for (unsigned int n = 0; n < v.list.length; ++n) { |     for (unsigned int n = 0; n < v.list.length; ++n) { | ||||||
|         v.list.elems[n] = &vs[n]; |         v.list.elems[n] = &vs[n]; | ||||||
|         vs[n].type = tApp; |         mkApp(vs[n], *args[0], *args[1]->list.elems[n]); | ||||||
|         vs[n].app.left = args[0]; |  | ||||||
|         vs[n].app.right = args[1]->list.elems[n]; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -8,7 +8,6 @@ | ||||||
| #include "help.txt.hh" | #include "help.txt.hh" | ||||||
| #include "get-drvs.hh" | #include "get-drvs.hh" | ||||||
| #include "attr-path.hh" | #include "attr-path.hh" | ||||||
| #include "pathlocks.hh" |  | ||||||
| #include "common-opts.hh" | #include "common-opts.hh" | ||||||
| #include "xml-writer.hh" | #include "xml-writer.hh" | ||||||
| #include "store-api.hh" | #include "store-api.hh" | ||||||
|  | @ -193,141 +192,6 @@ static Path getDefNixExprPath() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /* Ensure exclusive access to a profile.  Any command that modifies
 |  | ||||||
|    the profile first acquires this lock. */ |  | ||||||
| static void lockProfile(PathLocks & lock, const Path & profile) |  | ||||||
| { |  | ||||||
|     lock.lockPaths(singleton<PathSet>(profile), |  | ||||||
|         (format("waiting for lock on profile `%1%'") % profile).str()); |  | ||||||
|     lock.setDeletion(true); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /* Optimistic locking is used by long-running operations like `nix-env
 |  | ||||||
|    -i'.  Instead of acquiring the exclusive lock for the entire |  | ||||||
|    duration of the operation, we just perform the operation |  | ||||||
|    optimistically (without an exclusive lock), and check at the end |  | ||||||
|    whether the profile changed while we were busy (i.e., the symlink |  | ||||||
|    target changed).  If so, the operation is restarted.  Restarting is |  | ||||||
|    generally cheap, since the build results are still in the Nix |  | ||||||
|    store.  Most of the time, only the user environment has to be |  | ||||||
|    rebuilt. */ |  | ||||||
| static string optimisticLockProfile(const Path & profile) |  | ||||||
| { |  | ||||||
|     return pathExists(profile) ? readLink(profile) : ""; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static bool createUserEnv(EvalState & state, DrvInfos & elems, |  | ||||||
|     const Path & profile, bool keepDerivations, |  | ||||||
|     const string & lockToken) |  | ||||||
| { |  | ||||||
|     throw Error("not implemented"); |  | ||||||
| #if 0 |  | ||||||
|     /* Build the components in the user environment, if they don't
 |  | ||||||
|        exist already. */ |  | ||||||
|     PathSet drvsToBuild; |  | ||||||
|     foreach (DrvInfos::const_iterator, i, elems) |  | ||||||
|         /* Call to `isDerivation' is for compatibility with Nix <= 0.7
 |  | ||||||
|            user environments. */ |  | ||||||
|         if (i->queryDrvPath(state) != "" && |  | ||||||
|             isDerivation(i->queryDrvPath(state))) |  | ||||||
|             drvsToBuild.insert(i->queryDrvPath(state)); |  | ||||||
| 
 |  | ||||||
|     debug(format("building user environment dependencies")); |  | ||||||
|     store->buildDerivations(drvsToBuild); |  | ||||||
| 
 |  | ||||||
|     /* Get the environment builder expression. */ |  | ||||||
|     Expr envBuilder = parseExprFromFile(state, |  | ||||||
|         nixDataDir + "/nix/corepkgs/buildenv"); /* !!! */ |  | ||||||
| 
 |  | ||||||
|     /* Construct the whole top level derivation. */ |  | ||||||
|     PathSet references; |  | ||||||
|     ATermList manifest = ATempty; |  | ||||||
|     ATermList inputs = ATempty; |  | ||||||
|     foreach (DrvInfos::iterator, i, elems) { |  | ||||||
|         /* Create a pseudo-derivation containing the name, system,
 |  | ||||||
|            output path, and optionally the derivation path, as well as |  | ||||||
|            the meta attributes. */ |  | ||||||
|         Path drvPath = keepDerivations ? i->queryDrvPath(state) : ""; |  | ||||||
| 
 |  | ||||||
|         /* Round trip to get rid of "bad" meta values (like
 |  | ||||||
|            functions). */ |  | ||||||
|         MetaInfo meta = i->queryMetaInfo(state); |  | ||||||
|         i->setMetaInfo(meta); |  | ||||||
|          |  | ||||||
|         ATermList as = ATmakeList5( |  | ||||||
|             makeBind(toATerm("type"), |  | ||||||
|                 makeStr("derivation"), makeNoPos()), |  | ||||||
|             makeBind(toATerm("name"), |  | ||||||
|                 makeStr(i->name), makeNoPos()), |  | ||||||
|             makeBind(toATerm("system"), |  | ||||||
|                 makeStr(i->system), makeNoPos()), |  | ||||||
|             makeBind(toATerm("outPath"), |  | ||||||
|                 makeStr(i->queryOutPath(state)), makeNoPos()),  |  | ||||||
|             makeBind(toATerm("meta"), |  | ||||||
|                 i->attrs->get(toATerm("meta")), makeNoPos())); |  | ||||||
|          |  | ||||||
|         if (drvPath != "") as = ATinsert(as,  |  | ||||||
|             makeBind(toATerm("drvPath"), |  | ||||||
|                 makeStr(drvPath), makeNoPos())); |  | ||||||
|          |  | ||||||
|         manifest = ATinsert(manifest, makeAttrs(as)); |  | ||||||
|          |  | ||||||
|         inputs = ATinsert(inputs, makeStr(i->queryOutPath(state))); |  | ||||||
| 
 |  | ||||||
|         /* This is only necessary when installing store paths, e.g.,
 |  | ||||||
|            `nix-env -i /nix/store/abcd...-foo'. */ |  | ||||||
|         store->addTempRoot(i->queryOutPath(state)); |  | ||||||
|         store->ensurePath(i->queryOutPath(state)); |  | ||||||
|          |  | ||||||
|         references.insert(i->queryOutPath(state)); |  | ||||||
|         if (drvPath != "") references.insert(drvPath); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Also write a copy of the list of inputs to the store; we need
 |  | ||||||
|        it for future modifications of the environment. */ |  | ||||||
|     Path manifestFile = store->addTextToStore("env-manifest", |  | ||||||
|         atPrint(canonicaliseExpr(makeList(ATreverse(manifest)))), references); |  | ||||||
| 
 |  | ||||||
|     Expr topLevel = makeCall(envBuilder, makeAttrs(ATmakeList3( |  | ||||||
|         makeBind(toATerm("system"), |  | ||||||
|             makeStr(thisSystem), makeNoPos()), |  | ||||||
|         makeBind(toATerm("derivations"), |  | ||||||
|             makeList(ATreverse(manifest)), makeNoPos()), |  | ||||||
|         makeBind(toATerm("manifest"), |  | ||||||
|             makeStr(manifestFile, singleton<PathSet>(manifestFile)), makeNoPos()) |  | ||||||
|         ))); |  | ||||||
| 
 |  | ||||||
|     /* Instantiate it. */ |  | ||||||
|     debug(format("evaluating builder expression `%1%'") % topLevel); |  | ||||||
|     DrvInfo topLevelDrv; |  | ||||||
|     if (!getDerivation(state, topLevel, topLevelDrv)) |  | ||||||
|         abort(); |  | ||||||
|      |  | ||||||
|     /* Realise the resulting store expression. */ |  | ||||||
|     debug(format("building user environment")); |  | ||||||
|     store->buildDerivations(singleton<PathSet>(topLevelDrv.queryDrvPath(state))); |  | ||||||
| 
 |  | ||||||
|     /* Switch the current user environment to the output path. */ |  | ||||||
|     PathLocks lock; |  | ||||||
|     lockProfile(lock, profile); |  | ||||||
| 
 |  | ||||||
|     Path lockTokenCur = optimisticLockProfile(profile); |  | ||||||
|     if (lockToken != lockTokenCur) { |  | ||||||
|         printMsg(lvlError, format("profile `%1%' changed while we were busy; restarting") % profile); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     debug(format("switching to new user environment")); |  | ||||||
|     Path generation = createGeneration(profile, topLevelDrv.queryOutPath(state)); |  | ||||||
|     switchLink(profile, generation); |  | ||||||
| 
 |  | ||||||
|     return true; |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static int getPriority(EvalState & state, const DrvInfo & drv) | static int getPriority(EvalState & state, const DrvInfo & drv) | ||||||
| { | { | ||||||
|     MetaValue value = drv.queryMetaInfo(state, "priority"); |     MetaValue value = drv.queryMetaInfo(state, "priority"); | ||||||
|  |  | ||||||
|  | @ -131,5 +131,19 @@ void switchLink(Path link, Path target) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | void lockProfile(PathLocks & lock, const Path & profile) | ||||||
|  | { | ||||||
|  |     lock.lockPaths(singleton<PathSet>(profile), | ||||||
|  |         (format("waiting for lock on profile `%1%'") % profile).str()); | ||||||
|  |     lock.setDeletion(true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | string optimisticLockProfile(const Path & profile) | ||||||
|  | { | ||||||
|  |     return pathExists(profile) ? readLink(profile) : ""; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ | ||||||
| #define __PROFILES_H | #define __PROFILES_H | ||||||
| 
 | 
 | ||||||
| #include "types.hh" | #include "types.hh" | ||||||
|  | #include "pathlocks.hh" | ||||||
| 
 | 
 | ||||||
| #include <time.h> | #include <time.h> | ||||||
| 
 | 
 | ||||||
|  | @ -37,6 +38,20 @@ void deleteGeneration(const Path & profile, unsigned int gen); | ||||||
| 
 | 
 | ||||||
| void switchLink(Path link, Path target); | void switchLink(Path link, Path target); | ||||||
| 
 | 
 | ||||||
|  | /* Ensure exclusive access to a profile.  Any command that modifies
 | ||||||
|  |    the profile first acquires this lock. */ | ||||||
|  | void lockProfile(PathLocks & lock, const Path & profile); | ||||||
|  | 
 | ||||||
|  | /* Optimistic locking is used by long-running operations like `nix-env
 | ||||||
|  |    -i'.  Instead of acquiring the exclusive lock for the entire | ||||||
|  |    duration of the operation, we just perform the operation | ||||||
|  |    optimistically (without an exclusive lock), and check at the end | ||||||
|  |    whether the profile changed while we were busy (i.e., the symlink | ||||||
|  |    target changed).  If so, the operation is restarted.  Restarting is | ||||||
|  |    generally cheap, since the build results are still in the Nix | ||||||
|  |    store.  Most of the time, only the user environment has to be | ||||||
|  |    rebuilt. */ | ||||||
|  | string optimisticLockProfile(const Path & profile); | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,5 +1,12 @@ | ||||||
| #include "util.hh" | #include "util.hh" | ||||||
| #include "get-drvs.hh" | #include "get-drvs.hh" | ||||||
|  | #include "derivations.hh" | ||||||
|  | #include "store-api.hh" | ||||||
|  | #include "globals.hh" | ||||||
|  | #include "shared.hh" | ||||||
|  | #include "eval.hh" | ||||||
|  | #include "parser.hh" | ||||||
|  | #include "profiles.hh" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| namespace nix { | namespace nix { | ||||||
|  | @ -12,17 +19,137 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv) | ||||||
| { | { | ||||||
|     DrvInfos elems; |     DrvInfos elems; | ||||||
| 
 | 
 | ||||||
|     Path path = userEnv + "/manifest"; |     Path manifestFile = userEnv + "/manifest.nix"; | ||||||
|  |     Path oldManifestFile = userEnv + "/manifest"; | ||||||
| 
 | 
 | ||||||
|     if (!pathExists(path)) |     if (pathExists(manifestFile)) { | ||||||
|         return DrvInfos(); /* not an error, assume nothing installed */ |         Value v; | ||||||
| 
 |         state.eval(parseExprFromFile(state, manifestFile), v); | ||||||
|     readLegacyManifest(path, elems); |         getDerivations(state, v, "", Bindings(), elems); | ||||||
|  |     } else if (pathExists(oldManifestFile)) | ||||||
|  |         readLegacyManifest(oldManifestFile, elems); | ||||||
| 
 | 
 | ||||||
|     return elems; |     return elems; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | bool createUserEnv(EvalState & state, DrvInfos & elems, | ||||||
|  |     const Path & profile, bool keepDerivations, | ||||||
|  |     const string & lockToken) | ||||||
|  | { | ||||||
|  |     /* Build the components in the user environment, if they don't
 | ||||||
|  |        exist already. */ | ||||||
|  |     PathSet drvsToBuild; | ||||||
|  |     foreach (DrvInfos::const_iterator, i, elems) | ||||||
|  |         if (i->queryDrvPath(state) != "") | ||||||
|  |             drvsToBuild.insert(i->queryDrvPath(state)); | ||||||
|  | 
 | ||||||
|  |     debug(format("building user environment dependencies")); | ||||||
|  |     store->buildDerivations(drvsToBuild); | ||||||
|  | 
 | ||||||
|  |     /* Construct the whole top level derivation. */ | ||||||
|  |     PathSet references; | ||||||
|  |     Value manifest; | ||||||
|  |     state.mkList(manifest, elems.size()); | ||||||
|  |     unsigned int n = 0; | ||||||
|  |     foreach (DrvInfos::iterator, i, elems) { | ||||||
|  |         /* Create a pseudo-derivation containing the name, system,
 | ||||||
|  |            output path, and optionally the derivation path, as well as | ||||||
|  |            the meta attributes. */ | ||||||
|  |         Path drvPath = keepDerivations ? i->queryDrvPath(state) : ""; | ||||||
|  | 
 | ||||||
|  |         Value & v(*state.allocValues(1)); | ||||||
|  |         manifest.list.elems[n++] = &v; | ||||||
|  |         state.mkAttrs(v); | ||||||
|  | 
 | ||||||
|  |         mkString((*v.attrs)[state.sType], "derivation"); | ||||||
|  |         mkString((*v.attrs)[state.sName], i->name); | ||||||
|  |         mkString((*v.attrs)[state.sSystem], i->system); | ||||||
|  |         mkString((*v.attrs)[state.sOutPath], i->queryOutPath(state)); | ||||||
|  |         if (drvPath != "") | ||||||
|  |             mkString((*v.attrs)[state.sDrvPath], i->queryDrvPath(state)); | ||||||
|  |          | ||||||
|  |         state.mkAttrs((*v.attrs)[state.sMeta]); | ||||||
|  |          | ||||||
|  |         MetaInfo meta = i->queryMetaInfo(state); | ||||||
|  | 
 | ||||||
|  |         foreach (MetaInfo::const_iterator, j, meta) { | ||||||
|  |             Value & v2((*(*v.attrs)[state.sMeta].attrs)[state.symbols.create(j->first)]); | ||||||
|  |             switch (j->second.type) { | ||||||
|  |                 case MetaValue::tpInt: mkInt(v2, j->second.intValue); break; | ||||||
|  |                 case MetaValue::tpString: mkString(v2, j->second.stringValue); break; | ||||||
|  |                 case MetaValue::tpStrings: { | ||||||
|  |                     state.mkList(v2, j->second.stringValues.size()); | ||||||
|  |                     unsigned int m = 0; | ||||||
|  |                     foreach (Strings::const_iterator, k, j->second.stringValues) { | ||||||
|  |                         v2.list.elems[m] = state.allocValues(1); | ||||||
|  |                         mkString(*v2.list.elems[m++], *k); | ||||||
|  |                     } | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 default: abort(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |      | ||||||
|  |         /* This is only necessary when installing store paths, e.g.,
 | ||||||
|  |            `nix-env -i /nix/store/abcd...-foo'. */ | ||||||
|  |         store->addTempRoot(i->queryOutPath(state)); | ||||||
|  |         store->ensurePath(i->queryOutPath(state)); | ||||||
|  |          | ||||||
|  |         references.insert(i->queryOutPath(state)); | ||||||
|  |         if (drvPath != "") references.insert(drvPath); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Also write a copy of the list of user environment elements to
 | ||||||
|  |        the store; we need it for future modifications of the | ||||||
|  |        environment. */ | ||||||
|  |     Path manifestFile = store->addTextToStore("env-manifest.nix", | ||||||
|  |         (format("%1%") % manifest).str(), references); | ||||||
|  | 
 | ||||||
|  |     printMsg(lvlError, manifestFile); | ||||||
|  | 
 | ||||||
|  |     /* Get the environment builder expression. */ | ||||||
|  |     Value envBuilder; | ||||||
|  |     state.eval(parseExprFromFile(state, nixDataDir + "/nix/corepkgs/buildenv"), envBuilder); | ||||||
|  | 
 | ||||||
|  |     /* Construct a Nix expression that calls the user environment
 | ||||||
|  |        builder with the manifest as argument. */ | ||||||
|  |     Value args, topLevel; | ||||||
|  |     state.mkAttrs(args); | ||||||
|  |     mkString((*args.attrs)[state.sSystem], thisSystem); | ||||||
|  |     mkString((*args.attrs)[state.symbols.create("manifest")], | ||||||
|  |         manifestFile, singleton<PathSet>(manifestFile)); | ||||||
|  |     (*args.attrs)[state.symbols.create("derivations")] = manifest; | ||||||
|  |     mkApp(topLevel, envBuilder, args); | ||||||
|  |          | ||||||
|  |     /* Evaluate it. */ | ||||||
|  |     debug("evaluating user environment builder"); | ||||||
|  |     DrvInfo topLevelDrv; | ||||||
|  |     if (!getDerivation(state, topLevel, topLevelDrv)) | ||||||
|  |         abort(); | ||||||
|  |      | ||||||
|  |     /* Realise the resulting store expression. */ | ||||||
|  |     debug("building user environment"); | ||||||
|  |     store->buildDerivations(singleton<PathSet>(topLevelDrv.queryDrvPath(state))); | ||||||
|  | 
 | ||||||
|  |     /* Switch the current user environment to the output path. */ | ||||||
|  |     PathLocks lock; | ||||||
|  |     lockProfile(lock, profile); | ||||||
|  | 
 | ||||||
|  |     Path lockTokenCur = optimisticLockProfile(profile); | ||||||
|  |     if (lockToken != lockTokenCur) { | ||||||
|  |         printMsg(lvlError, format("profile `%1%' changed while we were busy; restarting") % profile); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     debug(format("switching to new user environment")); | ||||||
|  |     Path generation = createGeneration(profile, topLevelDrv.queryOutPath(state)); | ||||||
|  |     switchLink(profile, generation); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /* Code for parsing manifests in the old textual ATerm format. */ | /* Code for parsing manifests in the old textual ATerm format. */ | ||||||
| 
 | 
 | ||||||
| static string parseStr(std::istream & str) | static string parseStr(std::istream & str) | ||||||
|  |  | ||||||
|  | @ -7,6 +7,10 @@ namespace nix { | ||||||
| 
 | 
 | ||||||
| DrvInfos queryInstalled(EvalState & state, const Path & userEnv); | DrvInfos queryInstalled(EvalState & state, const Path & userEnv); | ||||||
| 
 | 
 | ||||||
|  | bool createUserEnv(EvalState & state, DrvInfos & elems, | ||||||
|  |     const Path & profile, bool keepDerivations, | ||||||
|  |     const string & lockToken); | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #endif /* !__USER_ENV_H */ | #endif /* !__USER_ENV_H */ | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue