about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2003-11-19T17·27+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2003-11-19T17·27+0000
commit9898746ef3732979bf30e9048021b6232ddf15ac (patch)
tree77f1391387f0e28f5495104fed3e4227bbeb4af3
parentfd7ac09f1073179d9ac439c3e9fb12a1bf00a7d5 (diff)
* nix-env: a tool to manage user environments.
* Replace all directory reading code by a generic readDirectory()
  function.

-rw-r--r--configure.ac1
-rwxr-xr-xcorepkgs/buildenv/builder.pl72
-rw-r--r--corepkgs/buildenv/default.nix9
-rw-r--r--src/Makefile.am2
-rw-r--r--src/libexpr/parser.cc2
-rw-r--r--src/libmain/shared.cc1
-rw-r--r--src/libstore/db.cc4
-rw-r--r--src/libstore/globals.cc1
-rw-r--r--src/libstore/globals.hh3
-rw-r--r--src/libstore/references.cc12
-rw-r--r--src/libutil/archive.cc21
-rw-r--r--src/libutil/util.cc45
-rw-r--r--src/libutil/util.hh4
-rw-r--r--src/nix-env/Makefile.am11
-rw-r--r--src/nix-env/main.cc270
-rw-r--r--src/nix-store/main.cc5
16 files changed, 412 insertions, 51 deletions
diff --git a/configure.ac b/configure.ac
index 54a251b2363f..bc12b9a1060b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -36,6 +36,7 @@ AC_CONFIG_FILES([Makefile
    src/nix-hash/Makefile
    src/libexpr/Makefile
    src/nix-instantiate/Makefile
+   src/nix-env/Makefile
    scripts/Makefile
    corepkgs/Makefile
    corepkgs/fetchurl/Makefile
diff --git a/corepkgs/buildenv/builder.pl b/corepkgs/buildenv/builder.pl
new file mode 100755
index 000000000000..144ad3374b83
--- /dev/null
+++ b/corepkgs/buildenv/builder.pl
@@ -0,0 +1,72 @@
+#! /usr/bin/perl -w
+
+use strict;
+use Cwd;
+
+my $selfdir = $ENV{"out"};
+mkdir "$selfdir", 0755 || die "error creating $selfdir";
+
+# For each activated package, create symlinks.
+
+sub createLinks {
+    my $srcdir = shift;
+    my $dstdir = shift;
+
+    my @srcfiles = glob("$srcdir/*");
+
+    foreach my $srcfile (@srcfiles) {
+        my $basename = $srcfile;
+        $basename =~ s/^.*\///g; # strip directory
+        my $dstfile = "$dstdir/$basename";
+	if ($srcfile =~ /\/envpkgs$/) {
+	} elsif (-d $srcfile) {
+            # !!! hack for resolving name clashes
+            if (!-e $dstfile) {
+                mkdir $dstfile, 0755 || 
+                    die "error creating directory $dstfile";
+            }
+            -d $dstfile or die "$dstfile is not a directory";
+            createLinks($srcfile, $dstfile);
+        } elsif (-l $dstfile) {
+            my $target = readlink($dstfile);
+            die "collission between $srcfile and $target";
+        } else {
+#            print "linking $dstfile to $srcfile\n";
+            symlink($srcfile, $dstfile) ||
+                die "error creating link $dstfile";
+        }
+    }
+}
+
+my %done;
+
+sub addPkg {
+    my $pkgdir = shift;
+
+    return if (defined $done{$pkgdir});
+    $done{$pkgdir} = 1;
+
+#    print "merging $pkgdir\n";
+
+    createLinks("$pkgdir", "$selfdir");
+
+#    if (-f "$pkgdir/envpkgs") {
+#	my $envpkgs = `cat $pkgdir/envpkgs`;
+#	chomp $envpkgs;
+#	my @envpkgs = split / +/, $envpkgs;
+#	foreach my $envpkg (@envpkgs) {
+#	    addPkg($envpkg);
+#	}
+#    }
+}
+
+my @args = split ' ', $ENV{"derivations"};
+
+while (scalar @args > 0) {
+    my $drvpath = shift @args;
+    print "adding $drvpath\n";
+    addPkg($drvpath);
+}
+
+symlink($ENV{"manifest"}, "$selfdir/manifest") or die "cannot create manifest";
+
diff --git a/corepkgs/buildenv/default.nix b/corepkgs/buildenv/default.nix
new file mode 100644
index 000000000000..9bc704d8d511
--- /dev/null
+++ b/corepkgs/buildenv/default.nix
@@ -0,0 +1,9 @@
+{system, derivations, manifest}:
+
+derivation { 
+  name = "user-environment";
+  system = system;
+  builder = ./builder.pl;
+  derivations = derivations;
+  manifest = manifest;
+}
diff --git a/src/Makefile.am b/src/Makefile.am
index fe8cbf1e3283..0f1273426e32 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,2 +1,2 @@
 SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \
- libexpr nix-instantiate
+ libexpr nix-instantiate nix-env
diff --git a/src/libexpr/parser.cc b/src/libexpr/parser.cc
index 22d76c263ac6..e3c863a5580d 100644
--- a/src/libexpr/parser.cc
+++ b/src/libexpr/parser.cc
@@ -68,6 +68,8 @@ struct Cleanup : TermFun
 
 Expr parseExprFromFile(Path path)
 {
+    assert(path[0] == '/');
+
 #if 0
     /* Perhaps this is already an imploded parse tree? */
     Expr e = ATreadFromNamedFile(path.c_str());
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index b06f5eb8b465..632794db3d8d 100644
--- a/src/libmain/shared.cc
+++ b/src/libmain/shared.cc
@@ -19,6 +19,7 @@ static void initAndRun(int argc, char * * argv)
     nixStore = NIX_STORE_DIR;
     nixDataDir = NIX_DATA_DIR;
     nixLogDir = NIX_LOG_DIR;
+    nixStateDir = (string) NIX_STATE_DIR;
     nixDBPath = (string) NIX_STATE_DIR + "/db";
 
     /* Put the arguments in a vector. */
diff --git a/src/libstore/db.cc b/src/libstore/db.cc
index 63ec2724fcb3..a97111a3a96d 100644
--- a/src/libstore/db.cc
+++ b/src/libstore/db.cc
@@ -166,7 +166,7 @@ void Database::open(const string & path)
         /* The following code provides automatic recovery of the
            database environment.  Recovery is necessary when a process
            dies while it has the database open.  To detect this,
-           processes atomically increment a counter when the open the
+           processes atomically increment a counter when they open the
            database, and decrement it when they close it.  If we see
            that counter is > 0 but no processes are accessing the
            database---determined by attempting to obtain a write lock
@@ -199,7 +199,7 @@ void Database::open(const string & path)
                other readers or writers. */
 
             int n = getAccessorCount(fdAccessors);
-                setAccessorCount(fdAccessors, 1);
+            setAccessorCount(fdAccessors, 1);
 
             if (n != 0) {
                 printMsg(lvlTalkative,
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index a292b49aeae0..e5d76ff48563 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -3,6 +3,7 @@
 string nixStore = "/UNINIT";
 string nixDataDir = "/UNINIT";
 string nixLogDir = "/UNINIT";
+string nixStateDir = "/UNINIT";
 string nixDBPath = "/UNINIT";
 
 bool keepFailed = false;
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 1b4d0bde3ffe..3da294cc829a 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -16,6 +16,9 @@ extern string nixDataDir; /* !!! fix */
 /* nixLogDir is the directory where we log various operations. */ 
 extern string nixLogDir;
 
+/* nixStateDir is the directory where state is stored. */
+extern string nixStateDir;
+
 /* nixDBPath is the path name of our Berkeley DB environment. */
 extern string nixDBPath;
 
diff --git a/src/libstore/references.cc b/src/libstore/references.cc
index ab743f76d2d3..2bea44131ec0 100644
--- a/src/libstore/references.cc
+++ b/src/libstore/references.cc
@@ -36,14 +36,10 @@ void checkPath(const string & path,
         throw SysError(format("getting attributes of path `%1%'") % path);
 
     if (S_ISDIR(st.st_mode)) {
-        AutoCloseDir dir = opendir(path.c_str());
-
-        struct dirent * dirent;
-        while (errno = 0, dirent = readdir(dir)) {
-            string name = dirent->d_name;
-            if (name == "." || name == "..") continue;
-            search(name, ids, seen);
-            checkPath(path + "/" + name, ids, seen);
+        Strings names = readDirectory(path);
+	for (Strings::iterator i = names.begin(); i != names.end(); i++) {
+            search(*i, ids, seen);
+            checkPath(path + "/" + *i, ids, seen);
         }
     }
 
diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc
index ed57df4c9f7b..f605e8b61954 100644
--- a/src/libutil/archive.cc
+++ b/src/libutil/archive.cc
@@ -51,23 +51,12 @@ static void dump(const string & path, DumpSink & sink);
 
 static void dumpEntries(const Path & path, DumpSink & sink)
 {
-    AutoCloseDir dir = opendir(path.c_str());
-    if (!dir) throw SysError("opening directory " + path);
+    Strings names = readDirectory(path);
+    vector<string> names2(names.begin(), names.end());
+    sort(names2.begin(), names2.end());
 
-    vector<string> names;
-
-    struct dirent * dirent;
-    while (errno = 0, dirent = readdir(dir)) {
-        string name = dirent->d_name;
-        if (name == "." || name == "..") continue;
-        names.push_back(name);
-    }
-    if (errno) throw SysError("reading directory " + path);
-
-    sort(names.begin(), names.end());
-
-    for (vector<string>::iterator it = names.begin();
-         it != names.end(); it++)
+    for (vector<string>::iterator it = names2.begin();
+         it != names2.end(); it++)
     {
         writeString("entry", sink);
         writeString("(", sink);
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index 299a37f6c877..6a032a3f10f3 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -108,6 +108,25 @@ bool pathExists(const Path & path)
 }
 
 
+Strings readDirectory(const Path & path)
+{
+    Strings names;
+
+    AutoCloseDir dir = opendir(path.c_str());
+    if (!dir) throw SysError(format("opening directory `%1%'") % path);
+
+    struct dirent * dirent;
+    while (errno = 0, dirent = readdir(dir)) { /* sic */
+        string name = dirent->d_name;
+        if (name == "." || name == "..") continue;
+        names.push_back(name);
+    }
+    if (errno) throw SysError(format("reading directory `%1%'") % path);
+
+    return names;
+}
+
+
 void deletePath(const Path & path)
 {
     printMsg(lvlVomit, format("deleting path `%1%'") % path);
@@ -117,18 +136,7 @@ void deletePath(const Path & path)
 	throw SysError(format("getting attributes of path `%1%'") % path);
 
     if (S_ISDIR(st.st_mode)) {
-	Strings names;
-        
-        {
-            AutoCloseDir dir = opendir(path.c_str());
-
-            struct dirent * dirent;
-            while (errno = 0, dirent = readdir(dir)) {
-                string name = dirent->d_name;
-                if (name == "." || name == "..") continue;
-                names.push_back(name);
-            }
-        } /* scoped to ensure that dir is closed at this point */
+	Strings names = readDirectory(path);
 
 	/* Make the directory writable. */
 	if (!(st.st_mode & S_IWUSR)) {
@@ -136,7 +144,7 @@ void deletePath(const Path & path)
 		throw SysError(format("making `%1%' writable"));
 	}
 
-	for (Strings::iterator i = names.begin(); i != names.end(); i++)
+	for (Strings::iterator i = names.begin(); i != names.end(); ++i)
             deletePath(path + "/" + *i);
     }
 
@@ -157,14 +165,9 @@ void makePathReadOnly(const Path & path)
     }
 
     if (S_ISDIR(st.st_mode)) {
-        AutoCloseDir dir = opendir(path.c_str());
-
-        struct dirent * dirent;
-        while (errno = 0, dirent = readdir(dir)) {
-            string name = dirent->d_name;
-            if (name == "." || name == "..") continue;
-	    makePathReadOnly(path + "/" + name);
-        }
+        Strings names = readDirectory(path);
+	for (Strings::iterator i = names.begin(); i != names.end(); ++i)
+	    makePathReadOnly(path + "/" + *i);
     }
 }
 
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index e6b600eff8ce..d0e7b3ada873 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 of a directory.  The entries `.' and `..' are
+   removed. */
+Strings readDirectory(const Path & path);
+
 /* Delete a path; i.e., in the case of a directory, it is deleted
    recursively.  Don't use this at home, kids. */
 void deletePath(const Path & path);
diff --git a/src/nix-env/Makefile.am b/src/nix-env/Makefile.am
new file mode 100644
index 000000000000..add54581b051
--- /dev/null
+++ b/src/nix-env/Makefile.am
@@ -0,0 +1,11 @@
+bin_PROGRAMS = nix-env
+
+nix_env_SOURCES = main.cc
+nix_env_LDADD = ../libmain/libmain.a ../libexpr/libexpr.a \
+ ../libstore/libstore.a ../libutil/libutil.a \
+ ../boost/format/libformat.a -L../../externals/inst/lib -ldb_cxx \
+ -lsglr -lATB -lconversion -lasfix2 -lmept -lATerm
+
+AM_CXXFLAGS = \
+ -I.. -I../../externals/inst/include -I../libutil -I../libstore \
+ -I../libexpr -I../libmain
diff --git a/src/nix-env/main.cc b/src/nix-env/main.cc
new file mode 100644
index 000000000000..4296ce5090c4
--- /dev/null
+++ b/src/nix-env/main.cc
@@ -0,0 +1,270 @@
+#include "globals.hh"
+#include "normalise.hh"
+#include "shared.hh"
+#include "parser.hh"
+#include "eval.hh"
+
+
+typedef void (* Operation) (EvalState & state,
+    Strings opFlags, Strings opArgs);
+
+
+struct DrvInfo
+{
+    string name;
+    Path drvPath;
+    Path outPath;
+};
+
+typedef map<string, DrvInfo> DrvInfos;
+
+
+bool parseDerivation(EvalState & state, Expr e, DrvInfo & drv)
+{
+    ATMatcher m;
+    
+    e = evalExpr(state, e);
+    if (!(atMatch(m, e) >> "Attrs")) return false;
+    Expr a = queryAttr(e, "type");
+    if (!a || evalString(state, a) != "derivation") return false;
+
+    a = queryAttr(e, "name");
+    if (!a) throw badTerm("derivation name missing", e);
+    drv.name = evalString(state, a);
+
+    a = queryAttr(e, "drvPath");
+    if (!a) throw badTerm("derivation path missing", e);
+    drv.drvPath = evalPath(state, a);
+
+    a = queryAttr(e, "outPath");
+    if (!a) throw badTerm("output path missing", e);
+    drv.outPath = evalPath(state, a);
+
+    return true;
+}
+
+
+bool parseDerivations(EvalState & state, Expr e, DrvInfos & drvs)
+{
+    e = evalExpr(state, e);
+    
+    ATermMap drvMap;
+    queryAllAttrs(e, drvMap);
+
+    for (ATermIterator i(drvMap.keys()); i; ++i) {
+        DrvInfo drv;
+        debug(format("evaluating attribute `%1%'") % *i);
+        if (parseDerivation(state, drvMap.get(*i), drv))
+            drvs[drv.name] = drv;
+    }
+
+    return true;
+}
+
+
+void loadDerivations(EvalState & state, Path nePath, DrvInfos & drvs)
+{
+    Expr e = parseExprFromFile(absPath(nePath));
+    if (!parseDerivations(state, e, drvs))
+        throw badTerm("expected set of derivations", e);
+}
+
+
+static Path getLinksDir()
+{
+    return canonPath(nixStateDir + "/links");
+}
+
+
+Path createLink(Path outPath, Path drvPath)
+{
+    Path linksDir = getLinksDir();
+
+    unsigned int num = 0;
+    
+    Strings names = readDirectory(linksDir);
+    for (Strings::iterator i = names.begin(); i != names.end(); ++i) {
+        istringstream s(*i);
+        unsigned int n; 
+        if (s >> n && s.eof() && n > num) num = n + 1;
+    }
+
+    Path linkPath = (format("%1%/%2%") % linksDir % num).str();
+
+    if (symlink(outPath.c_str(), linkPath.c_str()) != 0)
+        throw SysError(format("creating symlink `%1%'") % linkPath);
+
+    return linkPath;
+}
+
+
+void installDerivations(EvalState & state,
+    Path nePath, Strings drvNames)
+{
+    debug(format("installing derivations from `%1%'") % nePath);
+
+    /* Fetch all derivations from the input file. */
+    DrvInfos availDrvs;
+    loadDerivations(state, nePath, availDrvs);
+
+    /* Filter out the ones we're not interested in. */
+    DrvInfos selectedDrvs;
+    for (Strings::iterator i = drvNames.begin();
+         i != drvNames.end(); ++i)
+    {
+        DrvInfos::iterator j = availDrvs.find(*i);
+        if (j == availDrvs.end())
+            throw Error(format("unknown derivation `%1%'") % *i);
+        else
+            selectedDrvs[j->first] = j->second;
+    }
+
+    /* Get the environment builder expression. */
+    Expr envBuilder = parseExprFromFile("/home/eelco/nix/corepkgs/buildenv"); /* !!! */
+
+    /* Construct the whole top level derivation. */
+    ATermList inputs = ATempty;
+    for (DrvInfos::iterator i = selectedDrvs.begin();
+         i != selectedDrvs.end(); ++i)
+    {
+        ATerm t = ATmake(
+            "Attrs(["
+            "Bind(\"type\", Str(\"derivation\")), "
+            "Bind(\"name\", Str(<str>)), "
+            "Bind(\"drvPath\", Path(<str>)), "
+            "Bind(\"outPath\", Path(<str>))"
+            "])",
+            i->second.name.c_str(),
+            i->second.drvPath.c_str(),
+            i->second.outPath.c_str());
+        inputs = ATinsert(inputs, t);
+    }
+
+    ATerm inputs2 = ATmake("List(<term>)", ATreverse(inputs));
+
+    /* Also write a copy of the list of inputs to the store; we need
+       it for future modifications of the environment. */
+    Path inputsFile = writeTerm(inputs2, "-env-inputs");
+
+    Expr topLevel = ATmake(
+        "Call(<term>, Attrs(["
+        "Bind(\"system\", Str(<str>)), "
+        "Bind(\"derivations\", <term>), " // !!! redundant
+        "Bind(\"manifest\", Path(<str>))"
+        "]))",
+        envBuilder, thisSystem.c_str(), inputs2, inputsFile.c_str());
+
+    /* Instantiate it. */
+    debug(format("evaluating builder expression `%1%'") % topLevel);
+    DrvInfo topLevelDrv;
+    if (!parseDerivation(state, topLevel, topLevelDrv))
+        abort();
+    
+    /* Realise the resulting store expression. */
+    debug(format("realising user environment"));
+    Path nfPath = normaliseStoreExpr(topLevelDrv.drvPath);
+    realiseClosure(nfPath);
+
+    /* Switch the current user environment to the output path. */
+    debug(format("switching to new user environment"));
+    Path linkPath = createLink(topLevelDrv.outPath, topLevelDrv.drvPath);
+//     switchLink(current"), link);
+}
+
+
+static void opInstall(EvalState & state,
+    Strings opFlags, Strings opArgs)
+{
+    if (opArgs.size() < 1) throw UsageError("Nix expression expected");
+
+    Path nePath = opArgs.front();
+    opArgs.pop_front();
+
+    installDerivations(state, nePath,
+        Strings(opArgs.begin(), opArgs.end()));
+}
+
+
+static void opQuery(EvalState & state,
+    Strings opFlags, Strings opArgs)
+{
+    enum { qName } query = qName;
+    enum { sInstalled, sAvailable } source = sInstalled;
+
+    for (Strings::iterator i = opFlags.begin();
+         i != opFlags.end(); ++i)
+        if (*i == "--name") query = qName;
+        else if (*i == "--installed") source = sInstalled;
+        else if (*i == "--available" || *i == "-f") source = sAvailable;
+        else throw UsageError(format("unknown flag `%1%'") % *i);
+
+    /* Obtain derivation information from the specified source. */
+    DrvInfos drvs;
+
+    switch (source) {
+
+        case sInstalled:
+            break;
+
+        case sAvailable: {
+            Path nePath = opArgs.front();
+            opArgs.pop_front();
+            loadDerivations(state, nePath, drvs);
+            break;
+        }
+
+        default: abort();
+    }
+
+    /* Perform the specified query on the derivations. */
+    switch (query) {
+
+        case qName: {
+            if (opArgs.size() != 0) throw UsageError("no arguments expected");
+            for (DrvInfos::iterator i = drvs.begin(); i != drvs.end(); ++i)
+                cout << format("%1%\n") % i->second.name;
+            break;
+        }
+        
+        default: abort();
+    }
+}
+
+
+void run(Strings args)
+{
+    EvalState state;
+    Strings opFlags, opArgs;
+    Operation op = 0;
+
+    for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
+        string arg = *i;
+
+        Operation oldOp = op;
+
+        if (arg == "--install" || arg == "-i")
+            op = opInstall;
+        if (arg == "--query" || arg == "-q")
+            op = opQuery;
+        else if (arg == "--verbose" || arg == "-v")
+            verbosity = (Verbosity) ((int) verbosity + 1);
+        else if (arg[0] == '-')
+            opFlags.push_back(arg);
+        else
+            opArgs.push_back(arg);
+
+        if (oldOp && oldOp != op)
+            throw UsageError("only one operation may be specified");
+    }
+
+    if (!op) throw UsageError("no operation specified");
+
+    openDB();
+
+    op(state, opFlags, opArgs);
+
+    printEvalStats(state);
+}
+
+
+string programId = "nix-env";
diff --git a/src/nix-store/main.cc b/src/nix-store/main.cc
index 0d87db9dfbb9..c73de52896fb 100644
--- a/src/nix-store/main.cc
+++ b/src/nix-store/main.cc
@@ -251,9 +251,8 @@ void run(Strings args)
     Strings opFlags, opArgs;
     Operation op = 0;
 
-    for (Strings::iterator it = args.begin(); it != args.end(); )
-    {
-        string arg = *it++;
+    for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
+        string arg = *i;
 
         Operation oldOp = op;