about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/fix.cc196
1 files changed, 130 insertions, 66 deletions
diff --git a/src/fix.cc b/src/fix.cc
index 0d3ae9bff868..052c1d4c9103 100644
--- a/src/fix.cc
+++ b/src/fix.cc
@@ -16,6 +16,9 @@ static string nixDescriptorDir;
 static string nixSourcesDir;
 
 
+static bool verbose = false;
+
+
 /* Mapping of Fix file names to the hashes of the resulting Nix
    descriptors. */
 typedef map<string, string> DescriptorMap;
@@ -23,10 +26,6 @@ typedef map<string, string> DescriptorMap;
 
 /* Forward declarations. */
 
-string instantiateDescriptor(string filename,
-    DescriptorMap & done);
-
-
 void registerFile(string filename)
 {
     int res = system(("nix regfile " + filename).c_str());
@@ -74,79 +73,136 @@ string fetchURL(string url)
 }
 
 
-/* Term evaluation functions. */
-
-string evaluateStr(ATerm e)
+Error badTerm(const string & msg, ATerm e)
 {
-    char * s;
-    if (ATmatch(e, "<str>", &s))
-        return s;
-    else throw Error("invalid string expression");
+    char * s = ATwriteToString(e);
+    return Error(msg + ", in `" + s + "'");
 }
 
 
-ATerm evaluateBool(ATerm e)
+/* Term evaluation. */
+
+typedef map<string, ATerm> BindingsMap;
+
+struct EvalContext
 {
-    if (ATmatch(e, "True") || ATmatch(e, "False"))
-        return e;
-    else throw Error("invalid boolean expression");
-}
+    string dir;
+    DescriptorMap * done;
+    BindingsMap * vars;
+};
+
 
+ATerm evaluate(ATerm e, EvalContext ctx);
+string instantiateDescriptor(string filename, EvalContext ctx);
 
-string evaluateFile(ATerm e, string dir)
+
+string evaluateStr(ATerm e, EvalContext ctx)
 {
+    e = evaluate(e, ctx);
     char * s;
-    ATerm t;
-    if (ATmatch(e, "<str>", &s)) {
-        checkHash(s);
+    if (ATmatch(e, "Str(<str>)", &s))
         return s;
-    } else if (ATmatch(e, "Url(<term>)", &t)) {
-        string url = evaluateStr(t);
-        string filename = fetchURL(url);
-        registerFile(filename);
-        return hashFile(filename);
-    } else if (ATmatch(e, "Local(<term>)", &t)) {
-        string filename = absPath(evaluateStr(t), dir); /* !!! */
-        string cmd = "cp -p " + filename + " " + nixSourcesDir;
-        int res = system(cmd.c_str());
-        if (WEXITSTATUS(res) != 0)
-            throw Error("cannot copy " + filename);
-        registerFile(nixSourcesDir + "/" + baseNameOf(filename));
-        return hashFile(filename);
-    } else throw Error("invalid file expression");
+    else throw badTerm("string value expected", e);
 }
 
 
-string evaluatePkg(ATerm e, string dir, DescriptorMap & done)
+bool evaluateBool(ATerm e, EvalContext ctx)
 {
-    char * s;
-    ATerm t;
-    if (ATmatch(e, "<str>", &s)) {
-        checkHash(s);
-        return s;
-    } else if (ATmatch(e, "Fix(<term>)", &t)) {
-        string filename = absPath(evaluateStr(t), dir); /* !!! */
-        return instantiateDescriptor(filename, done);
-    } else throw Error("invalid pkg expression");
+    e = evaluate(e, ctx);
+    if (ATmatch(e, "Bool(True)"))
+        return true;
+    else if (ATmatch(e, "Bool(False)"))
+        return false;
+    else throw badTerm("boolean value expected", e);
 }
 
 
-ATerm evaluate(ATerm e, string dir, DescriptorMap & done)
+ATerm evaluate(ATerm e, EvalContext ctx)
 {
-    ATerm t;
-    if (ATmatch(e, "Str(<term>)", &t))
-        return ATmake("Str(<str>)", evaluateStr(t).c_str());
-    else if (ATmatch(e, "Bool(<term>)", &t))
-        return ATmake("Bool(<term>)", evaluateBool(t));
-    else if (ATmatch(e, "File(<term>)", &t))
-        return ATmake("File(<str>)", evaluateFile(t, dir).c_str());
-    else if (ATmatch(e, "Pkg(<term>)", &t))
-        return ATmake("Pkg(<str>)", evaluatePkg(t, dir, done).c_str());
-    else throw Error("invalid expression type");
-}
+    char * s;
+    ATerm e2;
+    ATerm eCond, eTrue, eFalse;
 
+    /* Check for normal forms first. */
 
