LTP GCOV extension - code coverage report
Current view: directory - src/nix-env - nix-env.cc
Test: app.info
Date: 2008-11-20 Instrumented lines: 699
Code covered: 64.5 % Executed lines: 451

       1                 : #include "profiles.hh"
       2                 : #include "names.hh"
       3                 : #include "globals.hh"
       4                 : #include "misc.hh"
       5                 : #include "shared.hh"
       6                 : #include "parser.hh"
       7                 : #include "eval.hh"
       8                 : #include "help.txt.hh"
       9                 : #include "nixexpr-ast.hh"
      10                 : #include "get-drvs.hh"
      11                 : #include "attr-path.hh"
      12                 : #include "pathlocks.hh"
      13                 : #include "common-opts.hh"
      14                 : #include "xml-writer.hh"
      15                 : #include "store-api.hh"
      16                 : #include "util.hh"
      17                 : 
      18                 : #include <cerrno>
      19                 : #include <ctime>
      20                 : #include <algorithm>
      21                 : #include <iostream>
      22                 : #include <sstream>
      23                 : 
      24                 : #include <sys/types.h>
      25                 : #include <sys/stat.h>
      26                 : #include <unistd.h>
      27                 : 
      28                 : 
      29                 : using namespace nix;
      30                 : using std::cout;
      31                 : 
      32                 : 
      33                 : typedef enum {
      34                 :     srcNixExprDrvs,
      35                 :     srcNixExprs,
      36                 :     srcStorePaths,
      37                 :     srcProfile,
      38                 :     srcAttrPath,
      39                 :     srcUnknown
      40                 : } InstallSourceType;
      41                 : 
      42                 : 
      43                 : struct InstallSourceInfo
      44             145 : {
      45                 :     InstallSourceType type;
      46                 :     Path nixExprPath; /* for srcNixExprDrvs, srcNixExprs */
      47                 :     Path profile; /* for srcProfile */
      48                 :     string systemFilter; /* for srcNixExprDrvs */
      49                 :     bool prebuiltOnly;
      50                 :     ATermMap autoArgs;
      51             145 :     InstallSourceInfo() : prebuiltOnly(false) { };
      52                 : };
      53                 : 
      54                 : 
      55                 : struct Globals
      56             290 : {
      57                 :     InstallSourceInfo instSource;
      58                 :     Path profile;
      59                 :     EvalState state;
      60                 :     bool dryRun;
      61                 :     bool preserveInstalled;
      62                 :     bool keepDerivations;
      63                 :     string forceName;
      64                 : };
      65                 : 
      66                 : 
      67                 : typedef void (* Operation) (Globals & globals,
      68                 :     Strings args, Strings opFlags, Strings opArgs);
      69                 : 
      70                 : 
      71               1 : void printHelp()
      72                 : {
      73               1 :     cout << string((char *) helpText, sizeof helpText);
      74               1 : }
      75                 : 
      76                 : 
      77                 : static string needArg(Strings::iterator & i,
      78             185 :     Strings & args, const string & arg)
      79                 : {
      80             185 :     if (i == args.end()) throw UsageError(
      81               0 :         format("`%1%' requires an argument") % arg);
      82             185 :     return *i++;
      83                 : }
      84                 : 
      85                 : 
      86                 : static bool parseInstallSourceOptions(Globals & globals,
      87               0 :     Strings::iterator & i, Strings & args, const string & arg)
      88                 : {
      89               0 :     if (arg == "--from-expression" || arg == "-E")
      90               0 :         globals.instSource.type = srcNixExprs;
      91               0 :     else if (arg == "--from-profile") {
      92               0 :         globals.instSource.type = srcProfile;
      93               0 :         globals.instSource.profile = needArg(i, args, arg);
      94                 :     }
      95               0 :     else if (arg == "--attr" || arg == "-A")
      96               0 :         globals.instSource.type = srcAttrPath;
      97               0 :     else if (arg == "--prebuilt-only" || arg == "-b")
      98               0 :         globals.instSource.prebuiltOnly = true;
      99               0 :     else return false;
     100               0 :     return true;
     101                 : }
     102                 : 
     103                 : 
     104              28 : static bool isNixExpr(const Path & path)
     105                 : {
     106                 :     struct stat st;
     107              28 :     if (stat(path.c_str(), &st) == -1)
     108               0 :         throw SysError(format("getting information about `%1%'") % path);
     109                 : 
     110              28 :     return !S_ISDIR(st.st_mode) || pathExists(path + "/default.nix");
     111                 : }
     112                 : 
     113                 : 
     114                 : static void getAllExprs(EvalState & state,
     115               0 :     const Path & path, ATermMap & attrs)
     116                 : {
     117               0 :     Strings names = readDirectory(path);
     118               0 :     StringSet namesSorted(names.begin(), names.end());
     119                 : 
     120               0 :     foreach (StringSet::iterator, i, namesSorted) {
     121               0 :         Path path2 = path + "/" + *i;
     122                 :         
     123                 :         struct stat st;
     124               0 :         if (stat(path2.c_str(), &st) == -1)
     125               0 :             continue; // ignore dangling symlinks in ~/.nix-defexpr
     126                 :         
     127               0 :         if (isNixExpr(path2)) {
     128                 :             /* Strip off the `.nix' filename suffix (if applicable),
     129                 :                otherwise the attribute cannot be selected with the
     130                 :                `-A' option.  Useful if you want to stick a Nix
     131                 :                expression directly in ~/.nix-defexpr. */
     132               0 :             string attrName = *i;
     133               0 :             if (hasSuffix(attrName, ".nix"))
     134               0 :                 attrName = string(attrName, 0, attrName.size() - 4);
     135                 :             attrs.set(toATerm(attrName), makeAttrRHS(
     136               0 :                     parseExprFromFile(state, absPath(path2)), makeNoPos()));
     137                 :         }
     138                 :         else
     139                 :             /* `path2' is a directory (with no default.nix in it);
     140                 :                recurse into it. */
     141               0 :             getAllExprs(state, path2, attrs);
     142               0 :     }
     143               0 : }
     144                 : 
     145                 : 
     146              28 : static Expr loadSourceExpr(EvalState & state, const Path & path)
     147                 : {
     148              28 :     if (isNixExpr(path)) return parseExprFromFile(state, absPath(path));
     149                 : 
     150                 :     /* The path is a directory.  Put the Nix expressions in the
     151                 :        directory in an attribute set, with the file name of each
     152                 :        expression as the attribute name.  Recurse into subdirectories
     153                 :        (but keep the attribute set flat, not nested, to make it easier
     154                 :        for a user to have a ~/.nix-defexpr directory that includes
     155                 :        some system-wide directory). */
     156               0 :     ATermMap attrs;
     157               0 :     attrs.set(toATerm("_combineChannels"), makeAttrRHS(makeList(ATempty), makeNoPos()));
     158               0 :     getAllExprs(state, path, attrs);
     159               0 :     return makeAttrs(attrs);
     160                 : }
     161                 : 
     162                 : 
     163                 : static void loadDerivations(EvalState & state, Path nixExprPath,
     164                 :     string systemFilter, const ATermMap & autoArgs,
     165              28 :     const string & pathPrefix, DrvInfos & elems)
     166                 : {
     167                 :     getDerivations(state,
     168                 :         findAlongAttrPath(state, pathPrefix, autoArgs, loadSourceExpr(state, nixExprPath)),
     169              28 :         pathPrefix, autoArgs, elems);
     170                 : 
     171                 :     /* Filter out all derivations not applicable to the current
     172                 :        system. */
     173             164 :     for (DrvInfos::iterator i = elems.begin(), j; i != elems.end(); i = j) {
     174             136 :         j = i; j++;
     175             136 :         if (systemFilter != "*" && i->system != systemFilter)
     176               0 :             elems.erase(i);
     177                 :     }
     178              28 : }
     179                 : 
     180                 : 
     181             146 : static Path getHomeDir()
     182                 : {
     183             146 :     Path homeDir(getEnv("HOME", ""));
     184             292 :     if (homeDir == "") throw Error("HOME environment variable not set");
     185               0 :     return homeDir;
     186                 : }
     187                 : 
     188                 : 
     189             145 : static Path getDefNixExprPath()
     190                 : {
     191             145 :     return getHomeDir() + "/.nix-defexpr";
     192                 : }
     193                 : 
     194                 : 
     195                 : struct AddPos : TermFun
     196             224 : {
     197            4161 :     ATerm operator () (ATerm e)
     198                 :     {
     199                 :         ATerm x, y;
     200            4161 :         if (matchObsoleteBind(e, x, y))
     201               0 :             return makeBind(x, y, makeNoPos());
     202            4161 :         if (matchObsoleteStr(e, x))
     203               0 :             return makeStr(x, ATempty);
     204            4161 :         return e;
     205                 :     }
     206                 : };
     207                 : 
     208                 : 
     209             121 : static DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
     210                 : {
     211             121 :     Path path = userEnv + "/manifest";
     212                 : 
     213             121 :     if (!pathExists(path))
     214               9 :         return DrvInfos(); /* not an error, assume nothing installed */
     215                 : 
     216             112 :     Expr e = ATreadFromNamedFile(path.c_str());
     217             112 :     if (!e) throw Error(format("cannot read Nix expression from `%1%'") % path);
     218                 : 
     219                 :     /* Compatibility: Bind(x, y) -> Bind(x, y, NoPos). */
     220             112 :     AddPos addPos;
     221             112 :     e = bottomupRewrite(addPos, e);
     222                 : 
     223             112 :     DrvInfos elems;
     224             224 :     getDerivations(state, e, "", ATermMap(1), elems);
     225             112 :     return elems;
     226                 : }
     227                 : 
     228                 : 
     229                 : /* Ensure exclusive access to a profile.  Any command that modifies
     230                 :    the profile first acquires this lock. */
     231              59 : static void lockProfile(PathLocks & lock, const Path & profile)
     232                 : {
     233                 :     lock.lockPaths(singleton<PathSet>(profile),
     234              59 :         (format("waiting for lock on profile `%1%'") % profile).str());
     235              59 :     lock.setDeletion(true);
     236              59 : }
     237                 : 
     238                 : 
     239                 : /* Optimistic locking is used by long-running operations like `nix-env
     240                 :    -i'.  Instead of acquiring the exclusive lock for the entire
     241                 :    duration of the operation, we just perform the operation
     242                 :    optimistically (without an exclusive lock), and check at the end
     243                 :    whether the profile changed while we were busy (i.e., the symlink
     244                 :    target changed).  If so, the operation is restarted.  Restarting is
     245                 :    generally cheap, since the build results are still in the Nix
     246                 :    store.  Most of the time, only the user environment has to be
     247                 :    rebuilt. */
     248              89 : static string optimisticLockProfile(const Path & profile)
     249                 : {
     250              89 :     return pathExists(profile) ? readLink(profile) : "";
     251                 : }
     252                 : 
     253                 : 
     254                 : static bool createUserEnv(EvalState & state, const DrvInfos & elems,
     255                 :     const Path & profile, bool keepDerivations,
     256              46 :     const string & lockToken)
     257                 : {
     258                 :     /* Build the components in the user environment, if they don't
     259                 :        exist already. */
     260              46 :     PathSet drvsToBuild;
     261              88 :     for (DrvInfos::const_iterator i = elems.begin(); 
     262                 :          i != elems.end(); ++i)
     263                 :         /* Call to `isDerivation' is for compatibility with Nix <= 0.7
     264                 :            user environments. */
     265              42 :         if (i->queryDrvPath(state) != "" &&
     266                 :             isDerivation(i->queryDrvPath(state)))
     267              28 :             drvsToBuild.insert(i->queryDrvPath(state));
     268                 : 
     269              46 :     debug(format("building user environment dependencies"));
     270              46 :     store->buildDerivations(drvsToBuild);
     271                 : 
     272                 :     /* Get the environment builder expression. */
     273                 :     Expr envBuilder = parseExprFromFile(state,
     274              46 :         nixDataDir + "/nix/corepkgs/buildenv"); /* !!! */
     275                 : 
     276                 :     /* Construct the whole top level derivation. */
     277              46 :     PathSet references;
     278              46 :     ATermList manifest = ATempty;
     279              46 :     ATermList inputs = ATempty;
     280              88 :     for (DrvInfos::const_iterator i = elems.begin(); 
     281                 :          i != elems.end(); ++i)
     282                 :     {
     283                 :         /* Create a pseudo-derivation containing the name, system,
     284                 :            output path, and optionally the derivation path, as well as
     285                 :            the meta attributes. */
     286              42 :         Path drvPath = keepDerivations ? i->queryDrvPath(state) : "";
     287                 : 
     288              42 :         MetaInfo meta = i->queryMetaInfo(state);
     289              42 :         ATermList metaList = ATempty;
     290              75 :         foreach (MetaInfo::iterator, j, meta)
     291                 :             metaList = ATinsert(metaList,
     292              33 :                 makeBind(toATerm(j->first), makeStr(j->second), makeNoPos()));
     293                 :         
     294              42 :         ATermList as = ATmakeList5(
     295                 :             makeBind(toATerm("type"),
     296                 :                 makeStr("derivation"), makeNoPos()),
     297                 :             makeBind(toATerm("name"),
     298                 :                 makeStr(i->name), makeNoPos()),
     299                 :             makeBind(toATerm("system"),
     300                 :                 makeStr(i->system), makeNoPos()),
     301                 :             makeBind(toATerm("outPath"),
     302                 :                 makeStr(i->queryOutPath(state)), makeNoPos()), 
     303                 :             makeBind(toATerm("meta"), makeAttrs(metaList), makeNoPos()));
     304                 :         
     305              42 :         if (drvPath != "") as = ATinsert(as, 
     306                 :             makeBind(toATerm("drvPath"),
     307               0 :                 makeStr(drvPath), makeNoPos()));
     308                 :         
     309              42 :         manifest = ATinsert(manifest, makeAttrs(as));
     310                 :         
     311              42 :         inputs = ATinsert(inputs, makeStr(i->queryOutPath(state)));
     312                 : 
     313                 :         /* This is only necessary when installing store paths, e.g.,
     314                 :            `nix-env -i /nix/store/abcd...-foo'. */
     315              42 :         store->addTempRoot(i->queryOutPath(state));
     316              42 :         store->ensurePath(i->queryOutPath(state));
     317                 :         
     318              42 :         references.insert(i->queryOutPath(state));
     319              42 :         if (drvPath != "") references.insert(drvPath);
     320                 :     }
     321                 : 
     322                 :     /* Also write a copy of the list of inputs to the store; we need
     323                 :        it for future modifications of the environment. */
     324                 :     Path manifestFile = store->addTextToStore("env-manifest",
     325              46 :         atPrint(canonicaliseExpr(makeList(ATreverse(manifest)))), references);
     326                 : 
     327              46 :     Expr topLevel = makeCall(envBuilder, makeAttrs(ATmakeList3(
     328                 :         makeBind(toATerm("system"),
     329                 :             makeStr(thisSystem), makeNoPos()),
     330                 :         makeBind(toATerm("derivations"),
     331                 :             makeList(ATreverse(manifest)), makeNoPos()),
     332                 :         makeBind(toATerm("manifest"),
     333                 :             makeStr(manifestFile, singleton<PathSet>(manifestFile)), makeNoPos())
     334                 :         )));
     335                 : 
     336                 :     /* Instantiate it. */
     337              46 :     debug(format("evaluating builder expression `%1%'") % topLevel);
     338              46 :     DrvInfo topLevelDrv;
     339              46 :     if (!getDerivation(state, topLevel, topLevelDrv))
     340               0 :         abort();
     341                 :     
     342                 :     /* Realise the resulting store expression. */
     343              46 :     debug(format("building user environment"));
     344              46 :     store->buildDerivations(singleton<PathSet>(topLevelDrv.queryDrvPath(state)));
     345                 : 
     346                 :     /* Switch the current user environment to the output path. */
     347              43 :     PathLocks lock;
     348              43 :     lockProfile(lock, profile);
     349                 : 
     350              43 :     Path lockTokenCur = optimisticLockProfile(profile);
     351              43 :     if (lockToken != lockTokenCur) {
     352               0 :         printMsg(lvlError, format("profile `%1%' changed while we were busy; restarting") % profile);
     353               0 :         return false;
     354                 :     }
     355                 :     
     356              43 :     debug(format("switching to new user environment"));
     357              43 :     Path generation = createGeneration(profile, topLevelDrv.queryOutPath(state));
     358              43 :     switchLink(profile, generation);
     359                 : 
     360              43 :     return true;
     361                 : }
     362                 : 
     363                 : 
     364                 : static int comparePriorities(EvalState & state,
     365              24 :     const DrvInfo & drv1, const DrvInfo & drv2)
     366                 : {
     367                 :     int prio1, prio2;
     368              24 :     if (!string2Int(drv1.queryMetaInfo(state, "priority"), prio1)) prio1 = 0;
     369              24 :     if (!string2Int(drv2.queryMetaInfo(state, "priority"), prio2)) prio2 = 0;
     370              24 :     return prio2 - prio1; /* higher number = lower priority, so negate */
     371                 : }
     372                 : 
     373                 : 
     374               0 : static bool isPrebuilt(EvalState & state, const DrvInfo & elem)
     375                 : {
     376                 :     return
     377                 :         store->isValidPath(elem.queryOutPath(state)) ||
     378               0 :         store->hasSubstitutes(elem.queryOutPath(state));
     379                 : }
     380                 : 
     381                 : 
     382                 : static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
     383             103 :     const Strings & args, bool newestOnly, bool prebuiltOnly)
     384                 : {
     385             103 :     DrvNames selectors = drvNamesFromArgs(args);
     386                 : 
     387             103 :     DrvInfos elems;
     388             103 :     set<unsigned int> done;
     389                 : 
     390             209 :     for (DrvNames::iterator i = selectors.begin();
     391                 :          i != selectors.end(); ++i)
     392                 :     {
     393                 :         typedef list<std::pair<DrvInfo, unsigned int> > Matches;
     394             106 :         Matches matches;
     395             106 :         unsigned int n = 0;
     396             444 :         for (DrvInfos::const_iterator j = allElems.begin();
     397                 :              j != allElems.end(); ++j, ++n)
     398                 :         {
     399             232 :             DrvName drvName(j->name);
     400             232 :             if (i->matches(drvName)) {
     401             160 :                 i->hits++;
     402             160 :                 if (!prebuiltOnly || isPrebuilt(state, *j))
     403             160 :                     matches.push_back(std::pair<DrvInfo, unsigned int>(*j, n));
     404                 :             }
     405                 :         }
     406                 : 
     407                 :         /* If `newestOnly', if a selector matches multiple derivations
     408                 :            with the same name, pick the one with the highest priority.
     409                 :            If there are multiple derivations with the same priority,
     410                 :            pick the one with the highest version.  If there are
     411                 :            multiple derivations with the same priority and name and
     412                 :            version, then pick the first one. */
     413             106 :         if (newestOnly) {
     414                 : 
     415                 :             /* Map from package names to derivations. */
     416                 :             typedef map<string, std::pair<DrvInfo, unsigned int> > Newest;
     417              22 :             Newest newest;
     418              22 :             StringSet multiple;
     419                 : 
     420              62 :             for (Matches::iterator j = matches.begin(); j != matches.end(); ++j) {
     421              40 :                 DrvName drvName(j->first.name);
     422              40 :                 int d = 1;
     423                 : 
     424              40 :                 Newest::iterator k = newest.find(drvName.name);
     425                 :                 
     426              40 :                 if (k != newest.end()) {
     427              15 :                     d = comparePriorities(state, j->first, k->second.first);
     428              15 :                     if (d == 0)
     429              15 :                         d = compareVersions(drvName.version, DrvName(k->second.first.name).version);
     430                 :                 }
     431                 : 
     432              40 :                 if (d > 0) {
     433              40 :                     newest[drvName.name] = *j;
     434              40 :                     multiple.erase(j->first.name);
     435               0 :                 } else if (d == 0) {
     436               0 :                     multiple.insert(j->first.name);
     437                 :                 }
     438                 :             }
     439                 : 
     440              22 :             matches.clear();
     441              47 :             for (Newest::iterator j = newest.begin(); j != newest.end(); ++j) {
     442              25 :                 if (multiple.find(j->second.first.name) != multiple.end())
     443               0 :                     printMsg(lvlInfo,
     444                 :                         format("warning: there are multiple derivations named `%1%'; using the first one")
     445                 :                         % j->second.first.name);
     446              25 :                 matches.push_back(j->second);
     447              22 :             }
     448                 :         }
     449                 : 
     450                 :         /* Insert only those elements in the final list that we
     451                 :            haven't inserted before. */
     452             251 :         for (Matches::iterator j = matches.begin(); j != matches.end(); ++j)
     453             145 :             if (done.find(j->second) == done.end()) {
     454             145 :                 done.insert(j->second);
     455             145 :                 elems.push_back(j->first);
     456                 :             }
     457                 :     }
     458                 :             
     459                 :     /* Check that all selectors have been used. */
     460             209 :     for (DrvNames::iterator i = selectors.begin();
     461                 :          i != selectors.end(); ++i)
     462             106 :         if (i->hits == 0 && i->fullName != "*")
     463                 :             throw Error(format("selector `%1%' matches no derivations")
     464               0 :                 % i->fullName);
     465                 : 
     466             103 :     return elems;
     467                 : }
     468                 : 
     469                 : 
     470              46 : static bool isPath(const string & s)
     471                 : {
     472              46 :     return s.find('/') != string::npos;
     473                 : }
     474                 : 
     475                 : 
     476                 : static void queryInstSources(EvalState & state,
     477                 :     const InstallSourceInfo & instSource, const Strings & args,
     478              30 :     DrvInfos & elems, bool newestOnly)
     479                 : {
     480              30 :     InstallSourceType type = instSource.type;
     481              30 :     if (type == srcUnknown && args.size() > 0 && isPath(args.front()))
     482               8 :         type = srcStorePaths;
     483                 :     
     484              30 :     switch (type) {
     485                 : 
     486                 :         /* Get the available user environment elements from the
     487                 :            derivations specified in a Nix expression, including only
     488                 :            those with names matching any of the names in `args'. */
     489                 :         case srcUnknown:
     490                 :         case srcNixExprDrvs: {
     491                 : 
     492                 :             /* Load the derivations from the (default or specified)
     493                 :                Nix expression. */
     494              22 :             DrvInfos allElems;
     495                 :             loadDerivations(state, instSource.nixExprPath,
     496              44 :                 instSource.systemFilter, instSource.autoArgs, "", allElems);
     497                 : 
     498                 :             elems = filterBySelector(state, allElems, args,
     499              44 :                 newestOnly, instSource.prebuiltOnly);
     500                 :     
     501              22 :             break;
     502                 :         }
     503                 : 
     504                 :         /* Get the available user environment elements from the Nix
     505                 :            expressions specified on the command line; these should be
     506                 :            functions that take the default Nix expression file as
     507                 :            argument, e.g., if the file is `./foo.nix', then the
     508                 :            argument `x: x.bar' is equivalent to `(x: x.bar)
     509                 :            (import ./foo.nix)' = `(import ./foo.nix).bar'. */
     510                 :         case srcNixExprs: {
     511                 :                 
     512               0 :             Expr e1 = loadSourceExpr(state, instSource.nixExprPath);
     513                 : 
     514               0 :             for (Strings::const_iterator i = args.begin();
     515                 :                  i != args.end(); ++i)
     516                 :             {
     517               0 :                 Expr e2 = parseExprFromString(state, *i, absPath("."));
     518               0 :                 Expr call = makeCall(e2, e1);
     519               0 :                 getDerivations(state, call, "", instSource.autoArgs, elems);
     520                 :             }
     521                 :             
     522               0 :             break;
     523                 :         }
     524                 :             
     525                 :         /* The available user environment elements are specified as a
     526                 :            list of store paths (which may or may not be
     527                 :            derivations). */
     528                 :         case srcStorePaths: {
     529                 : 
     530              16 :             for (Strings::const_iterator i = args.begin();
     531                 :                  i != args.end(); ++i)
     532                 :             {
     533               8 :                 Path path = followLinksToStorePath(*i);
     534                 : 
     535               8 :                 DrvInfo elem;
     536               8 :                 elem.attrs = boost::shared_ptr<ATermMap>(new ATermMap(0)); /* ugh... */
     537               8 :                 string name = baseNameOf(path);
     538               8 :                 string::size_type dash = name.find('-');
     539               8 :                 if (dash != string::npos)
     540               8 :                     name = string(name, dash + 1);
     541                 : 
     542               8 :                 if (isDerivation(path)) {
     543               0 :                     elem.setDrvPath(path);
     544               0 :                     elem.setOutPath(findOutput(derivationFromPath(path), "out"));
     545               0 :                     if (name.size() >= drvExtension.size() &&
     546                 :                         string(name, name.size() - drvExtension.size()) == drvExtension)
     547               0 :                         name = string(name, 0, name.size() - drvExtension.size());
     548                 :                 }
     549               8 :                 else elem.setOutPath(path);
     550                 : 
     551               8 :                 elem.name = name;
     552                 : 
     553               8 :                 elems.push_back(elem);
     554                 :             }
     555                 :             
     556               8 :             break;
     557                 :         }
     558                 :             
     559                 :         /* Get the available user environment elements from another
     560                 :            user environment.  These are then filtered as in the
     561                 :            `srcNixExprDrvs' case. */
     562                 :         case srcProfile: {
     563                 :             elems = filterBySelector(state,
     564                 :                 queryInstalled(state, instSource.profile),
     565               0 :                 args, newestOnly, instSource.prebuiltOnly);
     566               0 :             break;
     567                 :         }
     568                 : 
     569                 :         case srcAttrPath: {
     570               0 :             for (Strings::const_iterator i = args.begin();
     571                 :                  i != args.end(); ++i)
     572                 :                 getDerivations(state,
     573                 :                     findAlongAttrPath(state, *i, instSource.autoArgs,
     574                 :                         loadSourceExpr(state, instSource.nixExprPath)),
     575               0 :                     "", instSource.autoArgs, elems);
     576                 :             break;
     577                 :         }
     578                 :     }
     579              30 : }
     580                 : 
     581                 : 
     582              30 : static void printMissing(EvalState & state, const DrvInfos & elems)
     583                 : {
     584              30 :     PathSet targets;
     585              66 :     foreach (DrvInfos::const_iterator, i, elems) {
     586              36 :         Path drvPath = i->queryDrvPath(state);
     587              36 :         if (drvPath != "")
     588              28 :             targets.insert(drvPath);
     589                 :         else
     590               8 :             targets.insert(i->queryOutPath(state));
     591                 :     }
     592                 : 
     593              30 :     printMissing(targets);
     594              30 : }
     595                 : 
     596                 : 
     597                 : static void installDerivations(Globals & globals,
     598              27 :     const Strings & args, const Path & profile)
     599                 : {
     600              27 :     debug(format("installing derivations"));
     601                 : 
     602                 :     /* Get the set of user environment elements to be installed. */
     603              27 :     DrvInfos newElems;
     604              27 :     queryInstSources(globals.state, globals.instSource, args, newElems, true);
     605                 : 
     606              27 :     StringSet newNames;
     607              60 :     for (DrvInfos::iterator i = newElems.begin(); i != newElems.end(); ++i) {
     608                 :         /* `forceName' is a hack to get package names right in some
     609                 :            one-click installs, namely those where the name used in the
     610                 :            path is not the one we want (e.g., `java-front' versus
     611                 :            `java-front-0.9pre15899'). */
     612              33 :         if (globals.forceName != "")
     613               2 :             i->name = globals.forceName;
     614              33 :         newNames.insert(DrvName(i->name).name);
     615                 :     }
     616                 : 
     617                 :     /* Add in the already installed derivations, unless they have the
     618                 :        same name as a to-be-installed element. */
     619                 : 
     620               3 :     while (true) {
     621              27 :         string lockToken = optimisticLockProfile(profile);
     622                 :         
     623              27 :         DrvInfos installedElems = queryInstalled(globals.state, profile);
     624                 :         
     625              27 :         DrvInfos allElems(newElems);
     626              36 :         foreach (DrvInfos::iterator, i, installedElems) {
     627               9 :             DrvName drvName(i->name);
     628               9 :             MetaInfo meta = i->queryMetaInfo(globals.state);
     629               9 :             if (!globals.preserveInstalled &&
     630                 :                 newNames.find(drvName.name) != newNames.end() &&
     631                 :                 meta["keep"] != "true")
     632               6 :                 printMsg(lvlInfo,
     633                 :                     format("replacing old `%1%'") % i->name);
     634                 :             else
     635               3 :                 allElems.push_back(*i);
     636                 :         }
     637                 : 
     638              60 :         foreach (DrvInfos::iterator, i, newElems)
     639              33 :             printMsg(lvlInfo, format("installing `%1%'") % i->name);
     640                 :     
     641              27 :         printMissing(globals.state, newElems);
     642                 :     
     643              27 :         if (globals.dryRun) return;
     644                 : 
     645              27 :         if (createUserEnv(globals.state, allElems,
     646                 :                 profile, globals.keepDerivations, lockToken)) break;
     647               3 :     }
     648                 : }
     649                 : 
     650                 : 
     651                 : static void opInstall(Globals & globals,
     652              27 :     Strings args, Strings opFlags, Strings opArgs)
     653                 : {
     654              27 :     for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ) {
     655               0 :         string arg = *i++;
     656               0 :         if (parseInstallSourceOptions(globals, i, opFlags, arg)) ;
     657               0 :         else if (arg == "--preserve-installed" || arg == "-P")
     658               0 :             globals.preserveInstalled = true;
     659               0 :         else throw UsageError(format("unknown flag `%1%'") % arg);
     660                 :     }
     661                 : 
     662              27 :     installDerivations(globals, opArgs, globals.profile);
     663              24 : }
     664                 : 
     665                 : 
     666                 : typedef enum { utLt, utLeq, utEq, utAlways } UpgradeType;
     667                 : 
     668                 : 
     669                 : static void upgradeDerivations(Globals & globals,
     670               3 :     const Strings & args, UpgradeType upgradeType)
     671                 : {
     672               3 :     debug(format("upgrading derivations"));
     673                 : 
     674                 :     /* Upgrade works as follows: we take all currently installed
     675                 :        derivations, and for any derivation matching any selector, look
     676                 :        for a derivation in the input Nix expression that has the same
     677                 :        name and a higher version number. */
     678                 : 
     679               0 :     while (true) {
     680               3 :         string lockToken = optimisticLockProfile(globals.profile);
     681                 :         
     682               3 :         DrvInfos installedElems = queryInstalled(globals.state, globals.profile);
     683                 : 
     684                 :         /* Fetch all derivations from the input file. */
     685               3 :         DrvInfos availElems;
     686               3 :         queryInstSources(globals.state, globals.instSource, args, availElems, false);
     687                 : 
     688                 :         /* Go through all installed derivations. */
     689               3 :         DrvInfos newElems;
     690              12 :         foreach (DrvInfos::iterator, i, installedElems) {
     691               3 :             DrvName drvName(i->name);
     692                 : 
     693               3 :             MetaInfo meta = i->queryMetaInfo(globals.state);
     694               3 :             if (meta["keep"] == "true") {
     695               0 :                 newElems.push_back(*i);
     696               0 :                 continue;
     697                 :             }
     698                 : 
     699                 :             /* Find the derivation in the input Nix expression with
     700                 :                the same name that satisfies the version constraints
     701                 :                specified by upgradeType.  If there are multiple
     702                 :                matches, take the one with the highest priority.  If
     703                 :                there are still multiple matches, take the one with the
     704                 :                highest version. */
     705               3 :             DrvInfos::iterator bestElem = availElems.end();
     706               3 :             DrvName bestName;
     707              12 :             foreach (DrvInfos::iterator, j, availElems) {
     708               9 :                 DrvName newName(j->name);
     709               9 :                 if (newName.name == drvName.name) {
     710               9 :                     int d = comparePriorities(globals.state, *i, *j);
     711               9 :                     if (d == 0) d = compareVersions(drvName.version, newName.version);
     712               9 :                     if (upgradeType == utLt && d < 0 ||
     713                 :                         upgradeType == utLeq && d <= 0 ||
     714                 :                         upgradeType == utEq && d == 0 ||
     715                 :                         upgradeType == utAlways)
     716                 :                     {
     717               3 :                         int d2 = -1;
     718               3 :                         if (bestElem != availElems.end()) {
     719               0 :                             d2 = comparePriorities(globals.state, *bestElem, *j);
     720               0 :                             if (d2 == 0) d2 = compareVersions(bestName.version, newName.version);
     721                 :                         }
     722               3 :                         if (d2 < 0) {
     723               3 :                             bestElem = j;
     724               3 :                             bestName = newName;
     725                 :                         }
     726                 :                     }
     727                 :                 }
     728                 :             }
     729                 : 
     730               3 :             if (bestElem != availElems.end() &&
     731                 :                 i->queryOutPath(globals.state) !=
     732                 :                 bestElem->queryOutPath(globals.state))
     733                 :             {
     734               3 :                 printMsg(lvlInfo,
     735                 :                     format("upgrading `%1%' to `%2%'")
     736                 :                     % i->name % bestElem->name);
     737               3 :                 newElems.push_back(*bestElem);
     738               0 :             } else newElems.push_back(*i);
     739                 :         }
     740                 :     
     741               3 :         printMissing(globals.state, newElems);
     742                 :     
     743               3 :         if (globals.dryRun) return;
     744                 : 
     745               3 :         if (createUserEnv(globals.state, newElems,
     746               3 :                 globals.profile, globals.keepDerivations, lockToken)) break;
     747                 :     }
     748                 : }
     749                 : 
     750                 : 
     751                 : static void opUpgrade(Globals & globals,
     752               3 :     Strings args, Strings opFlags, Strings opArgs)
     753                 : {
     754               3 :     UpgradeType upgradeType = utLt;
     755               3 :     for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ) {
     756               0 :         string arg = *i++;
     757               0 :         if (parseInstallSourceOptions(globals, i, opFlags, arg)) ;
     758               0 :         else if (arg == "--lt") upgradeType = utLt;
     759               0 :         else if (arg == "--leq") upgradeType = utLeq;
     760               0 :         else if (arg == "--eq") upgradeType = utEq;
     761               0 :         else if (arg == "--always") upgradeType = utAlways;
     762               0 :         else throw UsageError(format("unknown flag `%1%'") % arg);
     763                 :     }
     764                 : 
     765               3 :     upgradeDerivations(globals, opArgs, upgradeType);
     766               3 : }
     767                 : 
     768                 : 
     769                 : static void setMetaFlag(EvalState & state, DrvInfo & drv,
     770               0 :     const string & name, const string & value)
     771                 : {
     772               0 :     MetaInfo meta = drv.queryMetaInfo(state);
     773               0 :     meta[name] = value;
     774               0 :     drv.setMetaInfo(meta);
     775               0 : }
     776                 : 
     777                 : 
     778                 : static void opSetFlag(Globals & globals,
     779               0 :     Strings args, Strings opFlags, Strings opArgs)
     780                 : {
     781               0 :     if (opFlags.size() > 0)
     782               0 :         throw UsageError(format("unknown flag `%1%'") % opFlags.front());
     783               0 :     if (opArgs.size() < 2)
     784               0 :         throw UsageError("not enough arguments to `--set-flag'");
     785                 : 
     786               0 :     Strings::iterator arg = opArgs.begin();
     787               0 :     string flagName = *arg++;
     788               0 :     string flagValue = *arg++;
     789               0 :     DrvNames selectors = drvNamesFromArgs(Strings(arg, opArgs.end()));
     790                 : 
     791               0 :     while (true) {
     792               0 :         string lockToken = optimisticLockProfile(globals.profile);
     793                 : 
     794               0 :         DrvInfos installedElems = queryInstalled(globals.state, globals.profile);
     795                 : 
     796                 :         /* Update all matching derivations. */
     797               0 :         foreach (DrvInfos::iterator, i, installedElems) {
     798               0 :             DrvName drvName(i->name);
     799               0 :             foreach (DrvNames::iterator, j, selectors)
     800               0 :                 if (j->matches(drvName)) {
     801               0 :                     printMsg(lvlInfo,
     802                 :                         format("setting flag on `%1%'") % i->name);
     803               0 :                     setMetaFlag(globals.state, *i, flagName, flagValue);
     804               0 :                     break;
     805                 :                 }
     806                 :         }
     807                 : 
     808                 :         /* Write the new user environment. */
     809               0 :         if (createUserEnv(globals.state, installedElems,
     810                 :                 globals.profile, globals.keepDerivations, lockToken)) break;
     811               0 :     }
     812               0 : }
     813                 : 
     814                 : 
     815                 : static void opSet(Globals & globals,
     816               0 :     Strings args, Strings opFlags, Strings opArgs)
     817                 : {
     818               0 :     for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ) {
     819               0 :         string arg = *i++;
     820               0 :         if (parseInstallSourceOptions(globals, i, opFlags, arg)) ;
     821               0 :         else throw UsageError(format("unknown flag `%1%'") % arg);
     822                 :     }
     823                 : 
     824               0 :     DrvInfos elems;
     825               0 :     queryInstSources(globals.state, globals.instSource, opArgs, elems, true);
     826                 : 
     827               0 :     if (elems.size() != 1)
     828               0 :         throw Error("--set requires exactly one derivation");
     829                 :     
     830               0 :     DrvInfo & drv(elems.front());
     831                 : 
     832               0 :     if (drv.queryDrvPath(globals.state) != "") {
     833               0 :         PathSet paths = singleton<PathSet>(drv.queryDrvPath(globals.state));
     834               0 :         printMissing(paths);
     835               0 :         if (globals.dryRun) return;
     836               0 :         store->buildDerivations(paths);
     837                 :     }
     838                 :     else {
     839               0 :         printMissing(singleton<PathSet>(drv.queryOutPath(globals.state)));
     840               0 :         if (globals.dryRun) return;
     841               0 :         store->ensurePath(drv.queryOutPath(globals.state));
     842                 :     }
     843                 : 
     844               0 :     debug(format("switching to new user environment"));
     845               0 :     Path generation = createGeneration(globals.profile, drv.queryOutPath(globals.state));
     846               0 :     switchLink(globals.profile, generation);
     847                 : }
     848                 : 
     849                 : 
     850                 : static void uninstallDerivations(Globals & globals, Strings & selectors,
     851              16 :     Path & profile)
     852                 : {
     853               0 :     while (true) {
     854              16 :         string lockToken = optimisticLockProfile(profile);
     855                 : 
     856              16 :         DrvInfos installedElems = queryInstalled(globals.state, profile);
     857              16 :         DrvInfos newElems;
     858                 : 
     859              48 :         foreach (DrvInfos::iterator, i, installedElems) {
     860              16 :             DrvName drvName(i->name);
     861              16 :             bool found = false;
     862              19 :             foreach (Strings::iterator, j, selectors)
     863                 :                 /* !!! the repeated calls to followLinksToStorePath()
     864                 :                    are expensive, should pre-compute them. */
     865              16 :                 if ((isPath(*j) && i->queryOutPath(globals.state) == followLinksToStorePath(*j))
     866                 :                     || DrvName(*j).matches(drvName))
     867                 :                 {
     868              13 :                     printMsg(lvlInfo, format("uninstalling `%1%'") % i->name);
     869              13 :                     found = true;
     870              13 :                     break;
     871                 :                 }
     872              16 :             if (!found) newElems.push_back(*i);
     873                 :         }
     874                 : 
     875              16 :         if (globals.dryRun) return;
     876                 : 
     877              16 :         if (createUserEnv(globals.state, newElems,
     878              16 :                 profile, globals.keepDerivations, lockToken)) break;
     879                 :     }
     880                 : }
     881                 : 
     882                 : 
     883                 : static void opUninstall(Globals & globals,
     884              16 :     Strings args, Strings opFlags, Strings opArgs)
     885                 : {
     886              16 :     if (opFlags.size() > 0)
     887               0 :         throw UsageError(format("unknown flag `%1%'") % opFlags.front());
     888              16 :     uninstallDerivations(globals, opArgs, globals.profile);
     889              16 : }
     890                 : 
     891                 : 
     892             528 : static bool cmpChars(char a, char b)
     893                 : {
     894             528 :     return toupper(a) < toupper(b);
     895                 : }
     896                 : 
     897                 : 
     898              96 : static bool cmpElemByName(const DrvInfo & a, const DrvInfo & b)
     899                 : {
     900                 :     return lexicographical_compare(
     901                 :         a.name.begin(), a.name.end(),
     902              96 :         b.name.begin(), b.name.end(), cmpChars);
     903                 : }
     904                 : 
     905                 : 
     906                 : typedef list<Strings> Table;
     907                 : 
     908                 : 
     909              81 : void printTable(Table & table)
     910                 : {
     911              81 :     unsigned int nrColumns = table.size() > 0 ? table.front().size() : 0;
     912                 :     
     913              81 :     vector<unsigned int> widths;
     914              81 :     widths.resize(nrColumns);
     915                 :     
     916             192 :     for (Table::iterator i = table.begin(); i != table.end(); ++i) {
     917             111 :         assert(i->size() == nrColumns);
     918             111 :         Strings::iterator j;
     919                 :         unsigned int column;
     920             237 :         for (j = i->begin(), column = 0; j != i->end(); ++j, ++column)
     921             126 :             if (j->size() > widths[column]) widths[column] = j->size();
     922                 :     }
     923                 : 
     924             192 :     for (Table::iterator i = table.begin(); i != table.end(); ++i) { 
     925             111 :         Strings::iterator j;
     926                 :         unsigned int column;
     927             237 :         for (j = i->begin(), column = 0; j != i->end(); ++j, ++column)
     928                 :         {
     929             126 :             cout << *j;
     930             126 :             if (column < nrColumns - 1)
     931              15 :                 cout << string(widths[column] - j->size() + 2, ' ');
     932                 :         }
     933             111 :         cout << std::endl;
     934              81 :     }
     935              81 : }
     936                 : 
     937                 : 
     938                 : /* This function compares the version of a element against the
     939                 :    versions in the given set of elements.  `cvLess' means that only
     940                 :    lower versions are in the set, `cvEqual' means that at most an
     941                 :    equal version is in the set, and `cvGreater' means that there is at
     942                 :    least one element with a higher version in the set.  `cvUnavail'
     943                 :    means that there are no elements with the same name in the set. */
     944                 : 
     945                 : typedef enum { cvLess, cvEqual, cvGreater, cvUnavail } VersionDiff;
     946                 : 
     947                 : static VersionDiff compareVersionAgainstSet(
     948               0 :     const DrvInfo & elem, const DrvInfos & elems, string & version)
     949                 : {
     950               0 :     DrvName name(elem.name);
     951                 :     
     952               0 :     VersionDiff diff = cvUnavail;
     953               0 :     version = "?";
     954                 :     
     955               0 :     for (DrvInfos::const_iterator i = elems.begin(); i != elems.end(); ++i) {
     956               0 :         DrvName name2(i->name);
     957               0 :         if (name.name == name2.name) {
     958               0 :             int d = compareVersions(name.version, name2.version);
     959               0 :             if (d < 0) {
     960               0 :                 diff = cvGreater;
     961               0 :                 version = name2.version;
     962                 :             }
     963               0 :             else if (diff != cvGreater && d == 0) {
     964               0 :                 diff = cvEqual;
     965               0 :                 version = name2.version;
     966                 :             }
     967               0 :             else if (diff != cvGreater && diff != cvEqual && d > 0) {
     968               0 :                 diff = cvLess;
     969               0 :                 if (version == "" || compareVersions(version, name2.version) < 0)
     970               0 :                     version = name2.version;
     971                 :             }
     972                 :         }
     973                 :     }
     974                 : 
     975               0 :     return diff;
     976                 : }
     977                 : 
     978                 : 
     979               0 : static string colorString(const string & s)
     980                 : {
     981               0 :     if (!isatty(STDOUT_FILENO)) return s;
     982               0 :     return "\e[1;31m" + s + "\e[0m";
     983                 : }
     984                 : 
     985                 : 
     986                 : static void opQuery(Globals & globals,
     987              82 :     Strings args, Strings opFlags, Strings opArgs)
     988                 : {
     989                 :     typedef vector< map<string, string> > ResultSet;
     990              82 :     Strings remaining;
     991              82 :     string attrPath;
     992                 :         
     993              82 :     bool printStatus = false;
     994              82 :     bool printName = true;
     995              82 :     bool printAttrPath = false;
     996              82 :     bool printSystem = false;
     997              82 :     bool printDrvPath = false;
     998              82 :     bool printOutPath = false;
     999              82 :     bool printDescription = false;
    1000              82 :     bool printMeta = false;
    1001              82 :     bool prebuiltOnly = false;
    1002              82 :     bool compareVersions = false;
    1003              82 :     bool xmlOutput = false;
    1004                 : 
    1005              82 :     enum { sInstalled, sAvailable } source = sInstalled;
    1006                 : 
    1007              82 :     readOnlyMode = true; /* makes evaluation a bit faster */
    1008                 : 
    1009             186 :     for (Strings::iterator i = args.begin(); i != args.end(); ) {
    1010             105 :         string arg = *i++;
    1011             105 :         if (arg == "--status" || arg == "-s") printStatus = true;
    1012             105 :         else if (arg == "--no-name") printName = false;
    1013              98 :         else if (arg == "--system") printSystem = true;
    1014              98 :         else if (arg == "--description") printDescription = true;
    1015              95 :         else if (arg == "--compare-versions" || arg == "-c") compareVersions = true;
    1016              95 :         else if (arg == "--drv-path") printDrvPath = true;
    1017              95 :         else if (arg == "--out-path") printOutPath = true;
    1018              88 :         else if (arg == "--meta") printMeta = true;
    1019              88 :         else if (arg == "--installed") source = sInstalled;
    1020              88 :         else if (arg == "--available" || arg == "-a") source = sAvailable;
    1021              82 :         else if (arg == "--prebuilt-only" || arg == "-b") prebuiltOnly = true;
    1022              82 :         else if (arg == "--xml") xmlOutput = true;
    1023              82 :         else if (arg == "--attr-path" || arg == "-P") printAttrPath = true;
    1024              82 :         else if (arg == "--attr" || arg == "-A")
    1025               0 :             attrPath = needArg(i, args, arg);
    1026              82 :         else if (arg[0] == '-')
    1027               1 :             throw UsageError(format("unknown flag `%1%'") % arg);
    1028              81 :         else remaining.push_back(arg);
    1029                 :     }
    1030                 : 
    1031              81 :     if (remaining.size() == 0)
    1032               0 :         printMsg(lvlInfo, "warning: you probably meant to specify the argument '*' to show all packages");
    1033                 : 
    1034                 :     
    1035                 :     /* Obtain derivation information from the specified source. */
    1036              81 :     DrvInfos availElems, installedElems;
    1037                 : 
    1038              87 :     if (source == sInstalled || compareVersions || printStatus)
    1039              75 :         installedElems = queryInstalled(globals.state, globals.profile);
    1040                 : 
    1041              81 :     if (source == sAvailable || compareVersions)
    1042                 :         loadDerivations(globals.state, globals.instSource.nixExprPath,
    1043                 :             globals.instSource.systemFilter, globals.instSource.autoArgs,
    1044               6 :             attrPath, availElems);
    1045                 : 
    1046                 :     DrvInfos elems = filterBySelector(globals.state,
    1047                 :         source == sInstalled ? installedElems : availElems,
    1048              81 :         remaining, false, prebuiltOnly);
    1049                 :     
    1050              81 :     DrvInfos & otherElems(source == sInstalled ? availElems : installedElems);
    1051                 : 
    1052                 :     
    1053                 :     /* Sort them by name. */
    1054                 :     /* !!! */
    1055              81 :     vector<DrvInfo> elems2;
    1056             273 :     for (DrvInfos::iterator i = elems.begin(); i != elems.end(); ++i)
    1057             111 :         elems2.push_back(*i);
    1058              81 :     sort(elems2.begin(), elems2.end(), cmpElemByName);
    1059                 : 
    1060                 :     
    1061                 :     /* We only need to know the installed paths when we are querying
    1062                 :        the status of the derivation. */
    1063              81 :     PathSet installed; /* installed paths */
    1064                 :     
    1065              81 :     if (printStatus) {
    1066               0 :         for (DrvInfos::iterator i = installedElems.begin();
    1067                 :              i != installedElems.end(); ++i)
    1068               0 :             installed.insert(i->queryOutPath(globals.state));
    1069                 :     }
    1070                 : 
    1071                 :     
    1072                 :     /* Print the desired columns, or XML output. */
    1073              81 :     Table table;
    1074              81 :     std::ostringstream dummy;
    1075              81 :     XMLWriter xml(true, *(xmlOutput ? &cout : &dummy));
    1076              81 :     XMLOpenElement xmlRoot(xml, "items");
    1077                 :     
    1078             192 :     for (vector<DrvInfo>::iterator i = elems2.begin();
    1079                 :          i != elems2.end(); ++i)
    1080                 :     {
    1081                 :         try {
    1082                 : 
    1083                 :             /* For table output. */
    1084             111 :             Strings columns;
    1085                 : 
    1086                 :             /* For XML output. */
    1087             111 :             XMLAttrs attrs;
    1088                 : 
    1089             111 :             if (printStatus) {
    1090               0 :                 bool hasSubs = store->hasSubstitutes(i->queryOutPath(globals.state));
    1091               0 :                 bool isInstalled = installed.find(i->queryOutPath(globals.state)) != installed.end();
    1092               0 :                 bool isValid = store->isValidPath(i->queryOutPath(globals.state));
    1093               0 :                 if (xmlOutput) {
    1094               0 :                     attrs["installed"] = isInstalled ? "1" : "0";
    1095               0 :                     attrs["valid"] = isValid ? "1" : "0";
    1096               0 :                     attrs["substitutable"] = hasSubs ? "1" : "0";
    1097                 :                 } else
    1098                 :                     columns.push_back(
    1099                 :                         (string) (isInstalled ? "I" : "-")
    1100                 :                         + (isValid ? "P" : "-")
    1101               0 :                         + (hasSubs ? "S" : "-"));
    1102                 :             }
    1103                 : 
    1104             111 :             if (xmlOutput)
    1105               0 :                 attrs["attrPath"] = i->attrPath;
    1106             111 :             else if (printAttrPath)
    1107               0 :                 columns.push_back(i->attrPath);
    1108                 : 
    1109             111 :             if (xmlOutput)
    1110               0 :                 attrs["name"] = i->name;
    1111             111 :             else if (printName)
    1112             104 :                 columns.push_back(i->name);
    1113                 : 
    1114             111 :             if (compareVersions) {
    1115                 :                 /* Compare this element against the versions of the
    1116                 :                    same named packages in either the set of available
    1117                 :                    elements, or the set of installed elements.  !!!
    1118                 :                    This is O(N * M), should be O(N * lg M). */
    1119               0 :                 string version;
    1120               0 :                 VersionDiff diff = compareVersionAgainstSet(*i, otherElems, version);
    1121                 : 
    1122                 :                 char ch;
    1123               0 :                 switch (diff) {
    1124               0 :                     case cvLess: ch = '>'; break;
    1125               0 :                     case cvEqual: ch = '='; break;
    1126               0 :                     case cvGreater: ch = '<'; break;
    1127               0 :                     case cvUnavail: ch = '-'; break;
    1128               0 :                     default: abort();
    1129                 :                 }
    1130                 : 
    1131               0 :                 if (xmlOutput) {
    1132               0 :                     if (diff != cvUnavail) {
    1133               0 :                         attrs["versionDiff"] = ch;
    1134               0 :                         attrs["maxComparedVersion"] = version;
    1135                 :                     }
    1136                 :                 } else {
    1137               0 :                     string column = (string) "" + ch + " " + version;
    1138               0 :                     if (diff == cvGreater) column = colorString(column);
    1139               0 :                     columns.push_back(column);
    1140               0 :                 }
    1141                 :             }
    1142                 : 
    1143             111 :             if (xmlOutput) {
    1144               0 :                 if (i->system != "") attrs["system"] = i->system;
    1145                 :             }
    1146             111 :             else if (printSystem) 
    1147               0 :                 columns.push_back(i->system);
    1148                 : 
    1149             111 :             if (printDrvPath) {
    1150               0 :                 string drvPath = i->queryDrvPath(globals.state);
    1151               0 :                 if (xmlOutput) {
    1152               0 :                     if (drvPath != "") attrs["drvPath"] = drvPath;
    1153                 :                 } else
    1154               0 :                     columns.push_back(drvPath == "" ? "-" : drvPath);
    1155                 :             }
    1156                 :         
    1157             111 :             if (printOutPath) {
    1158               7 :                 string outPath = i->queryOutPath(globals.state);
    1159               7 :                 if (xmlOutput) {
    1160               0 :                     if (outPath != "") attrs["outPath"] = outPath;
    1161                 :                 } else
    1162               7 :                     columns.push_back(outPath);
    1163                 :             }
    1164                 : 
    1165             111 :             if (printDescription) {
    1166              15 :                 MetaInfo meta = i->queryMetaInfo(globals.state);
    1167              15 :                 string descr = meta["description"];
    1168              15 :                 if (xmlOutput) {
    1169               0 :                     if (descr != "") attrs["description"] = descr;
    1170                 :                 } else
    1171              15 :                     columns.push_back(descr);
    1172                 :             }
    1173                 : 
    1174             111 :             if (xmlOutput)
    1175               0 :                 if (printMeta) {
    1176               0 :                     XMLOpenElement item(xml, "item", attrs);
    1177               0 :                     MetaInfo meta = i->queryMetaInfo(globals.state);
    1178               0 :                     for (MetaInfo::iterator j = meta.begin(); j != meta.end(); ++j) {
    1179               0 :                         XMLAttrs attrs2;
    1180               0 :                         attrs2["name"] = j->first;
    1181               0 :                         attrs2["value"] = j->second;
    1182               0 :                         xml.writeEmptyElement("meta", attrs2);
    1183               0 :                     }
    1184                 :                 }
    1185                 :                 else
    1186               0 :                     xml.writeEmptyElement("item", attrs);
    1187                 :             else
    1188             111 :                 table.push_back(columns);
    1189                 : 
    1190             111 :             cout.flush();
    1191                 : 
    1192               0 :         } catch (AssertionError & e) {
    1193               0 :             printMsg(lvlTalkative, format("skipping derivation named `%1%' which gives an assertion failure") % i->name);
    1194                 :         }
    1195                 :     }
    1196                 : 
    1197              82 :     if (!xmlOutput) printTable(table);
    1198              81 : }
    1199                 : 
    1200                 : 
    1201                 : static void opSwitchProfile(Globals & globals,
    1202               0 :     Strings args, Strings opFlags, Strings opArgs)
    1203                 : {
    1204               0 :     if (opFlags.size() > 0)
    1205               0 :         throw UsageError(format("unknown flag `%1%'") % opFlags.front());
    1206               0 :     if (opArgs.size() != 1)
    1207               0 :         throw UsageError(format("exactly one argument expected"));
    1208                 : 
    1209               0 :     Path profile = absPath(opArgs.front());
    1210               0 :     Path profileLink = getHomeDir() + "/.nix-profile";
    1211                 : 
    1212               0 :     switchLink(profileLink, profile);
    1213               0 : }
    1214                 : 
    1215                 : 
    1216                 : static const int prevGen = -2;
    1217                 : 
    1218                 : 
    1219               6 : static void switchGeneration(Globals & globals, int dstGen)
    1220                 : {
    1221               6 :     PathLocks lock;
    1222               6 :     lockProfile(lock, globals.profile);
    1223                 :     
    1224                 :     int curGen;
    1225               6 :     Generations gens = findGenerations(globals.profile, curGen);
    1226                 : 
    1227               6 :     Generation dst;
    1228              36 :     for (Generations::iterator i = gens.begin(); i != gens.end(); ++i)
    1229              30 :         if ((dstGen == prevGen && i->number < curGen) ||
    1230                 :             (dstGen >= 0 && i->number == dstGen))
    1231              21 :             dst = *i;
    1232                 : 
    1233               6 :     if (!dst)
    1234               0 :         if (dstGen == prevGen)
    1235                 :             throw Error(format("no generation older than the current (%1%) exists")
    1236               0 :                 % curGen);
    1237                 :         else
    1238               0 :             throw Error(format("generation %1% does not exist") % dstGen);
    1239                 : 
    1240               6 :     printMsg(lvlInfo, format("switching from generation %1% to %2%")
    1241                 :         % curGen % dst.number);
    1242                 :     
    1243               6 :     if (globals.dryRun) return;
    1244                 :     
    1245               6 :     switchLink(globals.profile, dst.path);
    1246                 : }
    1247                 : 
    1248                 : 
    1249                 : static void opSwitchGeneration(Globals & globals,
    1250               0 :     Strings args, Strings opFlags, Strings opArgs)
    1251                 : {
    1252               0 :     if (opFlags.size() > 0)
    1253               0 :         throw UsageError(format("unknown flag `%1%'") % opFlags.front());
    1254               0 :     if (opArgs.size() != 1)
    1255               0 :         throw UsageError(format("exactly one argument expected"));
    1256                 : 
    1257                 :     int dstGen;
    1258               0 :     if (!string2Int(opArgs.front(), dstGen))
    1259               0 :         throw UsageError(format("expected a generation number"));
    1260                 : 
    1261               0 :     switchGeneration(globals, dstGen);
    1262               0 : }
    1263                 : 
    1264                 : 
    1265                 : static void opRollback(Globals & globals,
    1266               6 :     Strings args, Strings opFlags, Strings opArgs)
    1267                 : {
    1268               6 :     if (opFlags.size() > 0)
    1269               0 :         throw UsageError(format("unknown flag `%1%'") % opFlags.front());
    1270               6 :     if (opArgs.size() != 0)
    1271               0 :         throw UsageError(format("no arguments expected"));
    1272                 : 
    1273               6 :     switchGeneration(globals, prevGen);
    1274               6 : }
    1275                 : 
    1276                 : 
    1277                 : static void opListGenerations(Globals & globals,
    1278               6 :     Strings args, Strings opFlags, Strings opArgs)
    1279                 : {
    1280               6 :     if (opFlags.size() > 0)
    1281               0 :         throw UsageError(format("unknown flag `%1%'") % opFlags.front());
    1282               6 :     if (opArgs.size() != 0)
    1283               0 :         throw UsageError(format("no arguments expected"));
    1284                 : 
    1285               6 :     PathLocks lock;
    1286               6 :     lockProfile(lock, globals.profile);
    1287                 :     
    1288                 :     int curGen;
    1289               6 :     Generations gens = findGenerations(globals.profile, curGen);
    1290                 : 
    1291              36 :     for (Generations::iterator i = gens.begin(); i != gens.end(); ++i) {
    1292                 :         tm t;
    1293              30 :         if (!localtime_r(&i->creationTime, &t)) throw Error("cannot convert time");
    1294                 :         cout << format("%|4|   %|4|-%|02|-%|02| %|02|:%|02|:%|02|   %||\n")
    1295                 :             % i->number
    1296                 :             % (t.tm_year + 1900) % (t.tm_mon + 1) % t.tm_mday
    1297                 :             % t.tm_hour % t.tm_min % t.tm_sec
    1298              30 :             % (i->number == curGen ? "(current)" : "");
    1299               6 :     }
    1300               6 : }
    1301                 : 
    1302                 : 
    1303              22 : static void deleteGeneration2(const Path & profile, unsigned int gen)
    1304                 : {
    1305              22 :     printMsg(lvlInfo, format("removing generation %1%") % gen);
    1306              22 :     deleteGeneration(profile, gen);
    1307              22 : }
    1308                 : 
    1309                 : 
    1310                 : static void opDeleteGenerations(Globals & globals,
    1311               4 :     Strings args, Strings opFlags, Strings opArgs)
    1312                 : {
    1313               4 :     if (opFlags.size() > 0)
    1314               0 :         throw UsageError(format("unknown flag `%1%'") % opFlags.front());
    1315                 : 
    1316               4 :     PathLocks lock;
    1317               4 :     lockProfile(lock, globals.profile);
    1318                 :     
    1319                 :     int curGen;
    1320               4 :     Generations gens = findGenerations(globals.profile, curGen);
    1321                 : 
    1322               8 :     for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); ++i) {
    1323                 : 
    1324               4 :         if (*i == "old") {
    1325              30 :             for (Generations::iterator j = gens.begin(); j != gens.end(); ++j)
    1326              26 :                 if (j->number != curGen)
    1327              22 :                     deleteGeneration2(globals.profile, j->number);
    1328                 :         }
    1329                 : 
    1330                 :         else {
    1331                 :             int n;
    1332               0 :             if (!string2Int(*i, n) || n < 0)
    1333               0 :                 throw UsageError(format("invalid generation specifier `%1%'")  % *i);
    1334               0 :             bool found = false;
    1335               0 :             for (Generations::iterator j = gens.begin(); j != gens.end(); ++j) {
    1336               0 :                 if (j->number == n) {
    1337               0 :                     deleteGeneration2(globals.profile, j->number);
    1338               0 :                     found = true;
    1339               0 :                     break;
    1340                 :                 }
    1341                 :             }
    1342               0 :             if (!found)
    1343               0 :                 printMsg(lvlError, format("generation %1% does not exist") % n);
    1344                 :         }
    1345               4 :     }
    1346               4 : }
    1347                 : 
    1348                 : 
    1349             145 : void run(Strings args)
    1350                 : {
    1351             145 :     Strings opFlags, opArgs, remaining;
    1352             145 :     Operation op = 0;
    1353                 :     
    1354             145 :     Globals globals;
    1355                 :     
    1356             145 :     globals.instSource.type = srcUnknown;
    1357             145 :     globals.instSource.nixExprPath = getDefNixExprPath();
    1358             145 :     globals.instSource.systemFilter = thisSystem;
    1359                 :     
    1360             145 :     globals.dryRun = false;
    1361             145 :     globals.preserveInstalled = false;
    1362                 : 
    1363                 :     globals.keepDerivations =
    1364             145 :         queryBoolSetting("env-keep-derivations", false);
    1365                 :     
    1366             778 :     for (Strings::iterator i = args.begin(); i != args.end(); ) {
    1367             488 :         string arg = *i++;
    1368                 : 
    1369             488 :         Operation oldOp = op;
    1370                 : 
    1371             488 :         if (arg == "--install" || arg == "-i")
    1372              27 :             op = opInstall;
    1373             461 :         else if (parseOptionArg(arg, i, args.end(),
    1374                 :                      globals.state, globals.instSource.autoArgs))
    1375                 :             ;
    1376             461 :         else if (arg == "--force-name") // undocumented flag for nix-install-package
    1377               2 :             globals.forceName = needArg(i, args, arg);
    1378             459 :         else if (arg == "--uninstall" || arg == "-e")
    1379              16 :             op = opUninstall;
    1380             443 :         else if (arg == "--upgrade" || arg == "-u")
    1381               3 :             op = opUpgrade;
    1382             440 :         else if (arg == "--set-flag")
    1383               0 :             op = opSetFlag;
    1384             440 :         else if (arg == "--set")
    1385               0 :             op = opSet;
    1386             440 :         else if (arg == "--query" || arg == "-q")
    1387              82 :             op = opQuery;
    1388             358 :         else if (arg == "--profile" || arg == "-p")
    1389             143 :             globals.profile = absPath(needArg(i, args, arg));
    1390             215 :         else if (arg == "--file" || arg == "-f")
    1391              40 :             globals.instSource.nixExprPath = absPath(needArg(i, args, arg));
    1392             175 :         else if (arg == "--switch-profile" || arg == "-S")
    1393               0 :             op = opSwitchProfile;
    1394             175 :         else if (arg == "--switch-generation" || arg == "-G")
    1395               0 :             op = opSwitchGeneration;
    1396             175 :         else if (arg == "--rollback")
    1397               6 :             op = opRollback;
    1398             169 :         else if (arg == "--list-generations")
    1399               6 :             op = opListGenerations;
    1400             163 :         else if (arg == "--delete-generations")
    1401               4 :             op = opDeleteGenerations;
    1402             159 :         else if (arg == "--dry-run") {
    1403               0 :             printMsg(lvlInfo, "(dry run; not doing anything)");
    1404               0 :             globals.dryRun = true;
    1405                 :         }
    1406             159 :         else if (arg == "--system-filter")
    1407               0 :             globals.instSource.systemFilter = needArg(i, args, arg);
    1408                 :         else {
    1409             159 :             remaining.push_back(arg);
    1410             159 :             if (arg[0] == '-') {
    1411              25 :                 opFlags.push_back(arg);
    1412              25 :                 if (arg == "--from-profile") { /* !!! hack */
    1413               0 :                     if (i != args.end()) opFlags.push_back(*i++);
    1414                 :                 }
    1415             134 :             } else opArgs.push_back(arg);
    1416                 :         }
    1417                 : 
    1418             488 :         if (oldOp && oldOp != op)
    1419               0 :             throw UsageError("only one operation may be specified");
    1420                 :     }
    1421                 : 
    1422             145 :     if (!op) throw UsageError("no operation specified");
    1423                 : 
    1424             144 :     if (globals.profile == "") {
    1425               1 :         Path profileLink = getHomeDir() + "/.nix-profile";
    1426                 :         globals.profile = pathExists(profileLink)
    1427                 :             ? absPath(readLink(profileLink), dirOf(profileLink))
    1428               1 :             : canonPath(nixStateDir + "/profiles/default");
    1429                 :     }
    1430                 :     
    1431             144 :     store = openStore();
    1432                 : 
    1433             144 :     op(globals, remaining, opFlags, opArgs);
    1434                 : 
    1435             145 :     printEvalStats(globals.state);
    1436             140 : }
    1437                 : 
    1438               0 : 
    1439             588 : string programId = "nix-env";

Generated by: LTP GCOV extension version 1.6