From 451dbf687f65b42d70e64c3858771abaab51b356 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 2 Feb 2007 01:52:42 +0000 Subject: [PATCH] * nix-env now maintains meta info (from the `meta' derivation attribute) about installed packages in user environments. Thus, an operation like `nix-env -q --description' shows useful information not only on available packages but also on installed packages. * nix-env now passes the entire manifest as an argument to the Nix expression of the user environment builder (not just a list of paths), so that in particular the user environment builder has access to the meta attributes. * New operation `--set-flag' in nix-env to change meta info of installed packages. This will be useful to pass per-package policies to the user environment builder (e.g., how to resolve collision or whether to disable a package (NIX-80)) or upgrade policies in nix-env (e.g., that a package should be "masked", that is, left untouched by upgrade actions). Example: $ nix-env --set-flag enabled false ghc-6.4 --- doc/manual/release-notes.xml | 16 +++++++ src/libexpr/get-drvs.cc | 10 +++++ src/libexpr/get-drvs.hh | 5 +++ src/libexpr/nixexpr.cc | 2 +- src/libexpr/nixexpr.hh | 2 +- src/nix-env/nix-env.cc | 86 ++++++++++++++++++++++++++++++------ 6 files changed, 105 insertions(+), 16 deletions(-) diff --git a/doc/manual/release-notes.xml b/doc/manual/release-notes.xml index 53c8d4bf2..ead19962d 100644 --- a/doc/manual/release-notes.xml +++ b/doc/manual/release-notes.xml @@ -14,6 +14,14 @@ TODO: multi-user support. + + nix-prefetch-url now by default + computes the SHA-256 hash of the file instead of the MD5 hash. In + calls to fetchurl you should pass an + sha256 attribute instead of + md5. You can pass either a hexadecimal or a + base-32 encoding of the hash. + nix-store has a new operation () @@ -51,6 +59,14 @@ TODO: . + + + TODO: nix-env now maintains meta + info about installed packages in user + environments. + + TODO: nix-env + . TODO: new built-ins diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index ba9fbcd6d..bd0ec4781 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -60,6 +60,16 @@ MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const } +void DrvInfo::setMetaInfo(const MetaInfo & meta) +{ + ATermMap metaAttrs; + for (MetaInfo::const_iterator i = meta.begin(); i != meta.end(); ++i) + metaAttrs.set(toATerm(i->first), + makeAttrRHS(makeStr(i->second), makeNoPos())); + attrs->set(toATerm("meta"), makeAttrs(metaAttrs)); +} + + /* Cache for already evaluated derivations. Usually putting ATerms in a STL container is unsafe (they're not scanning for GC roots), but here it doesn't matter; everything in this set is reachable from diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh index 3dac56a4f..920197d1f 100644 --- a/src/libexpr/get-drvs.hh +++ b/src/libexpr/get-drvs.hh @@ -26,6 +26,9 @@ public: string attrPath; /* path towards the derivation */ string system; + /* !!! these should really be hidden, and setMetaInfo() should + make a copy since the ATermMap can be shared between multiple + DrvInfos. */ boost::shared_ptr attrs; string queryDrvPath(EvalState & state) const; @@ -41,6 +44,8 @@ public: { outPath = s; } + + void setMetaInfo(const MetaInfo & meta); }; diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 7502fd166..123513594 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -319,7 +319,7 @@ struct Canonicalise : TermFun }; -Expr canonicaliseExpr(Expr & e) +Expr canonicaliseExpr(Expr e) { Canonicalise canonicalise; return bottomupRewrite(canonicalise, e); diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index d76bc1060..420911a87 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -96,7 +96,7 @@ void checkVarDefs(const ATermMap & def, Expr e); /* Canonicalise a Nix expression by sorting attributes and removing location information. */ -Expr canonicaliseExpr(Expr & e); +Expr canonicaliseExpr(Expr e); /* Create an expression representing a boolean. */ diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 113f49ccb..5dab2e60b 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -164,7 +164,11 @@ static void createUserEnv(EvalState & state, const DrvInfos & elems, for (DrvInfos::const_iterator i = elems.begin(); i != elems.end(); ++i) { + /* 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) : ""; + ATermList as = ATmakeList4( makeBind(toATerm("type"), makeStr("derivation"), makeNoPos()), @@ -174,10 +178,18 @@ static void createUserEnv(EvalState & state, const DrvInfos & elems, makeStr(i->system), makeNoPos()), makeBind(toATerm("outPath"), makeStr(i->queryOutPath(state)), makeNoPos())); + if (drvPath != "") as = ATinsert(as, makeBind(toATerm("drvPath"), makeStr(drvPath), makeNoPos())); + + if (i->attrs->get(toATerm("meta"))) as = ATinsert(as, + makeBind(toATerm("meta"), + strictEvalExpr(state, i->attrs->get(toATerm("meta"))), + makeNoPos())); + manifest = ATinsert(manifest, makeAttrs(as)); + inputs = ATinsert(inputs, makeStr(i->queryOutPath(state))); /* This is only necessary when installing store paths, e.g., @@ -192,13 +204,13 @@ static void createUserEnv(EvalState & state, const DrvInfos & elems, /* 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(makeList(ATreverse(manifest))), references); + atPrint(canonicaliseExpr(makeList(ATreverse(manifest)))), references); Expr topLevel = makeCall(envBuilder, makeAttrs(ATmakeList3( makeBind(toATerm("system"), makeStr(thisSystem), makeNoPos()), makeBind(toATerm("derivations"), - makeList(ATreverse(inputs)), makeNoPos()), + makeList(ATreverse(manifest)), makeNoPos()), makeBind(toATerm("manifest"), makeStr(manifestFile, singleton(manifestFile)), makeNoPos()) ))); @@ -506,8 +518,7 @@ typedef enum { utLt, utLeq, utEq, utAlways } UpgradeType; static void upgradeDerivations(Globals & globals, - const Strings & args, const Path & profile, - UpgradeType upgradeType) + const Strings & args, UpgradeType upgradeType) { debug(format("upgrading derivations")); @@ -518,8 +529,8 @@ static void upgradeDerivations(Globals & globals, /* Load the currently installed derivations. */ PathLocks lock; - lockProfile(lock, profile); - DrvInfos installedElems = queryInstalled(globals.state, profile); + lockProfile(lock, globals.profile); + DrvInfos installedElems = queryInstalled(globals.state, globals.profile); /* Fetch all derivations from the input file. */ DrvInfos availElems; @@ -577,7 +588,7 @@ static void upgradeDerivations(Globals & globals, } createUserEnv(globals.state, newElems, - profile, globals.keepDerivations); + globals.profile, globals.keepDerivations); } @@ -593,7 +604,55 @@ static void opUpgrade(Globals & globals, else if (*i == "--always") upgradeType = utAlways; else throw UsageError(format("unknown flag `%1%'") % *i); - upgradeDerivations(globals, opArgs, globals.profile, upgradeType); + upgradeDerivations(globals, opArgs, upgradeType); +} + + +static void setMetaFlag(EvalState & state, DrvInfo & drv, + const string & name, const string & value) +{ + MetaInfo meta = drv.queryMetaInfo(state); + meta[name] = value; + drv.setMetaInfo(meta); +} + + +static void opSetFlag(Globals & globals, + Strings opFlags, Strings opArgs) +{ + if (opFlags.size() > 0) + throw UsageError(format("unknown flag `%1%'") % opFlags.front()); + if (opArgs.size() < 2) + throw UsageError("not enough arguments to `--set-flag'"); + + Strings::iterator arg = opArgs.begin(); + string flagName = *arg++; + string flagValue = *arg++; + DrvNames selectors = drvNamesFromArgs(Strings(arg, opArgs.end())); + + /* Load the currently installed derivations. */ + PathLocks lock; + lockProfile(lock, globals.profile); + DrvInfos installedElems = queryInstalled(globals.state, globals.profile); + + /* Update all matching derivations. */ + for (DrvInfos::iterator i = installedElems.begin(); + i != installedElems.end(); ++i) + { + DrvName drvName(i->name); + for (DrvNames::iterator j = selectors.begin(); + j != selectors.end(); ++j) + if (j->matches(drvName)) { + printMsg(lvlInfo, + format("setting flag on `%1%'") % i->name); + setMetaFlag(globals.state, *i, flagName, flagValue); + break; + } + } + + /* Write the new user environment. */ + createUserEnv(globals.state, installedElems, + globals.profile, globals.keepDerivations); } @@ -1167,18 +1226,18 @@ void run(Strings args) op = opUninstall; else if (arg == "--upgrade" || arg == "-u") op = opUpgrade; + else if (arg == "--set-flag") + op = opSetFlag; else if (arg == "--set") op = opSet; else if (arg == "--query" || arg == "-q") op = opQuery; else if (arg == "--import" || arg == "-I") /* !!! bad name */ op = opDefaultExpr; - else if (arg == "--profile" || arg == "-p") { + else if (arg == "--profile" || arg == "-p") globals.profile = absPath(needArg(i, args, arg)); - } - else if (arg == "--file" || arg == "-f") { + else if (arg == "--file" || arg == "-f") globals.instSource.nixExprPath = absPath(needArg(i, args, arg)); - } else if (arg == "--switch-profile" || arg == "-S") op = opSwitchProfile; else if (arg == "--switch-generation" || arg == "-G") @@ -1195,9 +1254,8 @@ void run(Strings args) } else if (arg == "--preserve-installed" || arg == "-P") globals.preserveInstalled = true; - else if (arg == "--system-filter") { + else if (arg == "--system-filter") globals.instSource.systemFilter = needArg(i, args, arg); - } else if (arg[0] == '-') opFlags.push_back(arg); else