From 9898746ef3732979bf30e9048021b6232ddf15ac Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 19 Nov 2003 17:27:16 +0000 Subject: * nix-env: a tool to manage user environments. * Replace all directory reading code by a generic readDirectory() function. --- src/nix-env/Makefile.am | 11 ++ src/nix-env/main.cc | 270 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 281 insertions(+) create mode 100644 src/nix-env/Makefile.am create mode 100644 src/nix-env/main.cc (limited to 'src/nix-env') diff --git a/src/nix-env/Makefile.am b/src/nix-env/Makefile.am new file mode 100644 index 000000000..add54581b --- /dev/null +++ b/src/nix-env/Makefile.am @@ -0,0 +1,11 @@ +bin_PROGRAMS = nix-env + +nix_env_SOURCES = main.cc +nix_env_LDADD = ../libmain/libmain.a ../libexpr/libexpr.a \ + ../libstore/libstore.a ../libutil/libutil.a \ + ../boost/format/libformat.a -L../../externals/inst/lib -ldb_cxx \ + -lsglr -lATB -lconversion -lasfix2 -lmept -lATerm + +AM_CXXFLAGS = \ + -I.. -I../../externals/inst/include -I../libutil -I../libstore \ + -I../libexpr -I../libmain diff --git a/src/nix-env/main.cc b/src/nix-env/main.cc new file mode 100644 index 000000000..4296ce509 --- /dev/null +++ b/src/nix-env/main.cc @@ -0,0 +1,270 @@ +#include "globals.hh" +#include "normalise.hh" +#include "shared.hh" +#include "parser.hh" +#include "eval.hh" + + +typedef void (* Operation) (EvalState & state, + Strings opFlags, Strings opArgs); + + +struct DrvInfo +{ + string name; + Path drvPath; + Path outPath; +}; + +typedef map DrvInfos; + + +bool parseDerivation(EvalState & state, Expr e, DrvInfo & drv) +{ + ATMatcher m; + + e = evalExpr(state, e); + if (!(atMatch(m, e) >> "Attrs")) return false; + Expr a = queryAttr(e, "type"); + if (!a || evalString(state, a) != "derivation") return false; + + a = queryAttr(e, "name"); + if (!a) throw badTerm("derivation name missing", e); + drv.name = evalString(state, a); + + a = queryAttr(e, "drvPath"); + if (!a) throw badTerm("derivation path missing", e); + drv.drvPath = evalPath(state, a); + + a = queryAttr(e, "outPath"); + if (!a) throw badTerm("output path missing", e); + drv.outPath = evalPath(state, a); + + return true; +} + + +bool parseDerivations(EvalState & state, Expr e, DrvInfos & drvs) +{ + e = evalExpr(state, e); + + ATermMap drvMap; + queryAllAttrs(e, drvMap); + + for (ATermIterator i(drvMap.keys()); i; ++i) { + DrvInfo drv; + debug(format("evaluating attribute `%1%'") % *i); + if (parseDerivation(state, drvMap.get(*i), drv)) + drvs[drv.name] = drv; + } + + return true; +} + + +void loadDerivations(EvalState & state, Path nePath, DrvInfos & drvs) +{ + Expr e = parseExprFromFile(absPath(nePath)); + if (!parseDerivations(state, e, drvs)) + throw badTerm("expected set of derivations", e); +} + + +static Path getLinksDir() +{ + return canonPath(nixStateDir + "/links"); +} + + +Path createLink(Path outPath, Path drvPath) +{ + Path linksDir = getLinksDir(); + + unsigned int num = 0; + + Strings names = readDirectory(linksDir); + for (Strings::iterator i = names.begin(); i != names.end(); ++i) { + istringstream s(*i); + unsigned int n; + if (s >> n && s.eof() && n > num) num = n + 1; + } + + Path linkPath = (format("%1%/%2%") % linksDir % num).str(); + + if (symlink(outPath.c_str(), linkPath.c_str()) != 0) + throw SysError(format("creating symlink `%1%'") % linkPath); + + return linkPath; +} + + +void installDerivations(EvalState & state, + Path nePath, Strings drvNames) +{ + debug(format("installing derivations from `%1%'") % nePath); + + /* Fetch all derivations from the input file. */ + DrvInfos availDrvs; + loadDerivations(state, nePath, availDrvs); + + /* Filter out the ones we're not interested in. */ + DrvInfos selectedDrvs; + for (Strings::iterator i = drvNames.begin(); + i != drvNames.end(); ++i) + { + DrvInfos::iterator j = availDrvs.find(*i); + if (j == availDrvs.end()) + throw Error(format("unknown derivation `%1%'") % *i); + else + selectedDrvs[j->first] = j->second; + } + + /* Get the environment builder expression. */ + Expr envBuilder = parseExprFromFile("/home/eelco/nix/corepkgs/buildenv"); /* !!! */ + + /* Construct the whole top level derivation. */ + ATermList inputs = ATempty; + for (DrvInfos::iterator i = selectedDrvs.begin(); + i != selectedDrvs.end(); ++i) + { + ATerm t = ATmake( + "Attrs([" + "Bind(\"type\", Str(\"derivation\")), " + "Bind(\"name\", Str()), " + "Bind(\"drvPath\", Path()), " + "Bind(\"outPath\", Path())" + "])", + i->second.name.c_str(), + i->second.drvPath.c_str(), + i->second.outPath.c_str()); + inputs = ATinsert(inputs, t); + } + + ATerm inputs2 = ATmake("List()", ATreverse(inputs)); + + /* Also write a copy of the list of inputs to the store; we need + it for future modifications of the environment. */ + Path inputsFile = writeTerm(inputs2, "-env-inputs"); + + Expr topLevel = ATmake( + "Call(, Attrs([" + "Bind(\"system\", Str()), " + "Bind(\"derivations\", ), " // !!! redundant + "Bind(\"manifest\", Path())" + "]))", + envBuilder, thisSystem.c_str(), inputs2, inputsFile.c_str()); + + /* Instantiate it. */ + debug(format("evaluating builder expression `%1%'") % topLevel); + DrvInfo topLevelDrv; + if (!parseDerivation(state, topLevel, topLevelDrv)) + abort(); + + /* Realise the resulting store expression. */ + debug(format("realising user environment")); + Path nfPath = normaliseStoreExpr(topLevelDrv.drvPath); + realiseClosure(nfPath); + + /* Switch the current user environment to the output path. */ + debug(format("switching to new user environment")); + Path linkPath = createLink(topLevelDrv.outPath, topLevelDrv.drvPath); +// switchLink(current"), link); +} + + +static void opInstall(EvalState & state, + Strings opFlags, Strings opArgs) +{ + if (opArgs.size() < 1) throw UsageError("Nix expression expected"); + + Path nePath = opArgs.front(); + opArgs.pop_front(); + + installDerivations(state, nePath, + Strings(opArgs.begin(), opArgs.end())); +} + + +static void opQuery(EvalState & state, + Strings opFlags, Strings opArgs) +{ + enum { qName } query = qName; + enum { sInstalled, sAvailable } source = sInstalled; + + for (Strings::iterator i = opFlags.begin(); + i != opFlags.end(); ++i) + if (*i == "--name") query = qName; + else if (*i == "--installed") source = sInstalled; + else if (*i == "--available" || *i == "-f") source = sAvailable; + else throw UsageError(format("unknown flag `%1%'") % *i); + + /* Obtain derivation information from the specified source. */ + DrvInfos drvs; + + switch (source) { + + case sInstalled: + break; + + case sAvailable: { + Path nePath = opArgs.front(); + opArgs.pop_front(); + loadDerivations(state, nePath, drvs); + break; + } + + default: abort(); + } + + /* Perform the specified query on the derivations. */ + switch (query) { + + case qName: { + if (opArgs.size() != 0) throw UsageError("no arguments expected"); + for (DrvInfos::iterator i = drvs.begin(); i != drvs.end(); ++i) + cout << format("%1%\n") % i->second.name; + break; + } + + default: abort(); + } +} + + +void run(Strings args) +{ + EvalState state; + Strings opFlags, opArgs; + Operation op = 0; + + for (Strings::iterator i = args.begin(); i != args.end(); ++i) { + string arg = *i; + + Operation oldOp = op; + + if (arg == "--install" || arg == "-i") + op = opInstall; + if (arg == "--query" || arg == "-q") + op = opQuery; + else if (arg == "--verbose" || arg == "-v") + verbosity = (Verbosity) ((int) verbosity + 1); + else if (arg[0] == '-') + opFlags.push_back(arg); + else + opArgs.push_back(arg); + + if (oldOp && oldOp != op) + throw UsageError("only one operation may be specified"); + } + + if (!op) throw UsageError("no operation specified"); + + openDB(); + + op(state, opFlags, opArgs); + + printEvalStats(state); +} + + +string programId = "nix-env"; -- cgit v1.2.3