From 4a373a3e9ac07a2d4c43d495c0a44883106ecfde Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 5 Jan 2004 16:26:43 +0000 Subject: [PATCH] * Implemented Eelco V.'s `nix-env -I' command to specify the default path of the Nix expression to be used with the import, upgrade, and query commands. For instance, $ nix-env -I ~/nixpkgs/pkgs/system/i686-linux.nix $ nix-env --query --available [aka -qa] sylpheed-0.9.7 bison-1.875 pango-1.2.5 subversion-0.35.1 ... $ nix-env -i sylpheed $ nix-env -u subversion There can be only one default at a time. * If the path to a Nix expression is a symlink, follow the symlink prior to resolving relative path references in the expression. --- src/libexpr/parser.cc | 18 +++++----- src/libstore/references.cc | 8 ++--- src/libutil/archive.cc | 5 +-- src/libutil/util.cc | 14 ++++++++ src/libutil/util.hh | 4 +++ src/nix-env/help.txt | 10 +++--- src/nix-env/main.cc | 71 +++++++++++++++++++++++++++----------- 7 files changed, 87 insertions(+), 43 deletions(-) diff --git a/src/libexpr/parser.cc b/src/libexpr/parser.cc index aecfa4348..b9e79e13d 100644 --- a/src/libexpr/parser.cc +++ b/src/libexpr/parser.cc @@ -29,16 +29,12 @@ struct Cleanup : TermFun ATMatcher m; string s; - if (atMatch(m, e) >> "Str" >> s) { + if (atMatch(m, e) >> "Str" >> s) return ATmake("Str()", string(s, 1, s.size() - 2).c_str()); - } - if (atMatch(m, e) >> "Path" >> s) { - if (s[0] != '/') - s = basePath + "/" + s; - return ATmake("Path()", canonPath(s).c_str()); - } + if (atMatch(m, e) >> "Path" >> s) + return ATmake("Path()", absPath(s, basePath).c_str()); if (atMatch(m, e) >> "Int" >> s) { istringstream s2(s); @@ -147,8 +143,14 @@ Expr parseExprFromFile(Path path) if (e) return e; #endif - /* If `path' refers to a directory, append `/default.nix'. */ + /* If `path' is a symlink, follow it. This is so that relative + path references work. */ struct stat st; + if (lstat(path.c_str(), &st)) + throw SysError(format("getting status of `%1%'") % path); + if (S_ISLNK(st.st_mode)) path = absPath(readLink(path), dirOf(path)); + + /* If `path' refers to a directory, append `/default.nix'. */ if (stat(path.c_str(), &st)) throw SysError(format("getting status of `%1%'") % path); if (S_ISDIR(st.st_mode)) diff --git a/src/libstore/references.cc b/src/libstore/references.cc index 2bea44131..2daf4d4f4 100644 --- a/src/libstore/references.cc +++ b/src/libstore/references.cc @@ -59,12 +59,8 @@ void checkPath(const string & path, delete buf; /* !!! autodelete */ } - else if (S_ISLNK(st.st_mode)) { - char buf[st.st_size]; - if (readlink(path.c_str(), buf, st.st_size) != st.st_size) - throw SysError(format("reading symbolic link `%1%'") % path); - search(string(buf, st.st_size), ids, seen); - } + else if (S_ISLNK(st.st_mode)) + search(readLink(path), ids, seen); else throw Error(format("unknown file type: %1%") % path); } diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index f605e8b61..90a039164 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -122,11 +122,8 @@ static void dump(const Path & path, DumpSink & sink) else if (S_ISLNK(st.st_mode)) { writeString("type", sink); writeString("symlink", sink); - char buf[st.st_size]; - if (readlink(path.c_str(), buf, st.st_size) != st.st_size) - throw SysError("reading symbolic link " + path); writeString("target", sink); - writeString(string(buf, st.st_size), sink); + writeString(readLink(path), sink); } else throw Error("unknown file type: " + path); diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 60b86b162..28e276a32 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -109,6 +109,20 @@ bool pathExists(const Path & path) } +Path readLink(const Path & path) +{ + struct stat st; + if (lstat(path.c_str(), &st)) + throw SysError(format("getting status of `%1%'") % path); + if (!S_ISLNK(st.st_mode)) + throw Error(format("`%1%' is not a symlink") % path); + char buf[st.st_size]; + if (readlink(path.c_str(), buf, st.st_size) != st.st_size) + throw SysError(format("reading symbolic link `%1%'") % path); + return string(buf, st.st_size); +} + + Strings readDirectory(const Path & path) { Strings names; diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 4126381d9..5d27ac1bd 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -73,6 +73,10 @@ string baseNameOf(const Path & path); /* Return true iff the given path exists. */ bool pathExists(const Path & path); +/* Read the contents (target) of a symbolic link. The result is not + in any way canonicalised. */ +Path readLink(const Path & path); + /* Read the contents of a directory. The entries `.' and `..' are removed. */ Strings readDirectory(const Path & path); diff --git a/src/nix-env/help.txt b/src/nix-env/help.txt index 3f15e6a8e..823f5213a 100644 --- a/src/nix-env/help.txt +++ b/src/nix-env/help.txt @@ -4,15 +4,16 @@ nix-env [OPTIONS...] [ARGUMENTS...] Operations: - --install / -i FILE: add derivations to the user environment + --install / -i: add derivations to the user environment + --upgrade / -u: upgrade derivation in the user environment --uninstall / -e: remove derivations from the user environment - --upgrade / -u FILE: upgrade derivation in the user environment --query / -q: perform a query on an environment or Nix expression The previous operations take a list of derivation names. The special name `*' may be used to indicate all derivations. - --profile / -p [FILE]: switch to specified user environment + --profile / -p [FILE]: switch to specified user environment + --import / -I FILE: set default Nix expression --version: output version information --help: display help @@ -26,10 +27,11 @@ Query types: Query sources: --installed: use installed derivations (default) - --available / -f FILE: use derivations available in expression FILE + --available / -a: use derivations available in Nix expression Options: --link / -l LINK: use symlink LINK instead of (...)/current + --file / -f FILE: use Nix expression FILE for installation, etc. --verbose / -v: verbose operation (may be repeated) --keep-failed / -K: keep temporary directories of failed builds diff --git a/src/nix-env/main.cc b/src/nix-env/main.cc index 64ae6d412..f0877b058 100644 --- a/src/nix-env/main.cc +++ b/src/nix-env/main.cc @@ -11,6 +11,7 @@ struct Globals { Path linkPath; + Path nixExprPath; EvalState state; }; @@ -106,12 +107,26 @@ void loadDerivations(EvalState & state, Path nePath, DrvInfos & drvs) } +static Path getHomeDir() +{ + Path homeDir(getenv("HOME")); + if (homeDir == "") throw Error("HOME environment variable not set"); + return homeDir; +} + + static Path getLinksDir() { return canonPath(nixStateDir + "/links"); } +static Path getDefNixExprPath() +{ + return getHomeDir() + "/.nix-defexpr"; +} + + void queryInstalled(EvalState & state, DrvInfos & drvs, const Path & userEnv) { @@ -410,13 +425,11 @@ static void opInstall(Globals & globals, { if (opFlags.size() > 0) throw UsageError(format("unknown flags `%1%'") % opFlags.front()); - if (opArgs.size() < 1) throw UsageError("Nix file expected"); - Path nePath = opArgs.front(); - DrvNames drvNames = drvNamesFromArgs( - Strings(++opArgs.begin(), opArgs.end())); + DrvNames drvNames = drvNamesFromArgs(opArgs); - installDerivations(globals.state, nePath, drvNames, globals.linkPath); + installDerivations(globals.state, globals.nixExprPath, + drvNames, globals.linkPath); } @@ -492,11 +505,10 @@ static void opUpgrade(Globals & globals, throw UsageError(format("unknown flags `%1%'") % opFlags.front()); if (opArgs.size() < 1) throw UsageError("Nix file expected"); - Path nePath = opArgs.front(); - DrvNames drvNames = drvNamesFromArgs( - Strings(++opArgs.begin(), opArgs.end())); + DrvNames drvNames = drvNamesFromArgs(opArgs); - upgradeDerivations(globals.state, nePath, drvNames, globals.linkPath); + upgradeDerivations(globals.state, globals.nixExprPath, + drvNames, globals.linkPath); } @@ -547,7 +559,7 @@ static void opQuery(Globals & globals, else if (*i == "--expr" || *i == "-e") query = qDrvPath; else if (*i == "--status" || *i == "-s") query = qStatus; else if (*i == "--installed") source = sInstalled; - else if (*i == "--available" || *i == "-f") source = sAvailable; + else if (*i == "--available" || *i == "-a") source = sAvailable; else throw UsageError(format("unknown flag `%1%'") % *i); /* Obtain derivation information from the specified source. */ @@ -560,10 +572,7 @@ static void opQuery(Globals & globals, break; case sAvailable: { - if (opArgs.size() < 1) throw UsageError("Nix file expected"); - Path nePath = opArgs.front(); - opArgs.pop_front(); - loadDerivations(globals.state, nePath, drvs); + loadDerivations(globals.state, globals.nixExprPath, drvs); break; } @@ -611,20 +620,31 @@ static void opSwitchProfile(Globals & globals, if (opFlags.size() > 0) throw UsageError(format("unknown flags `%1%'") % opFlags.front()); if (opArgs.size() > 1) - throw UsageError(format("--profile takes at most one argument")); + throw UsageError(format("`--profile' takes at most one argument")); - string linkPath = + Path linkPath = opArgs.size() == 0 ? globals.linkPath : opArgs.front(); + Path linkPathFinal = getHomeDir() + "/.nix-userenv"; - string homeDir(getenv("HOME")); - if (homeDir == "") throw Error("HOME environment variable not set"); - - string linkPathFinal = homeDir + "/.nix-userenv"; - switchLink(linkPathFinal, linkPath); } +static void opDefaultExpr(Globals & globals, + Strings opFlags, Strings opArgs) +{ + if (opFlags.size() > 0) + throw UsageError(format("unknown flags `%1%'") % opFlags.front()); + if (opArgs.size() != 1) + throw UsageError(format("`--import' takes exactly one argument")); + + Path defNixExpr = opArgs.front(); + Path defNixExprLink = getDefNixExprPath(); + + switchLink(defNixExprLink, defNixExpr); +} + + void run(Strings args) { /* Use a higher default verbosity (lvlInfo). */ @@ -635,6 +655,7 @@ void run(Strings args) Globals globals; globals.linkPath = getLinksDir() + "/current"; + globals.nixExprPath = getDefNixExprPath(); for (Strings::iterator i = args.begin(); i != args.end(); ++i) { string arg = *i; @@ -649,12 +670,20 @@ void run(Strings args) op = opUpgrade; else if (arg == "--query" || arg == "-q") op = opQuery; + else if (arg == "--import" || arg == "-I") /* !!! bad name */ + op = opDefaultExpr; else if (arg == "--link" || arg == "-l") { ++i; if (i == args.end()) throw UsageError( format("`%1%' requires an argument") % arg); globals.linkPath = absPath(*i); } + else if (arg == "--file" || arg == "-f") { + ++i; + if (i == args.end()) throw UsageError( + format("`%1%' requires an argument") % arg); + globals.nixExprPath = absPath(*i); + } else if (arg == "--profile" || arg == "-p") op = opSwitchProfile; else if (arg[0] == '-')