* A command to run programs in Nix packages, that is, to execute a run
action. Run actions are described by uniquely hashed descriptors, just like build actions. Therefore run actions can have dependencies, but these need not be the same as the build time dependencies (e.g., at runtime we can link against a different version of a dynamic library). Example: nix run 31d6bf4c171282367065e0deecd7c579 will run the Pan 0.13.91 newsreader with gtkspell support.
This commit is contained in:
parent
800d8e950f
commit
20d165c344
1 changed files with 123 additions and 20 deletions
143
src/nix.cc
143
src/nix.cc
|
@ -166,7 +166,7 @@ string makeRef(string filename)
|
||||||
if (!pipe) throw BadRefError("cannot execute md5sum");
|
if (!pipe) throw BadRefError("cannot execute md5sum");
|
||||||
|
|
||||||
if (fread(hash, 32, 1, pipe) != 1)
|
if (fread(hash, 32, 1, pipe) != 1)
|
||||||
throw BadRefError("cannot read hash from md5sum");
|
throw BadRefError("cannot read hash from md5sum of " + filename);
|
||||||
hash[32] = 0;
|
hash[32] = 0;
|
||||||
|
|
||||||
pclose(pipe);
|
pclose(pipe);
|
||||||
|
@ -222,14 +222,14 @@ void readPkgDescr(const string & pkgfile,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string getPkg(string pkgref);
|
string getPkg(string hash);
|
||||||
|
|
||||||
|
|
||||||
typedef pair<string, string> EnvPair;
|
typedef pair<string, string> EnvPair;
|
||||||
typedef list<EnvPair> Environment;
|
typedef list<EnvPair> Environment;
|
||||||
|
|
||||||
|
|
||||||
void installPkg(string pkgref)
|
void installPkg(string hash)
|
||||||
{
|
{
|
||||||
string pkgfile;
|
string pkgfile;
|
||||||
string src;
|
string src;
|
||||||
|
@ -237,13 +237,13 @@ void installPkg(string pkgref)
|
||||||
string cmd;
|
string cmd;
|
||||||
string builder;
|
string builder;
|
||||||
|
|
||||||
if (!queryDB(dbRefs, pkgref, pkgfile))
|
if (!queryDB(dbRefs, hash, pkgfile))
|
||||||
throw Error("unknown package " + pkgref);
|
throw Error("unknown package " + hash);
|
||||||
|
|
||||||
cerr << "installing package " + pkgref + " from " + pkgfile + "\n";
|
cerr << "installing package " + hash + " from " + pkgfile + "\n";
|
||||||
|
|
||||||
/* Verify that the file hasn't changed. !!! race */
|
/* Verify that the file hasn't changed. !!! race */
|
||||||
if (makeRef(pkgfile) != pkgref)
|
if (makeRef(pkgfile) != hash)
|
||||||
throw Error("file " + pkgfile + " is stale");
|
throw Error("file " + pkgfile + " is stale");
|
||||||
|
|
||||||
/* Read the package description file. */
|
/* Read the package description file. */
|
||||||
|
@ -288,7 +288,7 @@ void installPkg(string pkgref)
|
||||||
throw Error("no builder specified");
|
throw Error("no builder specified");
|
||||||
|
|
||||||
/* Construct a path for the installed package. */
|
/* Construct a path for the installed package. */
|
||||||
path = pkgHome + "/" + pkgref;
|
path = pkgHome + "/" + hash;
|
||||||
|
|
||||||
/* Create the path. */
|
/* Create the path. */
|
||||||
if (mkdir(path.c_str(), 0777))
|
if (mkdir(path.c_str(), 0777))
|
||||||
|
@ -326,8 +326,7 @@ void installPkg(string pkgref)
|
||||||
cout << strerror(errno) << endl;
|
cout << strerror(errno) << endl;
|
||||||
|
|
||||||
cout << "unable to execute builder\n";
|
cout << "unable to execute builder\n";
|
||||||
_exit(1);
|
_exit(1); }
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,20 +345,118 @@ void installPkg(string pkgref)
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
setDB(dbInstPkgs, pkgref, path);
|
setDB(dbInstPkgs, hash, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string getPkg(string pkgref)
|
string getPkg(string hash)
|
||||||
{
|
{
|
||||||
string path;
|
string path;
|
||||||
checkRef(pkgref);
|
checkRef(hash);
|
||||||
while (!queryDB(dbInstPkgs, pkgref, path))
|
while (!queryDB(dbInstPkgs, hash, path))
|
||||||
installPkg(pkgref);
|
installPkg(hash);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void runPkg(string hash)
|
||||||
|
{
|
||||||
|
string pkgfile;
|
||||||
|
string src;
|
||||||
|
string path;
|
||||||
|
string cmd;
|
||||||
|
string runner;
|
||||||
|
|
||||||
|
if (!queryDB(dbRefs, hash, pkgfile))
|
||||||
|
throw Error("unknown package " + hash);
|
||||||
|
|
||||||
|
cerr << "running package " + hash + " from " + pkgfile + "\n";
|
||||||
|
|
||||||
|
/* Verify that the file hasn't changed. !!! race */
|
||||||
|
if (makeRef(pkgfile) != hash)
|
||||||
|
throw Error("file " + pkgfile + " is stale");
|
||||||
|
|
||||||
|
/* Read the package description file. */
|
||||||
|
DepList pkgImports, fileImports;
|
||||||
|
readPkgDescr(pkgfile, pkgImports, fileImports);
|
||||||
|
|
||||||
|
/* Recursively fetch all the dependencies, filling in the
|
||||||
|
environment as we go along. */
|
||||||
|
Environment env;
|
||||||
|
|
||||||
|
for (DepList::iterator it = pkgImports.begin();
|
||||||
|
it != pkgImports.end(); it++)
|
||||||
|
{
|
||||||
|
cerr << "fetching package dependency "
|
||||||
|
<< it->name << " <- " << it->ref
|
||||||
|
<< endl;
|
||||||
|
env.push_back(EnvPair(it->name, getPkg(it->ref)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (DepList::iterator it = fileImports.begin();
|
||||||
|
it != fileImports.end(); it++)
|
||||||
|
{
|
||||||
|
cerr << "fetching file dependency "
|
||||||
|
<< it->name << " = " << it->ref
|
||||||
|
<< endl;
|
||||||
|
|
||||||
|
string file;
|
||||||
|
|
||||||
|
if (!queryDB(dbRefs, it->ref, file))
|
||||||
|
throw Error("unknown file " + it->ref);
|
||||||
|
|
||||||
|
if (makeRef(file) != it->ref)
|
||||||
|
throw Error("file " + file + " is stale");
|
||||||
|
|
||||||
|
if (it->name == "run")
|
||||||
|
runner = file;
|
||||||
|
else
|
||||||
|
env.push_back(EnvPair(it->name, file));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runner == "")
|
||||||
|
throw Error("no runner specified");
|
||||||
|
|
||||||
|
/* Fork a child to build the package. */
|
||||||
|
pid_t pid;
|
||||||
|
switch (pid = fork()) {
|
||||||
|
|
||||||
|
case -1:
|
||||||
|
throw Error("unable to fork");
|
||||||
|
|
||||||
|
case 0: { /* child */
|
||||||
|
|
||||||
|
/* Fill in the environment. We don't bother freeing the
|
||||||
|
strings, since we'll exec or die soon anyway. */
|
||||||
|
for (Environment::iterator it = env.begin();
|
||||||
|
it != env.end(); it++)
|
||||||
|
{
|
||||||
|
string * s = new string(it->first + "=" + it->second);
|
||||||
|
putenv((char *) s->c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Execute the runner. This should not return. */
|
||||||
|
execl(runner.c_str(), runner.c_str(), 0);
|
||||||
|
|
||||||
|
cout << strerror(errno) << endl;
|
||||||
|
|
||||||
|
cout << "unable to execute runner\n";
|
||||||
|
_exit(1); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parent */
|
||||||
|
|
||||||
|
/* Wait for the child to finish. */
|
||||||
|
int status;
|
||||||
|
if (waitpid(pid, &status, 0) != pid)
|
||||||
|
throw Error("unable to wait for child");
|
||||||
|
|
||||||
|
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||||
|
throw Error("unable to run package");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
string absPath(string filename)
|
string absPath(string filename)
|
||||||
{
|
{
|
||||||
if (filename[0] != '/') {
|
if (filename[0] != '/') {
|
||||||
|
@ -381,13 +478,13 @@ void registerFile(string filename)
|
||||||
|
|
||||||
|
|
||||||
/* This is primarily used for bootstrapping. */
|
/* This is primarily used for bootstrapping. */
|
||||||
void registerInstalledPkg(string pkgref, string path)
|
void registerInstalledPkg(string hash, string path)
|
||||||
{
|
{
|
||||||
checkRef(pkgref);
|
checkRef(hash);
|
||||||
if (path == "")
|
if (path == "")
|
||||||
delDB(dbInstPkgs, pkgref);
|
delDB(dbInstPkgs, hash);
|
||||||
else
|
else
|
||||||
setDB(dbInstPkgs, pkgref, path);
|
setDB(dbInstPkgs, hash, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -475,6 +572,9 @@ void run(int argc, char * * argv)
|
||||||
if (argc != 1) throw argcError;
|
if (argc != 1) throw argcError;
|
||||||
string path = getPkg(argv[0]);
|
string path = getPkg(argv[0]);
|
||||||
cout << path << endl;
|
cout << path << endl;
|
||||||
|
} else if (cmd == "run") {
|
||||||
|
if (argc != 1) throw argcError;
|
||||||
|
runPkg(argv[0]);
|
||||||
} else if (cmd == "regfile") {
|
} else if (cmd == "regfile") {
|
||||||
if (argc != 1) throw argcError;
|
if (argc != 1) throw argcError;
|
||||||
registerFile(argv[0]);
|
registerFile(argv[0]);
|
||||||
|
@ -500,7 +600,7 @@ Subcommands:
|
||||||
Initialize the database.
|
Initialize the database.
|
||||||
|
|
||||||
verify
|
verify
|
||||||
Removes stale entries from the database.
|
Remove stale entries from the database.
|
||||||
|
|
||||||
regfile FILENAME
|
regfile FILENAME
|
||||||
Register FILENAME keyed by its hash.
|
Register FILENAME keyed by its hash.
|
||||||
|
@ -511,6 +611,9 @@ Subcommands:
|
||||||
getpkg HASH
|
getpkg HASH
|
||||||
Ensure that the package referenced by HASH is installed. Prints
|
Ensure that the package referenced by HASH is installed. Prints
|
||||||
out the path of the package on stdout.
|
out the path of the package on stdout.
|
||||||
|
|
||||||
|
run HASH
|
||||||
|
Run the descriptor referenced by HASH.
|
||||||
";
|
";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue