about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2003-03-23T23·24+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2003-03-23T23·24+0000
commit20d165c34467338f07c4808783cd50318c38a47b (patch)
tree152a2f7c04c61c6c69499c2302debb0978a08c6c
parent800d8e950f13b9cb9099c5d1270a4385d5ae55da (diff)
* 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.

-rw-r--r--src/nix.cc143
1 files changed, 123 insertions, 20 deletions
diff --git a/src/nix.cc b/src/nix.cc
index cc81260e32b7..40f41eb5a693 100644
--- a/src/nix.cc
+++ b/src/nix.cc
@@ -166,7 +166,7 @@ string makeRef(string filename)
     if (!pipe) throw BadRefError("cannot execute md5sum");
 
     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;
 
     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 list<EnvPair> Environment;
 
 
-void installPkg(string pkgref)
+void installPkg(string hash)
 {
     string pkgfile;
     string src;
@@ -237,13 +237,13 @@ void installPkg(string pkgref)
     string cmd;
     string builder;
 
-    if (!queryDB(dbRefs, pkgref, pkgfile))
-        throw Error("unknown package " + pkgref);
+    if (!queryDB(dbRefs, hash, pkgfile))
+        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 */
-    if (makeRef(pkgfile) != pkgref)
+    if (makeRef(pkgfile) != hash)
         throw Error("file " + pkgfile + " is stale");
 
     /* Read the package description file. */
@@ -288,7 +288,7 @@ void installPkg(string pkgref)
         throw Error("no builder specified");
 
     /* Construct a path for the installed package. */
-    path = pkgHome + "/" + pkgref;
+    path = pkgHome + "/" + hash;
 
     /* Create the path. */
     if (mkdir(path.c_str(), 0777))
@@ -326,8 +326,7 @@ void installPkg(string pkgref)
             cout << strerror(errno) << endl;
 
             cout << "unable to execute builder\n";
-            _exit(1);
-        }
+            _exit(1); }
 
         }
 
@@ -346,20 +345,118 @@ void installPkg(string pkgref)
         throw;
     }
 
-    setDB(dbInstPkgs, pkgref, path);
+    setDB(dbInstPkgs, hash, path);
 }
 
 
-string getPkg(string pkgref)
+string getPkg(string hash)
 {
     string path;
-    checkRef(pkgref);
-    while (!queryDB(dbInstPkgs, pkgref, path))
-        installPkg(pkgref);
+    checkRef(hash);
+    while (!queryDB(dbInstPkgs, hash, path))
+        installPkg(hash);
     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)
 {
     if (filename[0] != '/') {
@@ -381,13 +478,13 @@ void registerFile(string filename)
 
 
 /* This is primarily used for bootstrapping. */
-void registerInstalledPkg(string pkgref, string path)
+void registerInstalledPkg(string hash, string path)
 {
-    checkRef(pkgref);
+    checkRef(hash);
     if (path == "")
-        delDB(dbInstPkgs, pkgref);
+        delDB(dbInstPkgs, hash);
     else
-        setDB(dbInstPkgs, pkgref, path);
+        setDB(dbInstPkgs, hash, path);
 }
 
 
@@ -475,6 +572,9 @@ void run(int argc, char * * argv)
         if (argc != 1) throw argcError;
         string path = getPkg(argv[0]);
         cout << path << endl;
+    } else if (cmd == "run") {
+        if (argc != 1) throw argcError;
+        runPkg(argv[0]);
     } else if (cmd == "regfile") {
         if (argc != 1) throw argcError;
         registerFile(argv[0]);
@@ -500,7 +600,7 @@ Subcommands:
     Initialize the database.
 
   verify
-    Removes stale entries from the database.
+    Remove stale entries from the database.
 
   regfile FILENAME
     Register FILENAME keyed by its hash.
@@ -511,6 +611,9 @@ Subcommands:
   getpkg HASH
     Ensure that the package referenced by HASH is installed. Prints
     out the path of the package on stdout.
+
+  run HASH
+    Run the descriptor referenced by HASH.
 ";
 }