* nix-env: a tool to manage user environments.

* Replace all directory reading code by a generic readDirectory()
  function.
This commit is contained in:
Eelco Dolstra 2003-11-19 17:27:16 +00:00
parent fd7ac09f10
commit 9898746ef3
16 changed files with 412 additions and 51 deletions

View file

@ -36,6 +36,7 @@ AC_CONFIG_FILES([Makefile
src/nix-hash/Makefile src/nix-hash/Makefile
src/libexpr/Makefile src/libexpr/Makefile
src/nix-instantiate/Makefile src/nix-instantiate/Makefile
src/nix-env/Makefile
scripts/Makefile scripts/Makefile
corepkgs/Makefile corepkgs/Makefile
corepkgs/fetchurl/Makefile corepkgs/fetchurl/Makefile

72
corepkgs/buildenv/builder.pl Executable file
View file

@ -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";

View file

@ -0,0 +1,9 @@
{system, derivations, manifest}:
derivation {
name = "user-environment";
system = system;
builder = ./builder.pl;
derivations = derivations;
manifest = manifest;
}

View file

@ -1,2 +1,2 @@
SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \ SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \
libexpr nix-instantiate libexpr nix-instantiate nix-env

View file

@ -68,6 +68,8 @@ struct Cleanup : TermFun
Expr parseExprFromFile(Path path) Expr parseExprFromFile(Path path)
{ {
assert(path[0] == '/');
#if 0 #if 0
/* Perhaps this is already an imploded parse tree? */ /* Perhaps this is already an imploded parse tree? */
Expr e = ATreadFromNamedFile(path.c_str()); Expr e = ATreadFromNamedFile(path.c_str());

View file

@ -19,6 +19,7 @@ static void initAndRun(int argc, char * * argv)
nixStore = NIX_STORE_DIR; nixStore = NIX_STORE_DIR;
nixDataDir = NIX_DATA_DIR; nixDataDir = NIX_DATA_DIR;
nixLogDir = NIX_LOG_DIR; nixLogDir = NIX_LOG_DIR;
nixStateDir = (string) NIX_STATE_DIR;
nixDBPath = (string) NIX_STATE_DIR + "/db"; nixDBPath = (string) NIX_STATE_DIR + "/db";
/* Put the arguments in a vector. */ /* Put the arguments in a vector. */

View file

@ -166,7 +166,7 @@ void Database::open(const string & path)
/* The following code provides automatic recovery of the /* The following code provides automatic recovery of the
database environment. Recovery is necessary when a process database environment. Recovery is necessary when a process
dies while it has the database open. To detect this, 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 database, and decrement it when they close it. If we see
that counter is > 0 but no processes are accessing the that counter is > 0 but no processes are accessing the
database---determined by attempting to obtain a write lock database---determined by attempting to obtain a write lock

View file

@ -3,6 +3,7 @@
string nixStore = "/UNINIT"; string nixStore = "/UNINIT";
string nixDataDir = "/UNINIT"; string nixDataDir = "/UNINIT";
string nixLogDir = "/UNINIT"; string nixLogDir = "/UNINIT";
string nixStateDir = "/UNINIT";
string nixDBPath = "/UNINIT"; string nixDBPath = "/UNINIT";
bool keepFailed = false; bool keepFailed = false;

View file

@ -16,6 +16,9 @@ extern string nixDataDir; /* !!! fix */
/* nixLogDir is the directory where we log various operations. */ /* nixLogDir is the directory where we log various operations. */
extern string nixLogDir; extern string nixLogDir;
/* nixStateDir is the directory where state is stored. */
extern string nixStateDir;
/* nixDBPath is the path name of our Berkeley DB environment. */ /* nixDBPath is the path name of our Berkeley DB environment. */
extern string nixDBPath; extern string nixDBPath;

View file

@ -36,14 +36,10 @@ void checkPath(const string & path,
throw SysError(format("getting attributes of path `%1%'") % path); throw SysError(format("getting attributes of path `%1%'") % path);
if (S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) {
AutoCloseDir dir = opendir(path.c_str()); Strings names = readDirectory(path);
for (Strings::iterator i = names.begin(); i != names.end(); i++) {
struct dirent * dirent; search(*i, ids, seen);
while (errno = 0, dirent = readdir(dir)) { checkPath(path + "/" + *i, ids, seen);
string name = dirent->d_name;
if (name == "." || name == "..") continue;
search(name, ids, seen);
checkPath(path + "/" + name, ids, seen);
} }
} }

View file

@ -51,23 +51,12 @@ static void dump(const string & path, DumpSink & sink);
static void dumpEntries(const Path & path, DumpSink & sink) static void dumpEntries(const Path & path, DumpSink & sink)
{ {
AutoCloseDir dir = opendir(path.c_str()); Strings names = readDirectory(path);
if (!dir) throw SysError("opening directory " + path); vector<string> names2(names.begin(), names.end());
sort(names2.begin(), names2.end());
vector<string> names; for (vector<string>::iterator it = names2.begin();
it != names2.end(); it++)
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++)
{ {
writeString("entry", sink); writeString("entry", sink);
writeString("(", sink); writeString("(", sink);

View file

@ -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) void deletePath(const Path & path)
{ {
printMsg(lvlVomit, format("deleting path `%1%'") % 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); throw SysError(format("getting attributes of path `%1%'") % path);
if (S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) {
Strings names; Strings names = readDirectory(path);
{
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 */
/* Make the directory writable. */ /* Make the directory writable. */
if (!(st.st_mode & S_IWUSR)) { if (!(st.st_mode & S_IWUSR)) {
@ -136,7 +144,7 @@ void deletePath(const Path & path)
throw SysError(format("making `%1%' writable")); 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); deletePath(path + "/" + *i);
} }
@ -157,14 +165,9 @@ void makePathReadOnly(const Path & path)
} }
if (S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) {
AutoCloseDir dir = opendir(path.c_str()); Strings names = readDirectory(path);
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
struct dirent * dirent; makePathReadOnly(path + "/" + *i);
while (errno = 0, dirent = readdir(dir)) {
string name = dirent->d_name;
if (name == "." || name == "..") continue;
makePathReadOnly(path + "/" + name);
}
} }
} }

View file

@ -73,6 +73,10 @@ string baseNameOf(const Path & path);
/* Return true iff the given path exists. */ /* Return true iff the given path exists. */
bool pathExists(const Path & path); 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 /* Delete a path; i.e., in the case of a directory, it is deleted
recursively. Don't use this at home, kids. */ recursively. Don't use this at home, kids. */
void deletePath(const Path & path); void deletePath(const Path & path);

11
src/nix-env/Makefile.am Normal file
View file

@ -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

270
src/nix-env/main.cc Normal file
View file

@ -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";

View file

@ -251,9 +251,8 @@ void run(Strings args)
Strings opFlags, opArgs; Strings opFlags, opArgs;
Operation op = 0; Operation op = 0;
for (Strings::iterator it = args.begin(); it != args.end(); ) for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
{ string arg = *i;
string arg = *it++;
Operation oldOp = op; Operation oldOp = op;