about summary refs log tree commit diff
path: root/third_party/nix/src/nix-env/nix-env.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/nix/src/nix-env/nix-env.cc')
-rw-r--r--third_party/nix/src/nix-env/nix-env.cc1546
1 files changed, 0 insertions, 1546 deletions
diff --git a/third_party/nix/src/nix-env/nix-env.cc b/third_party/nix/src/nix-env/nix-env.cc
deleted file mode 100644
index ee36510f02..0000000000
--- a/third_party/nix/src/nix-env/nix-env.cc
+++ /dev/null
@@ -1,1546 +0,0 @@
-#include <algorithm>
-#include <cerrno>
-#include <ctime>
-#include <iostream>
-#include <sstream>
-
-#include <absl/strings/match.h>
-#include <absl/strings/numbers.h>
-#include <glog/logging.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "libexpr/attr-path.hh"
-#include "libexpr/common-eval-args.hh"
-#include "libexpr/eval.hh"
-#include "libexpr/get-drvs.hh"
-#include "libexpr/names.hh"
-#include "libexpr/value-to-json.hh"
-#include "libmain/shared.hh"
-#include "libstore/derivations.hh"
-#include "libstore/globals.hh"
-#include "libstore/profiles.hh"
-#include "libstore/store-api.hh"
-#include "libutil/json.hh"
-#include "libutil/status.hh"
-#include "libutil/util.hh"
-#include "libutil/xml-writer.hh"
-#include "nix-env/user-env.hh"
-#include "nix/legacy.hh"
-
-using namespace nix;
-using std::cout;
-
-using InstallSourceType = enum {
-  srcNixExprDrvs,
-  srcNixExprs,
-  srcStorePaths,
-  srcProfile,
-  srcAttrPath,
-  srcUnknown
-};
-
-struct InstallSourceInfo {
-  InstallSourceType type;
-  Path nixExprPath;         /* for srcNixExprDrvs, srcNixExprs */
-  Path profile;             /* for srcProfile */
-  std::string systemFilter; /* for srcNixExprDrvs */
-  std::unique_ptr<Bindings> autoArgs;
-};
-
-struct Globals {
-  InstallSourceInfo instSource;
-  Path profile;
-  std::shared_ptr<EvalState> state;
-  bool dryRun;
-  bool preserveInstalled;
-  bool removeAll;
-  std::string forceName;
-  bool prebuiltOnly;
-};
-
-using Operation = void (*)(Globals&, Strings, Strings);
-
-static std::string needArg(Strings::iterator& i, Strings& args,
-                           const std::string& arg) {
-  if (i == args.end()) {
-    throw UsageError(format("'%1%' requires an argument") % arg);
-  }
-  return *i++;
-}
-
-static bool parseInstallSourceOptions(Globals& globals, Strings::iterator& i,
-                                      Strings& args, const std::string& arg) {
-  if (arg == "--from-expression" || arg == "-E") {
-    globals.instSource.type = srcNixExprs;
-  } else if (arg == "--from-profile") {
-    globals.instSource.type = srcProfile;
-    globals.instSource.profile = needArg(i, args, arg);
-  } else if (arg == "--attr" || arg == "-A") {
-    globals.instSource.type = srcAttrPath;
-  } else {
-    return false;
-  }
-  return true;
-}
-
-static bool isNixExpr(const Path& path, struct stat& st) {
-  return S_ISREG(st.st_mode) ||
-         (S_ISDIR(st.st_mode) && pathExists(path + "/default.nix"));
-}
-
-static void getAllExprs(EvalState& state, const Path& path, StringSet& attrs,
-                        Value& v) {
-  StringSet namesSorted;
-  for (auto& i : readDirectory(path)) {
-    namesSorted.insert(i.name);
-  }
-
-  for (auto& i : namesSorted) {
-    /* Ignore the manifest.nix used by profiles.  This is
-       necessary to prevent it from showing up in channels (which
-       are implemented using profiles). */
-    if (i == "manifest.nix") {
-      continue;
-    }
-
-    Path path2 = path + "/" + i;
-
-    struct stat st;
-    if (stat(path2.c_str(), &st) == -1) {
-      continue;  // ignore dangling symlinks in ~/.nix-defexpr
-    }
-
-    if (isNixExpr(path2, st) &&
-        (!S_ISREG(st.st_mode) || absl::EndsWith(path2, ".nix"))) {
-      /* Strip off the `.nix' filename suffix (if applicable),
-         otherwise the attribute cannot be selected with the
-         `-A' option.  Useful if you want to stick a Nix
-         expression directly in ~/.nix-defexpr. */
-      std::string attrName = i;
-      if (absl::EndsWith(attrName, ".nix")) {
-        attrName = std::string(attrName, 0, attrName.size() - 4);
-      }
-      if (attrs.find(attrName) != attrs.end()) {
-        LOG(WARNING) << "name collision in input Nix expressions, skipping '"
-                     << path2 << "'";
-        continue;
-      }
-      attrs.insert(attrName);
-      /* Load the expression on demand. */
-      Value& vFun = state.getBuiltin("import");
-      Value& vArg(*state.allocValue());
-      mkString(vArg, path2);
-      mkApp(*state.allocAttr(v, state.symbols.Create(attrName)), vFun, vArg);
-    } else if (S_ISDIR(st.st_mode)) {
-      /* `path2' is a directory (with no default.nix in it);
-         recurse into it. */
-      getAllExprs(state, path2, attrs, v);
-    }
-  }
-}
-
-static void loadSourceExpr(EvalState& state, const Path& path, Value& v) {
-  struct stat st;
-  if (stat(path.c_str(), &st) == -1) {
-    throw SysError(format("getting information about '%1%'") % path);
-  }
-
-  if (isNixExpr(path, st)) {
-    state.evalFile(path, v);
-  }
-
-  /* The path is a directory.  Put the Nix expressions in the
-     directory in a set, with the file name of each expression as
-     the attribute name.  Recurse into subdirectories (but keep the
-     set flat, not nested, to make it easier for a user to have a
-     ~/.nix-defexpr directory that includes some system-wide
-     directory). */
-  else if (S_ISDIR(st.st_mode)) {
-    state.mkAttrs(v, 1024);
-    state.mkList(*state.allocAttr(v, state.symbols.Create("_combineChannels")));
-    StringSet attrs;
-    getAllExprs(state, path, attrs, v);
-  }
-
-  else {
-    throw Error("path '%s' is not a directory or a Nix expression", path);
-  }
-}
-
-static void loadDerivations(EvalState& state, const Path& nixExprPath,
-                            const std::string& systemFilter, Bindings* autoArgs,
-                            const std::string& pathPrefix, DrvInfos& elems) {
-  Value vRoot;
-  loadSourceExpr(state, nixExprPath, vRoot);
-
-  Value& v(*findAlongAttrPath(state, pathPrefix, autoArgs, vRoot));
-
-  getDerivations(state, v, pathPrefix, autoArgs, elems, true);
-
-  /* Filter out all derivations not applicable to the current
-     system. */
-  for (DrvInfos::iterator i = elems.begin(), j; i != elems.end(); i = j) {
-    j = i;
-    j++;
-    if (systemFilter != "*" && i->querySystem() != systemFilter) {
-      elems.erase(i);
-    }
-  }
-}
-
-static long getPriority(EvalState& state, DrvInfo& drv) {
-  return drv.queryMetaInt("priority", 0);
-}
-
-static long comparePriorities(EvalState& state, DrvInfo& drv1, DrvInfo& drv2) {
-  return getPriority(state, drv2) - getPriority(state, drv1);
-}
-
-// FIXME: this function is rather slow since it checks a single path
-// at a time.
-static bool isPrebuilt(EvalState& state, DrvInfo& elem) {
-  Path path = elem.queryOutPath();
-  if (state.store->isValidPath(path)) {
-    return true;
-  }
-  PathSet ps = state.store->querySubstitutablePaths({path});
-  return ps.find(path) != ps.end();
-}
-
-static void checkSelectorUse(DrvNames& selectors) {
-  /* Check that all selectors have been used. */
-  for (auto& i : selectors) {
-    if (i.hits == 0 && i.fullName != "*") {
-      throw Error(format("selector '%1%' matches no derivations") % i.fullName);
-    }
-  }
-}
-
-static DrvInfos filterBySelector(EvalState& state, const DrvInfos& allElems,
-                                 const Strings& args, bool newestOnly) {
-  DrvNames selectors = drvNamesFromArgs(args);
-  if (selectors.empty()) {
-    selectors.push_back(DrvName("*"));
-  }
-
-  DrvInfos elems;
-  std::set<unsigned int> done;
-
-  for (auto& i : selectors) {
-    using Matches = std::list<std::pair<DrvInfo, unsigned int> >;
-    Matches matches;
-    unsigned int n = 0;
-    for (auto j = allElems.begin(); j != allElems.end(); ++j, ++n) {
-      DrvName drvName(j->queryName());
-      if (i.matches(drvName)) {
-        i.hits++;
-        matches.push_back(std::pair<DrvInfo, unsigned int>(*j, n));
-      }
-    }
-
-    /* If `newestOnly', if a selector matches multiple derivations
-       with the same name, pick the one matching the current
-       system.  If there are still multiple derivations, pick the
-       one with the highest priority.  If there are still multiple
-       derivations, pick the one with the highest version.
-       Finally, if there are still multiple derivations,
-       arbitrarily pick the first one. */
-    if (newestOnly) {
-      /* Map from package names to derivations. */
-      using Newest = std::map<std::string, std::pair<DrvInfo, unsigned int> >;
-      Newest newest;
-      StringSet multiple;
-
-      for (auto& j : matches) {
-        DrvName drvName(j.first.queryName());
-        long d = 1;
-
-        auto k = newest.find(drvName.name);
-
-        if (k != newest.end()) {
-          d = j.first.querySystem() == k->second.first.querySystem()
-                  ? 0
-                  : j.first.querySystem() == settings.thisSystem
-                        ? 1
-                        : k->second.first.querySystem() == settings.thisSystem
-                              ? -1
-                              : 0;
-          if (d == 0) {
-            d = comparePriorities(state, j.first, k->second.first);
-          }
-          if (d == 0) {
-            d = compareVersions(drvName.version,
-                                DrvName(k->second.first.queryName()).version);
-          }
-        }
-
-        if (d > 0) {
-          newest.erase(drvName.name);
-          newest.insert(Newest::value_type(drvName.name, j));
-          multiple.erase(j.first.queryName());
-        } else if (d == 0) {
-          multiple.insert(j.first.queryName());
-        }
-      }
-
-      matches.clear();
-      for (auto& j : newest) {
-        if (multiple.find(j.second.first.queryName()) != multiple.end()) {
-          LOG(WARNING) << "warning: there are multiple derivations named '"
-                       << j.second.first.queryName()
-                       << "'; using the first one";
-        }
-        matches.push_back(j.second);
-      }
-    }
-
-    /* Insert only those elements in the final list that we
-       haven't inserted before. */
-    for (auto& j : matches) {
-      if (done.find(j.second) == done.end()) {
-        done.insert(j.second);
-        elems.push_back(j.first);
-      }
-    }
-  }
-
-  checkSelectorUse(selectors);
-
-  return elems;
-}
-
-static bool isPath(const std::string& s) {
-  return s.find('/') != std::string::npos;
-}
-
-static void queryInstSources(EvalState& state, InstallSourceInfo& instSource,
-                             const Strings& args, DrvInfos& elems,
-                             bool newestOnly) {
-  InstallSourceType type = instSource.type;
-  if (type == srcUnknown && !args.empty() && isPath(args.front())) {
-    type = srcStorePaths;
-  }
-
-  switch (type) {
-    /* Get the available user environment elements from the
-       derivations specified in a Nix expression, including only
-       those with names matching any of the names in `args'. */
-    case srcUnknown:
-    case srcNixExprDrvs: {
-      /* Load the derivations from the (default or specified)
-         Nix expression. */
-      DrvInfos allElems;
-      loadDerivations(state, instSource.nixExprPath, instSource.systemFilter,
-                      instSource.autoArgs.get(), "", allElems);
-
-      elems = filterBySelector(state, allElems, args, newestOnly);
-
-      break;
-    }
-
-    /* Get the available user environment elements from the Nix
-       expressions specified on the command line; these should be
-       functions that take the default Nix expression file as
-       argument, e.g., if the file is `./foo.nix', then the
-       argument `x: x.bar' is equivalent to `(x: x.bar)
-       (import ./foo.nix)' = `(import ./foo.nix).bar'. */
-    case srcNixExprs: {
-      Value vArg;
-      loadSourceExpr(state, instSource.nixExprPath, vArg);
-
-      for (auto& i : args) {
-        Expr* eFun = state.parseExprFromString(i, absPath("."));
-        Value vFun;
-        Value vTmp;
-        state.eval(eFun, vFun);
-        mkApp(vTmp, vFun, vArg);
-        getDerivations(state, vTmp, "", instSource.autoArgs.get(), elems, true);
-      }
-
-      break;
-    }
-
-    /* The available user environment elements are specified as a
-       list of store paths (which may or may not be
-       derivations). */
-    case srcStorePaths: {
-      for (auto& i : args) {
-        Path path = state.store->followLinksToStorePath(i);
-
-        std::string name = baseNameOf(path);
-        std::string::size_type dash = name.find('-');
-        if (dash != std::string::npos) {
-          name = std::string(name, dash + 1);
-        }
-
-        DrvInfo elem(state, "", nullptr);
-        elem.setName(name);
-
-        if (isDerivation(path)) {
-          elem.setDrvPath(path);
-          elem.setOutPath(
-              state.store->derivationFromPath(path).findOutput("out"));
-          if (name.size() >= drvExtension.size() &&
-              std::string(name, name.size() - drvExtension.size()) ==
-                  drvExtension) {
-            name = std::string(name, 0, name.size() - drvExtension.size());
-          }
-        } else {
-          elem.setOutPath(path);
-        }
-
-        elems.push_back(elem);
-      }
-
-      break;
-    }
-
-    /* Get the available user environment elements from another
-       user environment.  These are then filtered as in the
-       `srcNixExprDrvs' case. */
-    case srcProfile: {
-      elems = filterBySelector(state, queryInstalled(state, instSource.profile),
-                               args, newestOnly);
-      break;
-    }
-
-    case srcAttrPath: {
-      Value vRoot;
-      loadSourceExpr(state, instSource.nixExprPath, vRoot);
-      for (auto& i : args) {
-        Value& v(
-            *findAlongAttrPath(state, i, instSource.autoArgs.get(), vRoot));
-        getDerivations(state, v, "", instSource.autoArgs.get(), elems, true);
-      }
-      break;
-    }
-  }
-}
-
-static void printMissing(EvalState& state, DrvInfos& elems) {
-  PathSet targets;
-  for (auto& i : elems) {
-    Path drvPath = i.queryDrvPath();
-    if (!drvPath.empty()) {
-      targets.insert(drvPath);
-    } else {
-      targets.insert(i.queryOutPath());
-    }
-  }
-
-  printMissing(state.store, targets);
-}
-
-static bool keep(DrvInfo& drv) { return drv.queryMetaBool("keep", false); }
-
-static void installDerivations(Globals& globals, const Strings& args,
-                               const Path& profile) {
-  DLOG(INFO) << "installing derivations";
-
-  /* Get the set of user environment elements to be installed. */
-  DrvInfos newElems;
-  DrvInfos newElemsTmp;
-  queryInstSources(*globals.state, globals.instSource, args, newElemsTmp, true);
-
-  /* If --prebuilt-only is given, filter out source-only packages. */
-  for (auto& i : newElemsTmp) {
-    if (!globals.prebuiltOnly || isPrebuilt(*globals.state, i)) {
-      newElems.push_back(i);
-    }
-  }
-
-  StringSet newNames;
-  for (auto& i : newElems) {
-    /* `forceName' is a hack to get package names right in some
-       one-click installs, namely those where the name used in the
-       path is not the one we want (e.g., `java-front' versus
-       `java-front-0.9pre15899'). */
-    if (!globals.forceName.empty()) {
-      i.setName(globals.forceName);
-    }
-    newNames.insert(DrvName(i.queryName()).name);
-  }
-
-  while (true) {
-    std::string lockToken = optimisticLockProfile(profile);
-
-    DrvInfos allElems(newElems);
-
-    /* Add in the already installed derivations, unless they have
-       the same name as a to-be-installed element. */
-    if (!globals.removeAll) {
-      DrvInfos installedElems = queryInstalled(*globals.state, profile);
-
-      for (auto& i : installedElems) {
-        DrvName drvName(i.queryName());
-        if (!globals.preserveInstalled &&
-            newNames.find(drvName.name) != newNames.end() && !keep(i)) {
-          LOG(INFO) << "replacing old '" << i.queryName() << "'";
-        } else {
-          allElems.push_back(i);
-        }
-      }
-
-      for (auto& i : newElems) {
-        LOG(INFO) << "installing " << i.queryName();
-      }
-    }
-
-    printMissing(*globals.state, newElems);
-
-    if (globals.dryRun) {
-      return;
-    }
-
-    if (createUserEnv(*globals.state, allElems, profile,
-                      settings.envKeepDerivations, lockToken)) {
-      break;
-    }
-  }
-}
-
-static void opInstall(Globals& globals, Strings opFlags, Strings opArgs) {
-  for (auto i = opFlags.begin(); i != opFlags.end();) {
-    std::string arg = *i++;
-    if (parseInstallSourceOptions(globals, i, opFlags, arg)) {
-      ;
-    } else if (arg == "--preserve-installed" || arg == "-P") {
-      globals.preserveInstalled = true;
-    } else if (arg == "--remove-all" || arg == "-r") {
-      globals.removeAll = true;
-    } else {
-      throw UsageError(format("unknown flag '%1%'") % arg);
-    }
-  }
-
-  installDerivations(globals, opArgs, globals.profile);
-}
-
-typedef enum { utLt, utLeq, utEq, utAlways } UpgradeType;
-
-static void upgradeDerivations(Globals& globals, const Strings& args,
-                               UpgradeType upgradeType) {
-  DLOG(INFO) << "upgrading derivations";
-
-  /* Upgrade works as follows: we take all currently installed
-     derivations, and for any derivation matching any selector, look
-     for a derivation in the input Nix expression that has the same
-     name and a higher version number. */
-
-  while (true) {
-    std::string lockToken = optimisticLockProfile(globals.profile);
-
-    DrvInfos installedElems = queryInstalled(*globals.state, globals.profile);
-
-    /* Fetch all derivations from the input file. */
-    DrvInfos availElems;
-    queryInstSources(*globals.state, globals.instSource, args, availElems,
-                     false);
-
-    /* Go through all installed derivations. */
-    DrvInfos newElems;
-    for (auto& i : installedElems) {
-      DrvName drvName(i.queryName());
-
-      try {
-        if (keep(i)) {
-          newElems.push_back(i);
-          continue;
-        }
-
-        /* Find the derivation in the input Nix expression
-           with the same name that satisfies the version
-           constraints specified by upgradeType.  If there are
-           multiple matches, take the one with the highest
-           priority.  If there are still multiple matches,
-           take the one with the highest version.
-           Do not upgrade if it would decrease the priority. */
-        auto bestElem = availElems.end();
-        std::string bestVersion;
-        for (auto j = availElems.begin(); j != availElems.end(); ++j) {
-          if (comparePriorities(*globals.state, i, *j) > 0) {
-            continue;
-          }
-          DrvName newName(j->queryName());
-          if (newName.name == drvName.name) {
-            int d = compareVersions(drvName.version, newName.version);
-            if ((upgradeType == utLt && d < 0) ||
-                (upgradeType == utLeq && d <= 0) ||
-                (upgradeType == utEq && d == 0) || upgradeType == utAlways) {
-              long d2 = -1;
-              if (bestElem != availElems.end()) {
-                d2 = comparePriorities(*globals.state, *bestElem, *j);
-                if (d2 == 0) {
-                  d2 = compareVersions(bestVersion, newName.version);
-                }
-              }
-              if (d2 < 0 &&
-                  (!globals.prebuiltOnly || isPrebuilt(*globals.state, *j))) {
-                bestElem = j;
-                bestVersion = newName.version;
-              }
-            }
-          }
-        }
-
-        if (bestElem != availElems.end() &&
-            i.queryOutPath() != bestElem->queryOutPath()) {
-          const char* action =
-              compareVersions(drvName.version, bestVersion) <= 0
-                  ? "upgrading"
-                  : "downgrading";
-          LOG(INFO) << action << " '" << i.queryName() << "' to '"
-                    << bestElem->queryName() << "'";
-          newElems.push_back(*bestElem);
-        } else {
-          newElems.push_back(i);
-        }
-
-      } catch (Error& e) {
-        e.addPrefix(
-            fmt("while trying to find an upgrade for '%s':\n", i.queryName()));
-        throw;
-      }
-    }
-
-    printMissing(*globals.state, newElems);
-
-    if (globals.dryRun) {
-      return;
-    }
-
-    if (createUserEnv(*globals.state, newElems, globals.profile,
-                      settings.envKeepDerivations, lockToken)) {
-      break;
-    }
-  }
-}
-
-static void opUpgrade(Globals& globals, Strings opFlags, Strings opArgs) {
-  UpgradeType upgradeType = utLt;
-  for (auto i = opFlags.begin(); i != opFlags.end();) {
-    std::string arg = *i++;
-    if (parseInstallSourceOptions(globals, i, opFlags, arg)) {
-      ;
-    } else if (arg == "--lt") {
-      upgradeType = utLt;
-    } else if (arg == "--leq") {
-      upgradeType = utLeq;
-    } else if (arg == "--eq") {
-      upgradeType = utEq;
-    } else if (arg == "--always") {
-      upgradeType = utAlways;
-    } else {
-      throw UsageError(format("unknown flag '%1%'") % arg);
-    }
-  }
-
-  upgradeDerivations(globals, opArgs, upgradeType);
-}
-
-static void setMetaFlag(EvalState& state, DrvInfo& drv, const std::string& name,
-                        const std::string& value) {
-  Value* v = state.allocValue();
-  mkString(*v, value.c_str());
-  drv.setMeta(name, v);
-}
-
-static void opSetFlag(Globals& globals, Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError(format("unknown flag '%1%'") % opFlags.front());
-  }
-  if (opArgs.size() < 2) {
-    throw UsageError("not enough arguments to '--set-flag'");
-  }
-
-  auto arg = opArgs.begin();
-  std::string flagName = *arg++;
-  std::string flagValue = *arg++;
-  DrvNames selectors = drvNamesFromArgs(Strings(arg, opArgs.end()));
-
-  while (true) {
-    std::string lockToken = optimisticLockProfile(globals.profile);
-
-    DrvInfos installedElems = queryInstalled(*globals.state, globals.profile);
-
-    /* Update all matching derivations. */
-    for (auto& i : installedElems) {
-      DrvName drvName(i.queryName());
-      for (auto& j : selectors) {
-        if (j.matches(drvName)) {
-          LOG(INFO) << "setting flag on '" << i.queryName() << "'";
-          j.hits++;
-          setMetaFlag(*globals.state, i, flagName, flagValue);
-          break;
-        }
-      }
-    }
-
-    checkSelectorUse(selectors);
-
-    /* Write the new user environment. */
-    if (createUserEnv(*globals.state, installedElems, globals.profile,
-                      settings.envKeepDerivations, lockToken)) {
-      break;
-    }
-  }
-}
-
-static void opSet(Globals& globals, Strings opFlags, Strings opArgs) {
-  auto store2 = globals.state->store.dynamic_pointer_cast<LocalFSStore>();
-  if (!store2) {
-    throw Error("--set is not supported for this Nix store");
-  }
-
-  for (auto i = opFlags.begin(); i != opFlags.end();) {
-    std::string arg = *i++;
-    if (parseInstallSourceOptions(globals, i, opFlags, arg)) {
-      ;
-    } else {
-      throw UsageError(format("unknown flag '%1%'") % arg);
-    }
-  }
-
-  DrvInfos elems;
-  queryInstSources(*globals.state, globals.instSource, opArgs, elems, true);
-
-  if (elems.size() != 1) {
-    throw Error("--set requires exactly one derivation");
-  }
-
-  DrvInfo& drv(elems.front());
-
-  if (!globals.forceName.empty()) {
-    drv.setName(globals.forceName);
-  }
-
-  if (!drv.queryDrvPath().empty()) {
-    PathSet paths = {drv.queryDrvPath()};
-    printMissing(globals.state->store, paths);
-    if (globals.dryRun) {
-      return;
-    }
-    nix::util::OkOrThrow(globals.state->store->buildPaths(
-        std::cerr, paths, globals.state->repair != 0u ? bmRepair : bmNormal));
-  } else {
-    printMissing(globals.state->store, {drv.queryOutPath()});
-    if (globals.dryRun) {
-      return;
-    }
-    globals.state->store->ensurePath(drv.queryOutPath());
-  }
-
-  DLOG(INFO) << "switching to new user environment";
-  Path generation = createGeneration(ref<LocalFSStore>(store2), globals.profile,
-                                     drv.queryOutPath());
-  switchLink(globals.profile, generation);
-}
-
-static void uninstallDerivations(Globals& globals, Strings& selectors,
-                                 Path& profile) {
-  while (true) {
-    std::string lockToken = optimisticLockProfile(profile);
-
-    DrvInfos installedElems = queryInstalled(*globals.state, profile);
-    DrvInfos newElems;
-
-    for (auto& i : installedElems) {
-      DrvName drvName(i.queryName());
-      bool found = false;
-      for (auto& j : selectors) {
-        /* !!! the repeated calls to followLinksToStorePath()
-           are expensive, should pre-compute them. */
-        if ((isPath(j) &&
-             i.queryOutPath() ==
-                 globals.state->store->followLinksToStorePath(j)) ||
-            DrvName(j).matches(drvName)) {
-          LOG(INFO) << "uninstalling '" << i.queryName() << "'";
-          found = true;
-          break;
-        }
-      }
-      if (!found) {
-        newElems.push_back(i);
-      }
-    }
-
-    if (globals.dryRun) {
-      return;
-    }
-
-    if (createUserEnv(*globals.state, newElems, profile,
-                      settings.envKeepDerivations, lockToken)) {
-      break;
-    }
-  }
-}
-
-static void opUninstall(Globals& globals, Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError(format("unknown flag '%1%'") % opFlags.front());
-  }
-  uninstallDerivations(globals, opArgs, globals.profile);
-}
-
-static bool cmpChars(char a, char b) { return toupper(a) < toupper(b); }
-
-static bool cmpElemByName(const DrvInfo& a, const DrvInfo& b) {
-  auto a_name = a.queryName();
-  auto b_name = b.queryName();
-  return lexicographical_compare(a_name.begin(), a_name.end(), b_name.begin(),
-                                 b_name.end(), cmpChars);
-}
-
-using Table = std::list<Strings>;
-
-void printTable(Table& table) {
-  auto nrColumns = !table.empty() ? table.front().size() : 0;
-
-  std::vector<size_t> widths;
-  widths.resize(nrColumns);
-
-  for (auto& i : table) {
-    assert(i.size() == nrColumns);
-    Strings::iterator j;
-    size_t column;
-    for (j = i.begin(), column = 0; j != i.end(); ++j, ++column) {
-      if (j->size() > widths[column]) {
-        widths[column] = j->size();
-      }
-    }
-  }
-
-  for (auto& i : table) {
-    Strings::iterator j;
-    size_t column;
-    for (j = i.begin(), column = 0; j != i.end(); ++j, ++column) {
-      std::string s = *j;
-      replace(s.begin(), s.end(), '\n', ' ');
-      cout << s;
-      if (column < nrColumns - 1) {
-        cout << std::string(widths[column] - s.size() + 2, ' ');
-      }
-    }
-    cout << std::endl;
-  }
-}
-
-/* This function compares the version of an element against the
-   versions in the given set of elements.  `cvLess' means that only
-   lower versions are in the set, `cvEqual' means that at most an
-   equal version is in the set, and `cvGreater' means that there is at
-   least one element with a higher version in the set.  `cvUnavail'
-   means that there are no elements with the same name in the set. */
-
-using VersionDiff = enum { cvLess, cvEqual, cvGreater, cvUnavail };
-
-static VersionDiff compareVersionAgainstSet(const DrvInfo& elem,
-                                            const DrvInfos& elems,
-                                            std::string& version) {
-  DrvName name(elem.queryName());
-
-  VersionDiff diff = cvUnavail;
-  version = "?";
-
-  for (auto& i : elems) {
-    DrvName name2(i.queryName());
-    if (name.name == name2.name) {
-      int d = compareVersions(name.version, name2.version);
-      if (d < 0) {
-        diff = cvGreater;
-        version = name2.version;
-      } else if (diff != cvGreater && d == 0) {
-        diff = cvEqual;
-        version = name2.version;
-      } else if (diff != cvGreater && diff != cvEqual && d > 0) {
-        diff = cvLess;
-        if (version.empty() || compareVersions(version, name2.version) < 0) {
-          version = name2.version;
-        }
-      }
-    }
-  }
-
-  return diff;
-}
-
-static void queryJSON(Globals& globals, std::vector<DrvInfo>& elems) {
-  JSONObject topObj(cout, true);
-  for (auto& i : elems) {
-    JSONObject pkgObj = topObj.object(i.attrPath);
-
-    auto drvName = DrvName(i.queryName());
-    pkgObj.attr("name", drvName.fullName);
-    pkgObj.attr("pname", drvName.name);
-    pkgObj.attr("version", drvName.version);
-    pkgObj.attr("system", i.querySystem());
-
-    JSONObject metaObj = pkgObj.object("meta");
-    StringSet metaNames = i.queryMetaNames();
-    for (auto& j : metaNames) {
-      auto placeholder = metaObj.placeholder(j);
-      Value* v = i.queryMeta(j);
-      if (v == nullptr) {
-        LOG(ERROR) << "derivation '" << i.queryName()
-                   << "' has invalid meta attribute '" << j << "'";
-        placeholder.write(nullptr);
-      } else {
-        PathSet context;
-        printValueAsJSON(*globals.state, true, *v, placeholder, context);
-      }
-    }
-  }
-}
-
-static void opQuery(Globals& globals, Strings opFlags, Strings opArgs) {
-  Strings remaining;
-  std::string attrPath;
-
-  bool printStatus = false;
-  bool printName = true;
-  bool printAttrPath = false;
-  bool printSystem = false;
-  bool printDrvPath = false;
-  bool printOutPath = false;
-  bool printDescription = false;
-  bool printMeta = false;
-  bool compareVersions = false;
-  bool xmlOutput = false;
-  bool jsonOutput = false;
-
-  enum { sInstalled, sAvailable } source = sInstalled;
-
-  settings.readOnlyMode = true; /* makes evaluation a bit faster */
-
-  for (auto i = opFlags.begin(); i != opFlags.end();) {
-    std::string arg = *i++;
-    if (arg == "--status" || arg == "-s") {
-      printStatus = true;
-    } else if (arg == "--no-name") {
-      printName = false;
-    } else if (arg == "--system") {
-      printSystem = true;
-    } else if (arg == "--description") {
-      printDescription = true;
-    } else if (arg == "--compare-versions" || arg == "-c") {
-      compareVersions = true;
-    } else if (arg == "--drv-path") {
-      printDrvPath = true;
-    } else if (arg == "--out-path") {
-      printOutPath = true;
-    } else if (arg == "--meta") {
-      printMeta = true;
-    } else if (arg == "--installed") {
-      source = sInstalled;
-    } else if (arg == "--available" || arg == "-a") {
-      source = sAvailable;
-    } else if (arg == "--xml") {
-      xmlOutput = true;
-    } else if (arg == "--json") {
-      jsonOutput = true;
-    } else if (arg == "--attr-path" || arg == "-P") {
-      printAttrPath = true;
-    } else if (arg == "--attr" || arg == "-A") {
-      attrPath = needArg(i, opFlags, arg);
-    } else {
-      throw UsageError(format("unknown flag '%1%'") % arg);
-    }
-  }
-
-  /* Obtain derivation information from the specified source. */
-  DrvInfos availElems;
-  DrvInfos installedElems;
-
-  if (source == sInstalled || compareVersions || printStatus) {
-    installedElems = queryInstalled(*globals.state, globals.profile);
-  }
-
-  if (source == sAvailable || compareVersions) {
-    loadDerivations(*globals.state, globals.instSource.nixExprPath,
-                    globals.instSource.systemFilter,
-                    globals.instSource.autoArgs.get(), attrPath, availElems);
-  }
-
-  DrvInfos elems_ = filterBySelector(
-      *globals.state, source == sInstalled ? installedElems : availElems,
-      opArgs, false);
-
-  DrvInfos& otherElems(source == sInstalled ? availElems : installedElems);
-
-  /* Sort them by name. */
-  /* !!! */
-  std::vector<DrvInfo> elems;
-  for (auto& i : elems_) {
-    elems.push_back(i);
-  }
-  sort(elems.begin(), elems.end(), cmpElemByName);
-
-  /* We only need to know the installed paths when we are querying
-     the status of the derivation. */
-  PathSet installed; /* installed paths */
-
-  if (printStatus) {
-    for (auto& i : installedElems) {
-      installed.insert(i.queryOutPath());
-    }
-  }
-
-  /* Query which paths have substitutes. */
-  PathSet validPaths;
-  PathSet substitutablePaths;
-  if (printStatus || globals.prebuiltOnly) {
-    PathSet paths;
-    for (auto& i : elems) {
-      try {
-        paths.insert(i.queryOutPath());
-      } catch (AssertionError& e) {
-        DLOG(WARNING) << "skipping derivation named '" << i.queryName()
-                      << "' which gives an assertion failure";
-        i.setFailed();
-      }
-    }
-    validPaths = globals.state->store->queryValidPaths(paths);
-    substitutablePaths = globals.state->store->querySubstitutablePaths(paths);
-  }
-
-  /* Print the desired columns, or XML output. */
-  if (jsonOutput) {
-    queryJSON(globals, elems);
-    return;
-  }
-
-  bool tty = isatty(STDOUT_FILENO) != 0;
-  RunPager pager;
-
-  Table table;
-  std::ostringstream dummy;
-  XMLWriter xml(true, *(xmlOutput ? &cout : &dummy));
-  XMLOpenElement xmlRoot(xml, "items");
-
-  for (auto& i : elems) {
-    try {
-      if (i.hasFailed()) {
-        continue;
-      }
-
-      // Activity act(*logger, lvlDebug, format("outputting query result '%1%'")
-      // % i.attrPath);
-
-      if (globals.prebuiltOnly &&
-          validPaths.find(i.queryOutPath()) == validPaths.end() &&
-          substitutablePaths.find(i.queryOutPath()) ==
-              substitutablePaths.end()) {
-        continue;
-      }
-
-      /* For table output. */
-      Strings columns;
-
-      /* For XML output. */
-      XMLAttrs attrs;
-
-      if (printStatus) {
-        Path outPath = i.queryOutPath();
-        bool hasSubs =
-            substitutablePaths.find(outPath) != substitutablePaths.end();
-        bool isInstalled = installed.find(outPath) != installed.end();
-        bool isValid = validPaths.find(outPath) != validPaths.end();
-        if (xmlOutput) {
-          attrs["installed"] = isInstalled ? "1" : "0";
-          attrs["valid"] = isValid ? "1" : "0";
-          attrs["substitutable"] = hasSubs ? "1" : "0";
-        } else {
-          columns.push_back(absl::StrCat((isInstalled ? "I" : "-"),
-                                         (isValid ? "P" : "-"),
-                                         (hasSubs ? "S" : "-")));
-        }
-      }
-
-      if (xmlOutput) {
-        attrs["attrPath"] = i.attrPath;
-      } else if (printAttrPath) {
-        columns.push_back(i.attrPath);
-      }
-
-      if (xmlOutput) {
-        auto drvName = DrvName(i.queryName());
-        attrs["name"] = drvName.fullName;
-        attrs["pname"] = drvName.name;
-        attrs["version"] = drvName.version;
-      } else if (printName) {
-        columns.push_back(i.queryName());
-      }
-
-      if (compareVersions) {
-        /* Compare this element against the versions of the
-           same named packages in either the set of available
-           elements, or the set of installed elements.  !!!
-           This is O(N * M), should be O(N * lg M). */
-        std::string version;
-        VersionDiff diff = compareVersionAgainstSet(i, otherElems, version);
-
-        char ch;
-        switch (diff) {
-          case cvLess:
-            ch = '>';
-            break;
-          case cvEqual:
-            ch = '=';
-            break;
-          case cvGreater:
-            ch = '<';
-            break;
-          case cvUnavail:
-            ch = '-';
-            break;
-          default:
-            abort();
-        }
-
-        if (xmlOutput) {
-          if (diff != cvUnavail) {
-            attrs["versionDiff"] = ch;
-            attrs["maxComparedVersion"] = version;
-          }
-        } else {
-          std::string column = std::to_string(ch) + " " + version;
-          if (diff == cvGreater && tty) {
-            column = ANSI_RED + column + ANSI_NORMAL;
-          }
-          columns.push_back(column);
-        }
-      }
-
-      if (xmlOutput) {
-        if (!i.querySystem().empty()) {
-          attrs["system"] = i.querySystem();
-        }
-      } else if (printSystem) {
-        columns.push_back(i.querySystem());
-      }
-
-      if (printDrvPath) {
-        std::string drvPath = i.queryDrvPath();
-        if (xmlOutput) {
-          if (!drvPath.empty()) {
-            attrs["drvPath"] = drvPath;
-          }
-        } else {
-          columns.push_back(drvPath.empty() ? "-" : drvPath);
-        }
-      }
-
-      if (printOutPath && !xmlOutput) {
-        DrvInfo::Outputs outputs = i.queryOutputs();
-        std::string s;
-        for (auto& j : outputs) {
-          if (!s.empty()) {
-            s += ';';
-          }
-          if (j.first != "out") {
-            s += j.first;
-            s += "=";
-          }
-          s += j.second;
-        }
-        columns.push_back(s);
-      }
-
-      if (printDescription) {
-        std::string descr = i.queryMetaString("description");
-        if (xmlOutput) {
-          if (!descr.empty()) {
-            attrs["description"] = descr;
-          }
-        } else {
-          columns.push_back(descr);
-        }
-      }
-
-      if (xmlOutput) {
-        if (printOutPath || printMeta) {
-          XMLOpenElement item(xml, "item", attrs);
-          if (printOutPath) {
-            DrvInfo::Outputs outputs = i.queryOutputs();
-            for (auto& j : outputs) {
-              XMLAttrs attrs2;
-              attrs2["name"] = j.first;
-              attrs2["path"] = j.second;
-              xml.writeEmptyElement("output", attrs2);
-            }
-          }
-          if (printMeta) {
-            StringSet metaNames = i.queryMetaNames();
-            for (auto& j : metaNames) {
-              XMLAttrs attrs2;
-              attrs2["name"] = j;
-              Value* v = i.queryMeta(j);
-              if (v == nullptr) {
-                LOG(ERROR) << "derivation '" << i.queryName()
-                           << "' has invalid meta attribute '" << j << "'";
-              } else {
-                if (v->type == tString) {
-                  attrs2["type"] = "string";
-                  attrs2["value"] = v->string.s;
-                  xml.writeEmptyElement("meta", attrs2);
-                } else if (v->type == tInt) {
-                  attrs2["type"] = "int";
-                  attrs2["value"] = (format("%1%") % v->integer).str();
-                  xml.writeEmptyElement("meta", attrs2);
-                } else if (v->type == tFloat) {
-                  attrs2["type"] = "float";
-                  attrs2["value"] = (format("%1%") % v->fpoint).str();
-                  xml.writeEmptyElement("meta", attrs2);
-                } else if (v->type == tBool) {
-                  attrs2["type"] = "bool";
-                  attrs2["value"] = v->boolean ? "true" : "false";
-                  xml.writeEmptyElement("meta", attrs2);
-                } else if (v->isList()) {
-                  attrs2["type"] = "strings";
-                  XMLOpenElement m(xml, "meta", attrs2);
-                  for (unsigned int j = 0; j < v->listSize(); ++j) {
-                    if ((*v->list)[j]->type != tString) {
-                      continue;
-                    }
-                    XMLAttrs attrs3;
-                    attrs3["value"] = (*v->list)[j]->string.s;
-                    xml.writeEmptyElement("string", attrs3);
-                  }
-                } else if (v->type == tAttrs) {
-                  attrs2["type"] = "strings";
-                  XMLOpenElement m(xml, "meta", attrs2);
-                  Bindings& attrs = *v->attrs;
-                  for (auto& [name, a] : attrs) {
-                    if (a.value->type != tString) {
-                      continue;
-                    }
-                    XMLAttrs attrs3;
-                    attrs3["type"] = name;
-                    attrs3["value"] = a.value->string.s;
-                    xml.writeEmptyElement("string", attrs3);
-                  }
-                }
-              }
-            }
-          }
-        } else {
-          xml.writeEmptyElement("item", attrs);
-        }
-      } else {
-        table.push_back(columns);
-      }
-
-      cout.flush();
-
-    } catch (AssertionError& e) {
-      DLOG(WARNING) << "skipping derivation named '" << i.queryName()
-                    << "' which gives an assertion failure";
-    } catch (Error& e) {
-      e.addPrefix(
-          fmt("while querying the derivation named '%1%':\n", i.queryName()));
-      throw;
-    }
-  }
-
-  if (!xmlOutput) {
-    printTable(table);
-  }
-}
-
-static void opSwitchProfile(Globals& globals, Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError(format("unknown flag '%1%'") % opFlags.front());
-  }
-  if (opArgs.size() != 1) {
-    throw UsageError(format("exactly one argument expected"));
-  }
-
-  Path profile = absPath(opArgs.front());
-  Path profileLink = getHome() + "/.nix-profile";
-
-  switchLink(profileLink, profile);
-}
-
-static const int prevGen = -2;
-
-static void switchGeneration(Globals& globals, int dstGen) {
-  PathLocks lock;
-  lockProfile(lock, globals.profile);
-
-  int curGen;
-  Generations gens = findGenerations(globals.profile, curGen);
-
-  Generation dst;
-  for (auto& i : gens) {
-    if ((dstGen == prevGen && i.number < curGen) ||
-        (dstGen >= 0 && i.number == dstGen)) {
-      dst = i;
-    }
-  }
-
-  if (!dst) {
-    if (dstGen == prevGen) {
-      throw Error(format("no generation older than the current (%1%) exists") %
-                  curGen);
-    }
-    throw Error(format("generation %1% does not exist") % dstGen);
-  }
-
-  LOG(INFO) << "switching from generation " << curGen << " to " << dst.number;
-
-  if (globals.dryRun) {
-    return;
-  }
-
-  switchLink(globals.profile, dst.path);
-}
-
-static void opSwitchGeneration(Globals& globals, Strings opFlags,
-                               Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError(format("unknown flag '%1%'") % opFlags.front());
-  }
-  if (opArgs.size() != 1) {
-    throw UsageError(format("exactly one argument expected"));
-  }
-
-  int dstGen;
-  if (!absl::SimpleAtoi(opArgs.front(), &dstGen)) {
-    throw UsageError(format("expected a generation number"));
-  }
-
-  switchGeneration(globals, dstGen);
-}
-
-static void opRollback(Globals& globals, Strings opFlags, Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError(format("unknown flag '%1%'") % opFlags.front());
-  }
-  if (!opArgs.empty()) {
-    throw UsageError(format("no arguments expected"));
-  }
-
-  switchGeneration(globals, prevGen);
-}
-
-static void opListGenerations(Globals& globals, Strings opFlags,
-                              Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError(format("unknown flag '%1%'") % opFlags.front());
-  }
-  if (!opArgs.empty()) {
-    throw UsageError(format("no arguments expected"));
-  }
-
-  PathLocks lock;
-  lockProfile(lock, globals.profile);
-
-  int curGen;
-  Generations gens = findGenerations(globals.profile, curGen);
-
-  RunPager pager;
-
-  for (auto& i : gens) {
-    tm t;
-    if (localtime_r(&i.creationTime, &t) == nullptr) {
-      throw Error("cannot convert time");
-    }
-    cout << format("%|4|   %|4|-%|02|-%|02| %|02|:%|02|:%|02|   %||\n") %
-                i.number % (t.tm_year + 1900) % (t.tm_mon + 1) % t.tm_mday %
-                t.tm_hour % t.tm_min % t.tm_sec %
-                (i.number == curGen ? "(current)" : "");
-  }
-}
-
-static void opDeleteGenerations(Globals& globals, Strings opFlags,
-                                Strings opArgs) {
-  if (!opFlags.empty()) {
-    throw UsageError(format("unknown flag '%1%'") % opFlags.front());
-  }
-
-  if (opArgs.size() == 1 && opArgs.front() == "old") {
-    deleteOldGenerations(globals.profile, globals.dryRun);
-  } else if (opArgs.size() == 1 &&
-             opArgs.front().find('d') != std::string::npos) {
-    deleteGenerationsOlderThan(globals.profile, opArgs.front(), globals.dryRun);
-  } else if (opArgs.size() == 1 &&
-             opArgs.front().find('+') != std::string::npos) {
-    if (opArgs.front().size() < 2) {
-      throw Error(format("invalid number of generations ‘%1%’") %
-                  opArgs.front());
-    }
-    std::string str_max = std::string(opArgs.front(), 1, opArgs.front().size());
-    int max;
-    if (!absl::SimpleAtoi(str_max, &max) || max == 0) {
-      throw Error(format("invalid number of generations to keep ‘%1%’") %
-                  opArgs.front());
-    }
-    deleteGenerationsGreaterThan(globals.profile, max, globals.dryRun);
-  } else {
-    std::set<unsigned int> gens;
-    for (auto& i : opArgs) {
-      unsigned int n;
-      if (!absl::SimpleAtoi(i, &n)) {
-        throw UsageError(format("invalid generation number '%1%'") % i);
-      }
-      gens.insert(n);
-    }
-    deleteGenerations(globals.profile, gens, globals.dryRun);
-  }
-}
-
-static void opVersion(Globals& globals, Strings opFlags, Strings opArgs) {
-  printVersion("nix-env");
-}
-
-static int _main(int argc, char** argv) {
-  {
-    Strings opFlags;
-    Strings opArgs;
-    Operation op = nullptr;
-    RepairFlag repair = NoRepair;
-    std::string file;
-
-    Globals globals;
-
-    globals.instSource.type = srcUnknown;
-    globals.instSource.nixExprPath = getHome() + "/.nix-defexpr";
-    globals.instSource.systemFilter = "*";
-
-    if (!pathExists(globals.instSource.nixExprPath)) {
-      try {
-        createDirs(globals.instSource.nixExprPath);
-        replaceSymlink(fmt("%s/profiles/per-user/%s/channels",
-                           settings.nixStateDir, getUserName()),
-                       globals.instSource.nixExprPath + "/channels");
-        if (getuid() != 0) {
-          replaceSymlink(
-              fmt("%s/profiles/per-user/root/channels", settings.nixStateDir),
-              globals.instSource.nixExprPath + "/channels_root");
-        }
-      } catch (Error&) {
-      }
-    }
-
-    globals.dryRun = false;
-    globals.preserveInstalled = false;
-    globals.removeAll = false;
-    globals.prebuiltOnly = false;
-
-    struct MyArgs : LegacyArgs, MixEvalArgs {
-      using LegacyArgs::LegacyArgs;
-    };
-
-    MyArgs myArgs(baseNameOf(argv[0]), [&](Strings::iterator& arg,
-                                           const Strings::iterator& end) {
-      Operation oldOp = op;
-
-      if (*arg == "--help") {
-        showManPage("nix-env");
-      } else if (*arg == "--version") {
-        op = opVersion;
-      } else if (*arg == "--install" || *arg == "-i") {
-        op = opInstall;
-      } else if (*arg ==
-                 "--force-name") {  // undocumented flag for nix-install-package
-        globals.forceName = getArg(*arg, arg, end);
-      } else if (*arg == "--uninstall" || *arg == "-e") {
-        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 == "--profile" || *arg == "-p") {
-        globals.profile = absPath(getArg(*arg, arg, end));
-      } else if (*arg == "--file" || *arg == "-f") {
-        file = getArg(*arg, arg, end);
-      } else if (*arg == "--switch-profile" || *arg == "-S") {
-        op = opSwitchProfile;
-      } else if (*arg == "--switch-generation" || *arg == "-G") {
-        op = opSwitchGeneration;
-      } else if (*arg == "--rollback") {
-        op = opRollback;
-      } else if (*arg == "--list-generations") {
-        op = opListGenerations;
-      } else if (*arg == "--delete-generations") {
-        op = opDeleteGenerations;
-      } else if (*arg == "--dry-run") {
-        LOG(INFO) << "(dry run; not doing anything)";
-        globals.dryRun = true;
-      } else if (*arg == "--system-filter") {
-        globals.instSource.systemFilter = getArg(*arg, arg, end);
-      } else if (*arg == "--prebuilt-only" || *arg == "-b") {
-        globals.prebuiltOnly = true;
-      } else if (*arg == "--repair") {
-        repair = Repair;
-      } else if (*arg != "" && arg->at(0) == '-') {
-        opFlags.push_back(*arg);
-        /* FIXME: hacky */
-        if (*arg == "--from-profile" ||
-            (op == opQuery && (*arg == "--attr" || *arg == "-A"))) {
-          opFlags.push_back(getArg(*arg, arg, end));
-        }
-      } else {
-        opArgs.push_back(*arg);
-      }
-
-      if ((oldOp != nullptr) && oldOp != op) {
-        throw UsageError("only one operation may be specified");
-      }
-
-      return true;
-    });
-
-    myArgs.parseCmdline(argvToStrings(argc, argv));
-
-    if (op == nullptr) {
-      throw UsageError("no operation specified");
-    }
-
-    auto store = openStore();
-
-    globals.state =
-        std::shared_ptr<EvalState>(new EvalState(myArgs.searchPath, store));
-    globals.state->repair = repair;
-
-    if (!file.empty()) {
-      globals.instSource.nixExprPath = lookupFileArg(*globals.state, file);
-    }
-
-    globals.instSource.autoArgs = myArgs.getAutoArgs(*globals.state);
-
-    if (globals.profile.empty()) {
-      globals.profile = getEnv("NIX_PROFILE").value_or("");
-    }
-
-    if (globals.profile.empty()) {
-      Path profileLink = getHome() + "/.nix-profile";
-      try {
-        if (!pathExists(profileLink)) {
-          replaceSymlink(getuid() == 0
-                             ? settings.nixStateDir + "/profiles/default"
-                             : fmt("%s/profiles/per-user/%s/profile",
-                                   settings.nixStateDir, getUserName()),
-                         profileLink);
-        }
-        globals.profile = absPath(readLink(profileLink), dirOf(profileLink));
-      } catch (Error&) {
-        globals.profile = profileLink;
-      }
-    }
-
-    op(globals, opFlags, opArgs);
-
-    globals.state->printStats();
-
-    return 0;
-  }
-}
-
-static RegisterLegacyCommand s1("nix-env", _main);