1 #include "user-env.hh"
2 #include "util.hh"
3 #include "derivations.hh"
4 #include "store-api.hh"
5 #include "globals.hh"
6 #include "shared.hh"
7 #include "eval.hh"
8 #include "eval-inline.hh"
9 #include "profiles.hh"
10
11
12 namespace nix {
13
14
queryInstalled(EvalState & state,const Path & userEnv)15 DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
16 {
17 DrvInfos elems;
18 Path manifestFile = userEnv + "/manifest.nix";
19 if (pathExists(manifestFile)) {
20 Value v;
21 state.evalFile(manifestFile, v);
22 Bindings & bindings(*state.allocBindings(0));
23 getDerivations(state, v, "", bindings, elems, false);
24 }
25 return elems;
26 }
27
28
createUserEnv(EvalState & state,DrvInfos & elems,const Path & profile,bool keepDerivations,const string & lockToken)29 bool createUserEnv(EvalState & state, DrvInfos & elems,
30 const Path & profile, bool keepDerivations,
31 const string & lockToken)
32 {
33 /* Build the components in the user environment, if they don't
34 exist already. */
35 PathSet drvsToBuild;
36 for (auto & i : elems)
37 if (i.queryDrvPath() != "")
38 drvsToBuild.insert(i.queryDrvPath());
39
40 debug(format("building user environment dependencies"));
41 state.store->buildPaths(drvsToBuild, state.repair ? bmRepair : bmNormal);
42
43 /* Construct the whole top level derivation. */
44 PathSet references;
45 Value manifest;
46 state.mkList(manifest, elems.size());
47 unsigned int n = 0;
48 for (auto & i : elems) {
49 /* Create a pseudo-derivation containing the name, system,
50 output paths, and optionally the derivation path, as well
51 as the meta attributes. */
52 Path drvPath = keepDerivations ? i.queryDrvPath() : "";
53
54 Value & v(*state.allocValue());
55 manifest.listElems()[n++] = &v;
56 state.mkAttrs(v, 16);
57
58 mkString(*state.allocAttr(v, state.sType), "derivation");
59 mkString(*state.allocAttr(v, state.sName), i.queryName());
60 auto system = i.querySystem();
61 if (!system.empty())
62 mkString(*state.allocAttr(v, state.sSystem), system);
63 mkString(*state.allocAttr(v, state.sOutPath), i.queryOutPath());
64 if (drvPath != "")
65 mkString(*state.allocAttr(v, state.sDrvPath), i.queryDrvPath());
66
67 // Copy each output meant for installation.
68 DrvInfo::Outputs outputs = i.queryOutputs(true);
69 Value & vOutputs = *state.allocAttr(v, state.sOutputs);
70 state.mkList(vOutputs, outputs.size());
71 unsigned int m = 0;
72 for (auto & j : outputs) {
73 mkString(*(vOutputs.listElems()[m++] = state.allocValue()), j.first);
74 Value & vOutputs = *state.allocAttr(v, state.symbols.create(j.first));
75 state.mkAttrs(vOutputs, 2);
76 mkString(*state.allocAttr(vOutputs, state.sOutPath), j.second);
77
78 /* This is only necessary when installing store paths, e.g.,
79 `nix-env -i /nix/store/abcd...-foo'. */
80 state.store->addTempRoot(j.second);
81 state.store->ensurePath(j.second);
82
83 references.insert(j.second);
84 }
85
86 // Copy the meta attributes.
87 Value & vMeta = *state.allocAttr(v, state.sMeta);
88 state.mkAttrs(vMeta, 16);
89 StringSet metaNames = i.queryMetaNames();
90 for (auto & j : metaNames) {
91 Value * v = i.queryMeta(j);
92 if (!v) continue;
93 vMeta.attrs->push_back(Attr(state.symbols.create(j), v));
94 }
95 vMeta.attrs->sort();
96 v.attrs->sort();
97
98 if (drvPath != "") references.insert(drvPath);
99 }
100
101 /* Also write a copy of the list of user environment elements to
102 the store; we need it for future modifications of the
103 environment. */
104 Path manifestFile = state.store->addTextToStore("env-manifest.nix",
105 (format("%1%") % manifest).str(), references);
106
107 /* Get the environment builder expression. */
108 Value envBuilder;
109 state.evalFile(state.findFile("nix/buildenv.nix"), envBuilder);
110
111 /* Construct a Nix expression that calls the user environment
112 builder with the manifest as argument. */
113 Value args, topLevel;
114 state.mkAttrs(args, 3);
115 mkString(*state.allocAttr(args, state.symbols.create("manifest")),
116 manifestFile, {manifestFile});
117 args.attrs->push_back(Attr(state.symbols.create("derivations"), &manifest));
118 args.attrs->sort();
119 mkApp(topLevel, envBuilder, args);
120
121 /* Evaluate it. */
122 debug("evaluating user environment builder");
123 state.forceValue(topLevel);
124 PathSet context;
125 Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath));
126 Path topLevelDrv = state.coerceToPath(aDrvPath.pos ? *(aDrvPath.pos) : noPos, *(aDrvPath.value), context);
127 Attr & aOutPath(*topLevel.attrs->find(state.sOutPath));
128 Path topLevelOut = state.coerceToPath(aOutPath.pos ? *(aOutPath.pos) : noPos, *(aOutPath.value), context);
129
130 /* Realise the resulting store expression. */
131 debug("building user environment");
132 state.store->buildPaths({topLevelDrv}, state.repair ? bmRepair : bmNormal);
133
134 /* Switch the current user environment to the output path. */
135 auto store2 = state.store.dynamic_pointer_cast<LocalFSStore>();
136
137 if (store2) {
138 PathLocks lock;
139 lockProfile(lock, profile);
140
141 Path lockTokenCur = optimisticLockProfile(profile);
142 if (lockToken != lockTokenCur) {
143 printError(format("profile '%1%' changed while we were busy; restarting") % profile);
144 return false;
145 }
146
147 debug(format("switching to new user environment"));
148 Path generation = createGeneration(ref<LocalFSStore>(store2), profile, topLevelOut);
149 switchLink(profile, generation);
150 }
151
152 return true;
153 }
154
155
156 }
157