-typedef map<string, ATerm> BindingsMap;
+    if (ATmatch(e, "Str(<str>)", &s) ||
+        ATmatch(e, "Bool(True)") || ATmatch(e, "Bool(False)"))
+        return e;
+    
+    else if (
+        ATmatch(e, "Pkg(<str>)", &s) || 
+        ATmatch(e, "File(<str>)", &s))
+    {
+        checkHash(s);
+        return e;
+    }
+
+    /* Short-hands. */
+
+    else if (ATmatch(e, "<str>", &s))
+        return ATmake("Str(<str>)", s);
+
+    else if (ATmatch(e, "True", &s))
+        return ATmake("Bool(True)", s);
+
+    else if (ATmatch(e, "False", &s))
+        return ATmake("Bool(False)", s);
+
+    /* Functions. */
+
+    /* `Var' looks up a variable. */
+    else if (ATmatch(e, "Var(<str>)", &s)) {
+        string name(s);
+        ATerm e2 = (*ctx.vars)[name];
+        if (!e2) throw Error("undefined variable " + name);
+        return evaluate(e2, ctx); /* !!! update binding */
+    }
+
+    /* `Fix' recursively instantiates a Fix descriptor, returning the
+       hash of the generated Nix descriptor. */
+    else if (ATmatch(e, "Fix(<term>)", &e2)) {
+        string filename = absPath(evaluateStr(e2, ctx), ctx.dir); /* !!! */
+        return ATmake("Pkg(<str>)",
+            instantiateDescriptor(filename, ctx).c_str());
+    }
+
+    /* `Source' copies the specified file to nixSourcesDir, registers
+       it with Nix, and returns the hash of the file. */
+    else if (ATmatch(e, "Source(<term>)", &e2)) {
+        string source = absPath(evaluateStr(e2, ctx), ctx.dir); /* !!! */
+        string target = nixSourcesDir + "/" + baseNameOf(source);
+
+        // Don't copy if filename is already in nixSourcesDir.
+        if (source != target) {
+            if (verbose)
+                cerr << "copying source " << source << endl;
+            string cmd = "cp -p " + source + " " + target;
+            int res = system(cmd.c_str());
+            if (WEXITSTATUS(res) != 0)
+                throw Error("cannot copy " + source + " to " + target);
+        }
+        
+        registerFile(target);
+        return ATmake("File(<str>)", hashFile(target).c_str());
+    }
+
+    /* `Url' fetches a file from the network, caching it in
+       nixSourcesDir and returning the file name. */
+    else if (ATmatch(e, "Url(<term>)", &e2)) {
+        string url = evaluateStr(e2, ctx);
+        if (verbose)
+            cerr << "fetching " << url << endl;
+        string filename = fetchURL(url);
+        return ATmake("Str(<str>)", filename.c_str());
+    }
+
+    /* `If' provides conditional evaluation. */
+    else if (ATmatch(e, "If(<term>, <term>, <term>)", 
+                 &eCond, &eTrue, &eFalse)) 
+        return evaluate(evaluateBool(eCond, ctx) ? eTrue : eFalse, ctx);
+
+    else throw badTerm("invalid expression", e);
+}
 
 
 string getStringFromMap(BindingsMap & bindingsMap,
@@ -164,15 +220,14 @@ string getStringFromMap(BindingsMap & bindingsMap,
 
 /* Instantiate a Fix descriptors into a Nix descriptor, recursively
    instantiating referenced descriptors as well. */
-string instantiateDescriptor(string filename,
-    DescriptorMap & done)
+string instantiateDescriptor(string filename, EvalContext ctx)
 {
     /* Already done? */
-    DescriptorMap::iterator isInMap = done.find(filename);
-    if (isInMap != done.end()) return isInMap->second;
+    DescriptorMap::iterator isInMap = ctx.done->find(filename);
+    if (isInMap != ctx.done->end()) return isInMap->second;
 
     /* No. */
-    string dir = dirOf(filename);
+    ctx.dir = dirOf(filename);
 
     /* Read the Fix descriptor as an ATerm. */
     ATerm inTerm = ATreadFromNamedFile(filename.c_str());
@@ -184,6 +239,7 @@ string instantiateDescriptor(string filename,
     
     /* Iterate over the bindings and evaluate them to normal form. */
     BindingsMap bindingsMap; /* the normal forms */
+    ctx.vars = &bindingsMap;
 
     char * cname;
     ATerm value;
@@ -191,7 +247,7 @@ string instantiateDescriptor(string filename,
                &cname, &value, &bindings)) 
     {
         string name(cname);
-        ATerm e = evaluate(value, dir, done);
+        ATerm e = evaluate(value, ctx);
         bindingsMap[name] = e;
     }
 
@@ -229,7 +285,10 @@ string instantiateDescriptor(string filename,
     /* Register it with Nix. */
     registerFile(outFilename);
 
-    done[filename] = outHash;
+    if (verbose)
+        cerr << "instantiated " << outHash << " from " << filename << endl;
+
+    (*ctx.done)[filename] = outHash;
     return outHash;
 }
 
@@ -239,11 +298,14 @@ void instantiateDescriptors(Strings filenames)
 {
     DescriptorMap done;
 
+    EvalContext ctx;
+    ctx.done = &done;
+
     for (Strings::iterator it = filenames.begin();
          it != filenames.end(); it++)
     {
         string filename = absPath(*it);
-        cout << instantiateDescriptor(filename, done) << endl;
+        cout << instantiateDescriptor(filename, ctx) << endl;
     }
 }
 
@@ -274,7 +336,9 @@ void run(Strings::iterator argCur, Strings::iterator argEnd)
         if (arg == "-h" || arg == "--help") {
             printUsage();
             return;
-        } if (arg == "--instantiate" || arg == "-i") {
+        } else if (arg == "-v" || arg == "--verbose") {
+            verbose = true;
+        } else if (arg == "--instantiate" || arg == "-i") {
             command = cmdInstantiate;
         } else if (arg[0] == '-')
             throw UsageError("invalid option `" + arg + "'");