aboutsummaryrefslogtreecommitdiff
path: root/src/fix.cc
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2003-04-08 12:00:51 +0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2003-04-08 12:00:51 +0000
commit0d2b24cdd103f21861ad42fd6d98e5d1cb252646 (patch)
tree3956ec0b4b024acbf4c3a4f0ecc979fd9ccfa52c /src/fix.cc
parent814b256da43ebdad79fe9544a59f6ae680da7cb9 (diff)
* `Fix' is a high-level descriptor instantiator for Nix. It replaces
nix-instantiate.
Diffstat (limited to 'src/fix.cc')
-rw-r--r--src/fix.cc295
1 files changed, 295 insertions, 0 deletions
diff --git a/src/fix.cc b/src/fix.cc
new file mode 100644
index 000000000..cb1990928
--- /dev/null
+++ b/src/fix.cc
@@ -0,0 +1,295 @@
+#include <iostream>
+#include <map>
+
+extern "C" {
+#include <aterm2.h>
+}
+
+#include "util.hh"
+
+
+static string nixDescriptorDir;
+static string nixSourcesDir;
+
+
+typedef map<string, string> DescriptorMap;
+
+
+void registerFile(string filename)
+{
+ int res = system(("nix regfile " + filename).c_str());
+ if (WEXITSTATUS(res) != 0)
+ throw Error("cannot register " + filename + " with Nix");
+}
+
+
+/* Download object referenced by the given URL into the sources
+ directory. Return the file name it was downloaded to. */
+string fetchURL(string url)
+{
+ unsigned int pos = url.rfind('/');
+ if (pos == string::npos) throw Error("invalid url");
+ string filename(url, pos + 1);
+ string fullname = nixSourcesDir + "/" + filename;
+ /* !!! quoting */
+ string shellCmd =
+ "cd " + nixSourcesDir + " && wget --quiet -N \"" + url + "\"";
+ int res = system(shellCmd.c_str());
+ if (WEXITSTATUS(res) != 0)
+ throw Error("cannot fetch " + url);
+ return fullname;
+}
+
+
+/* Return the directory part of the given path, i.e., everything
+ before the final `/'. */
+string dirOf(string s)
+{
+ unsigned int pos = s.rfind('/');
+ if (pos == string::npos) throw Error("invalid file name");
+ return string(s, 0, pos);
+}
+
+
+/* Term evaluation functions. */
+
+string evaluateStr(ATerm e)
+{
+ char * s;
+ if (ATmatch(e, "<str>", &s))
+ return s;
+ else throw Error("invalid string expression");
+}
+
+
+ATerm evaluateBool(ATerm e)
+{
+ if (ATmatch(e, "True") || ATmatch(e, "False"))
+ return e;
+ else throw Error("invalid boolean expression");
+}
+
+
+string evaluateFile(ATerm e, string dir)
+{
+ char * s;
+ ATerm t;
+ if (ATmatch(e, "<str>", &s)) {
+ checkHash(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);
+ return hashFile(filename);
+ } else throw Error("invalid hash expression");
+}
+
+
+ATerm evaluatePkg(ATerm e, DescriptorMap & done)
+{
+ char * s;
+ if (ATmatch(e, "<str>", &s)) {
+ checkHash(s);
+ return s;
+ } else throw Error("invalid hash expression");
+}
+
+
+ATerm evaluate(ATerm e, string dir, DescriptorMap & done)
+{
+ 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(<term>)", evaluatePkg(t, done));
+ else throw Error("invalid expression type");
+}
+
+
+typedef map<string, ATerm> BindingsMap;
+
+
+string getStringFromMap(BindingsMap & bindingsMap,
+ const string & name)
+{
+ ATerm e = bindingsMap[name];
+ if (!e) throw Error("binding " + name + " is not set");
+ char * s;
+ if (ATmatch(e, "Str(<str>)", &s))
+ return s;
+ else
+ throw Error("binding " + name + " is not a string");
+}
+
+
+/* Instantiate a Fix descriptors into a Nix descriptor, recursively
+ instantiating referenced descriptors as well. */
+string instantiateDescriptor(string filename,
+ DescriptorMap & done)
+{
+ /* Already done? */
+ DescriptorMap::iterator isInMap = done.find(filename);
+ if (isInMap != done.end()) return isInMap->second;
+
+ /* No. */
+ string dir = dirOf(filename);
+
+ /* Read the Fix descriptor as an ATerm. */
+ ATerm inTerm = ATreadFromNamedFile(filename.c_str());
+ if (!inTerm) throw Error("cannot read aterm " + filename);
+
+ ATerm bindings;
+ if (!ATmatch(inTerm, "Descr(<term>)", &bindings))
+ throw Error("invalid term in " + filename);
+
+ /* Iterate over the bindings and evaluate them to normal form. */
+ BindingsMap bindingsMap; /* the normal forms */
+
+ char * cname;
+ ATerm value;
+ while (ATmatch(bindings, "[Bind(<str>, <term>), <list>]",
+ &cname, &value, &bindings))
+ {
+ string name(cname);
+ ATerm e = evaluate(value, dir, done);
+ bindingsMap[name] = e;
+ }
+
+ /* Construct a descriptor identifier by concatenating the package
+ and release ids. */
+ string pkgId = getStringFromMap(bindingsMap, "pkgId");
+ string releaseId = getStringFromMap(bindingsMap, "releaseId");
+ string id = pkgId + "-" + releaseId;
+ bindingsMap["id"] = ATmake("Str(<str>)", id.c_str());
+
+ /* Add a system name. */
+ bindingsMap["system"] = ATmake("Str(<str>)", thisSystem.c_str());
+
+ /* Construct the resulting ATerm. Note that iterating over the
+ map yields the bindings in sorted order, which is exactly the
+ canonical form for Nix descriptors. */
+ ATermList bindingsList = ATempty;
+ for (BindingsMap::iterator it = bindingsMap.begin();
+ it != bindingsMap.end(); it++)
+ /* !!! O(n^2) */
+ bindingsList = ATappend(bindingsList,
+ ATmake("Bind(<str>, <term>)", it->first.c_str(), it->second));
+ ATerm outTerm = ATmake("Descr(<term>)", bindingsList);
+
+ /* Write out the resulting ATerm. */
+ string tmpFilename = nixDescriptorDir + "/tmp";
+ if (!ATwriteToNamedTextFile(outTerm, tmpFilename.c_str()))
+ throw Error("cannot write aterm to " + tmpFilename);
+
+ string outHash = hashFile(tmpFilename);
+ string outFilename = nixDescriptorDir + "/" + id + "-" + outHash + ".nix";
+ if (rename(tmpFilename.c_str(), outFilename.c_str()))
+ throw Error("cannot rename " + tmpFilename + " to " + outFilename);
+
+ cout << outFilename << endl;
+
+ /* Register it with Nix. */
+ registerFile(outFilename);
+
+ done[filename] = outFilename;
+ return outFilename;
+}
+
+
+/* Instantiate a set of Fix descriptors into Nix descriptors. */
+void instantiateDescriptors(Strings filenames)
+{
+ DescriptorMap done;
+
+ for (Strings::iterator it = filenames.begin();
+ it != filenames.end(); it++)
+ {
+ string filename = absPath(*it);
+ instantiateDescriptor(filename, done);
+ }
+}
+
+
+/* Print help. */
+void printUsage()
+{
+ cerr <<
+"Usage: fix ...
+";
+}
+
+
+/* Parse the command-line arguments, call the right operation. */
+void run(Strings::iterator argCur, Strings::iterator argEnd)
+{
+ Strings extraArgs;
+ enum { cmdUnknown, cmdInstantiate } command = cmdUnknown;
+
+ char * homeDir = getenv(nixHomeDirEnvVar.c_str());
+ if (homeDir) nixHomeDir = homeDir;
+
+ nixDescriptorDir = nixHomeDir + "/var/nix/descriptors";
+ nixSourcesDir = nixHomeDir + "/var/nix/sources";
+
+ for ( ; argCur != argEnd; argCur++) {
+ string arg(*argCur);
+ if (arg == "-h" || arg == "--help") {
+ printUsage();
+ return;
+ } if (arg == "--instantiate" || arg == "-i") {
+ command = cmdInstantiate;
+ } else if (arg[0] == '-')
+ throw UsageError("invalid option `" + arg + "'");
+ else
+ extraArgs.push_back(arg);
+ }
+
+ switch (command) {
+
+ case cmdInstantiate:
+ instantiateDescriptors(extraArgs);
+ break;
+
+ default:
+ throw UsageError("no operation specified");
+ }
+}
+
+
+int main(int argc, char * * argv)
+{
+ ATerm bottomOfStack;
+ ATinit(argc, argv, &bottomOfStack);
+
+ /* Put the arguments in a vector. */
+ Strings args;
+ while (argc--) args.push_back(*argv++);
+ Strings::iterator argCur = args.begin(), argEnd = args.end();
+
+ argCur++;
+
+ try {
+ run(argCur, argEnd);
+ } catch (UsageError & e) {
+ cerr << "error: " << e.what() << endl
+ << "Try `fix -h' for more information.\n";
+ return 1;
+ } catch (exception & e) {
+ cerr << "error: " << e.what() << endl;
+ return 1;
+ }
+
+ return 0;
+}