2020-05-27 21:56:34 +01:00
|
|
|
#include "nix-env/user-env.hh"
|
2020-05-19 15:54:39 +01:00
|
|
|
|
2020-05-19 04:52:47 +01:00
|
|
|
#include <glog/logging.h>
|
2020-05-19 15:54:39 +01:00
|
|
|
|
2020-05-27 21:56:34 +01:00
|
|
|
#include "libexpr/eval-inline.hh"
|
|
|
|
#include "libexpr/eval.hh"
|
|
|
|
#include "libmain/shared.hh"
|
|
|
|
#include "libstore/derivations.hh"
|
|
|
|
#include "libstore/globals.hh"
|
|
|
|
#include "libstore/profiles.hh"
|
|
|
|
#include "libstore/store-api.hh"
|
2020-08-13 22:06:23 -04:00
|
|
|
#include "libutil/status.hh"
|
2020-05-27 21:56:34 +01:00
|
|
|
#include "libutil/util.hh"
|
2010-04-19 10:47:56 +00:00
|
|
|
|
|
|
|
namespace nix {
|
|
|
|
|
|
|
|
DrvInfos queryInstalled(EvalState& state, const Path& userEnv) {
|
2010-04-19 12:10:04 +00:00
|
|
|
DrvInfos elems;
|
2010-04-21 15:08:58 +00:00
|
|
|
Path manifestFile = userEnv + "/manifest.nix";
|
|
|
|
if (pathExists(manifestFile)) {
|
2020-08-01 15:32:00 -07:00
|
|
|
Value v;
|
2011-08-06 13:02:55 +00:00
|
|
|
state.evalFile(manifestFile, v);
|
2020-08-13 16:40:27 -07:00
|
|
|
std::unique_ptr<Bindings> bindings(Bindings::New());
|
|
|
|
getDerivations(state, v, "", bindings.get(), elems, false);
|
2013-11-19 11:18:13 +01:00
|
|
|
}
|
2010-04-19 10:47:56 +00:00
|
|
|
return elems;
|
2010-04-19 12:10:04 +00:00
|
|
|
}
|
|
|
|
|
2010-04-21 15:08:58 +00:00
|
|
|
bool createUserEnv(EvalState& state, DrvInfos& elems, const Path& profile,
|
2020-05-24 22:29:21 +01:00
|
|
|
bool keepDerivations, const std::string& lockToken) {
|
2010-04-21 15:08:58 +00:00
|
|
|
/* Build the components in the user environment, if they don't
|
|
|
|
exist already. */
|
|
|
|
PathSet drvsToBuild;
|
2015-07-17 19:24:28 +02:00
|
|
|
for (auto& i : elems) {
|
2020-05-20 22:27:37 +01:00
|
|
|
if (!i.queryDrvPath().empty()) {
|
2015-07-17 19:24:28 +02:00
|
|
|
drvsToBuild.insert(i.queryDrvPath());
|
2020-05-19 18:55:58 +01:00
|
|
|
}
|
2020-05-19 20:47:23 +01:00
|
|
|
}
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2020-05-19 04:52:47 +01:00
|
|
|
DLOG(INFO) << "building user environment dependencies";
|
2020-08-13 22:06:23 -04:00
|
|
|
util::OkOrThrow(state.store->buildPaths(
|
|
|
|
drvsToBuild, state.repair != 0u ? bmRepair : bmNormal));
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2010-04-21 15:08:58 +00:00
|
|
|
/* Construct the whole top level derivation. */
|
|
|
|
PathSet references;
|
2020-08-01 15:32:00 -07:00
|
|
|
Value manifest;
|
2010-04-21 15:08:58 +00:00
|
|
|
state.mkList(manifest, elems.size());
|
|
|
|
unsigned int n = 0;
|
2015-07-17 19:24:28 +02:00
|
|
|
for (auto& i : elems) {
|
2010-04-21 15:08:58 +00:00
|
|
|
/* Create a pseudo-derivation containing the name, system,
|
2012-12-04 14:20:36 +01:00
|
|
|
output paths, and optionally the derivation path, as well
|
|
|
|
as the meta attributes. */
|
2015-07-17 19:24:28 +02:00
|
|
|
Path drvPath = keepDerivations ? i.queryDrvPath() : "";
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2020-07-18 16:40:15 +01:00
|
|
|
Value* v = state.allocValue();
|
|
|
|
(*manifest.list)[n++] = v;
|
|
|
|
state.mkAttrs(*v, 16);
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2020-07-18 16:40:15 +01:00
|
|
|
mkString(*state.allocAttr(*v, state.sType), "derivation");
|
|
|
|
mkString(*state.allocAttr(*v, state.sName), i.queryName());
|
2017-07-17 19:02:56 +02:00
|
|
|
auto system = i.querySystem();
|
|
|
|
if (!system.empty()) {
|
2020-07-18 16:40:15 +01:00
|
|
|
mkString(*state.allocAttr(*v, state.sSystem), system);
|
2020-05-19 18:55:58 +01:00
|
|
|
}
|
2020-07-18 16:40:15 +01:00
|
|
|
mkString(*state.allocAttr(*v, state.sOutPath), i.queryOutPath());
|
2020-05-20 22:27:37 +01:00
|
|
|
if (!drvPath.empty()) {
|
2020-07-18 16:40:15 +01:00
|
|
|
mkString(*state.allocAttr(*v, state.sDrvPath), i.queryDrvPath());
|
2020-05-19 20:47:23 +01:00
|
|
|
}
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2016-02-23 14:19:14 +01:00
|
|
|
// Copy each output meant for installation.
|
|
|
|
DrvInfo::Outputs outputs = i.queryOutputs(true);
|
2020-07-18 16:40:15 +01:00
|
|
|
Value& vOutputs = *state.allocAttr(*v, state.sOutputs);
|
2012-12-04 14:20:36 +01:00
|
|
|
state.mkList(vOutputs, outputs.size());
|
|
|
|
unsigned int m = 0;
|
2015-07-17 19:24:28 +02:00
|
|
|
for (auto& j : outputs) {
|
2020-07-18 16:40:15 +01:00
|
|
|
mkString(*((*vOutputs.list)[m++] = state.allocValue()), j.first);
|
|
|
|
Value& vOutputs = *state.allocAttr(*v, state.symbols.Create(j.first));
|
2010-10-24 20:09:37 +00:00
|
|
|
state.mkAttrs(vOutputs, 2);
|
2015-07-17 19:24:28 +02:00
|
|
|
mkString(*state.allocAttr(vOutputs, state.sOutPath), j.second);
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2012-12-04 14:20:36 +01:00
|
|
|
/* This is only necessary when installing store paths, e.g.,
|
2015-07-17 19:24:28 +02:00
|
|
|
`nix-env -i /nix/store/abcd...-foo'. */
|
2014-03-11 17:31:13 +01:00
|
|
|
state.store->addTempRoot(j.second);
|
2010-10-24 19:52:33 +00:00
|
|
|
state.store->ensurePath(j.second);
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2010-04-21 15:08:58 +00:00
|
|
|
references.insert(j.second);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy the meta attributes.
|
2020-07-18 16:40:15 +01:00
|
|
|
Value& vMeta = *state.allocAttr(*v, state.sMeta);
|
Eliminate the "store" global variable
Also, move a few free-standing functions into StoreAPI and Derivation.
Also, introduce a non-nullable smart pointer, ref<T>, which is just a
wrapper around std::shared_ptr ensuring that the pointer is never
null. (For reference-counted values, this is better than passing a
"T&", because the latter doesn't maintain the refcount. Usually, the
caller will have a shared_ptr keeping the value alive, but that's not
always the case, e.g., when passing a reference to a std::thread via
std::bind.)
2016-02-04 14:28:26 +01:00
|
|
|
state.mkAttrs(vMeta, 16);
|
|
|
|
StringSet metaNames = i.queryMetaNames();
|
|
|
|
for (auto& j : metaNames) {
|
2015-07-17 19:24:28 +02:00
|
|
|
Value* v = i.queryMeta(j);
|
2020-05-20 22:27:37 +01:00
|
|
|
if (v == nullptr) {
|
2013-11-19 14:29:39 +01:00
|
|
|
continue;
|
2020-05-19 18:55:58 +01:00
|
|
|
}
|
2020-05-21 04:56:22 +01:00
|
|
|
vMeta.attrs->push_back(Attr(state.symbols.Create(j), v));
|
2020-05-17 16:31:57 +01:00
|
|
|
}
|
|
|
|
|
2020-05-20 22:27:37 +01:00
|
|
|
if (!drvPath.empty()) {
|
Eliminate the "store" global variable
Also, move a few free-standing functions into StoreAPI and Derivation.
Also, introduce a non-nullable smart pointer, ref<T>, which is just a
wrapper around std::shared_ptr ensuring that the pointer is never
null. (For reference-counted values, this is better than passing a
"T&", because the latter doesn't maintain the refcount. Usually, the
caller will have a shared_ptr keeping the value alive, but that's not
always the case, e.g., when passing a reference to a std::thread via
std::bind.)
2016-02-04 14:28:26 +01:00
|
|
|
references.insert(drvPath);
|
2020-05-19 18:55:58 +01:00
|
|
|
}
|
2020-05-17 16:31:57 +01:00
|
|
|
}
|
|
|
|
|
Eliminate the "store" global variable
Also, move a few free-standing functions into StoreAPI and Derivation.
Also, introduce a non-nullable smart pointer, ref<T>, which is just a
wrapper around std::shared_ptr ensuring that the pointer is never
null. (For reference-counted values, this is better than passing a
"T&", because the latter doesn't maintain the refcount. Usually, the
caller will have a shared_ptr keeping the value alive, but that's not
always the case, e.g., when passing a reference to a std::thread via
std::bind.)
2016-02-04 14:28:26 +01:00
|
|
|
/* Also write a copy of the list of user environment elements to
|
|
|
|
the store; we need it for future modifications of the
|
2010-04-21 15:08:58 +00:00
|
|
|
environment. */
|
|
|
|
Path manifestFile = state.store->addTextToStore(
|
|
|
|
"env-manifest.nix", (format("%1%") % manifest).str(), references);
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2010-04-21 15:08:58 +00:00
|
|
|
/* Get the environment builder expression. */
|
2020-08-01 15:32:00 -07:00
|
|
|
Value envBuilder;
|
2012-01-03 00:16:29 +00:00
|
|
|
state.evalFile(state.findFile("nix/buildenv.nix"), envBuilder);
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2010-04-21 15:08:58 +00:00
|
|
|
/* Construct a Nix expression that calls the user environment
|
|
|
|
builder with the manifest as argument. */
|
2020-08-01 15:32:00 -07:00
|
|
|
Value args;
|
|
|
|
Value topLevel;
|
2010-10-24 20:09:37 +00:00
|
|
|
state.mkAttrs(args, 3);
|
2020-05-21 04:56:22 +01:00
|
|
|
mkString(*state.allocAttr(args, state.symbols.Create("manifest")),
|
2016-05-04 16:04:52 +02:00
|
|
|
manifestFile, {manifestFile});
|
2020-05-21 04:56:22 +01:00
|
|
|
args.attrs->push_back(Attr(state.symbols.Create("derivations"), &manifest));
|
2010-04-21 15:08:58 +00:00
|
|
|
mkApp(topLevel, envBuilder, args);
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2010-04-21 15:08:58 +00:00
|
|
|
/* Evaluate it. */
|
2020-05-19 04:52:47 +01:00
|
|
|
DLOG(INFO) << "evaluating user environment builder";
|
2013-11-19 14:09:03 +01:00
|
|
|
state.forceValue(topLevel);
|
|
|
|
PathSet context;
|
2020-05-22 01:58:12 +01:00
|
|
|
Attr& aDrvPath(topLevel.attrs->find(state.sDrvPath)->second);
|
2020-05-20 22:27:37 +01:00
|
|
|
Path topLevelDrv =
|
|
|
|
state.coerceToPath(aDrvPath.pos != nullptr ? *(aDrvPath.pos) : noPos,
|
|
|
|
*(aDrvPath.value), context);
|
2020-05-22 01:58:12 +01:00
|
|
|
Attr& aOutPath(topLevel.attrs->find(state.sOutPath)->second);
|
2020-05-20 22:27:37 +01:00
|
|
|
Path topLevelOut =
|
|
|
|
state.coerceToPath(aOutPath.pos != nullptr ? *(aOutPath.pos) : noPos,
|
|
|
|
*(aOutPath.value), context);
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2010-04-21 15:08:58 +00:00
|
|
|
/* Realise the resulting store expression. */
|
2020-05-19 04:52:47 +01:00
|
|
|
DLOG(INFO) << "building user environment";
|
2020-08-13 22:06:23 -04:00
|
|
|
util::OkOrThrow(state.store->buildPaths(
|
|
|
|
{topLevelDrv}, state.repair != 0u ? bmRepair : bmNormal));
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2010-04-21 15:08:58 +00:00
|
|
|
/* Switch the current user environment to the output path. */
|
2016-06-02 13:33:49 +02:00
|
|
|
auto store2 = state.store.dynamic_pointer_cast<LocalFSStore>();
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2016-06-02 13:33:49 +02:00
|
|
|
if (store2) {
|
|
|
|
PathLocks lock;
|
|
|
|
lockProfile(lock, profile);
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2016-06-02 13:33:49 +02:00
|
|
|
Path lockTokenCur = optimisticLockProfile(profile);
|
|
|
|
if (lockToken != lockTokenCur) {
|
2020-05-19 04:52:47 +01:00
|
|
|
LOG(WARNING) << "profile '" << profile
|
|
|
|
<< "' changed while we were busy; restarting";
|
2016-06-02 13:33:49 +02:00
|
|
|
return false;
|
|
|
|
}
|
2010-04-21 15:08:58 +00:00
|
|
|
|
2020-05-19 04:52:47 +01:00
|
|
|
DLOG(INFO) << "switching to new user environment";
|
2010-04-21 15:08:58 +00:00
|
|
|
Path generation =
|
|
|
|
createGeneration(ref<LocalFSStore>(store2), profile, topLevelOut);
|
2016-06-02 13:33:49 +02:00
|
|
|
switchLink(profile, generation);
|
2010-04-21 15:08:58 +00:00
|
|
|
}
|
|
|
|
|
2020-05-17 16:31:57 +01:00
|
|
|
return true;
|
2010-04-19 10:47:56 +00:00
|
|
|
}
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2010-04-19 10:47:56 +00:00
|
|
|
} // namespace nix
|