Hi,

This patch adds a dependence tracking mechanism to postinstall scripts.
The idea is essentially the one described in
<http://cygwin.com/ml/cygwin-apps/2003-03/msg00022.html>.  This patch is
very preliminary, and not tested much beyond its ability to sort scripts
correctly (compiles, though).  This should provide a foundation for
further refinement, however.  Please comment.
        Igor
==============================================================================
ChangeLog:
2003-03-03  Igor Pechtchanski <[EMAIL PROTECTED]>

        * postinstall.cc (RunFindVisitor::executeAll): New
        member function that propagates script dependences,
        topologically sorts the script list, and then executes
        the scripts.
        (RunFindVisitor::visitFile): Add file to list instead of
        running it.
        (RunFindVisitor::files): New member variable.
        (processFile): New static helper function that extracts
        dependences from each script file.
        (do_postinstall): Add executeAll call.
        (FileDesc): New helper class.
        (sharpbang): New static helper function.

--
                                http://cs.nyu.edu/~pechtcha/
      |\      _,,,---,,_                [EMAIL PROTECTED]
ZZZzz /,`.-'`'    -.  ;-;;,_            [EMAIL PROTECTED]
     |,4-  ) )-,_. ,\ (  `'-'           Igor Pechtchanski
    '---''(_/--'  `-'\_) fL     a.k.a JaguaR-R-R-r-r-r-.-.-.  Meow!

Oh, boy, virtual memory! Now I'm gonna make myself a really *big* RAMdisk!
  -- /usr/games/fortune
Index: postinstall.cc
===================================================================
RCS file: /cvs/cygwin-apps/setup/postinstall.cc,v
retrieving revision 2.9
diff -u -p -r2.9 postinstall.cc
--- postinstall.cc      19 May 2002 03:07:51 -0000      2.9
+++ postinstall.cc      4 Mar 2003 04:29:26 -0000
@@ -26,6 +26,112 @@ static const char *cvsid =
 #include "mount.h"
 #include "script.h"
 #include "FindVisitor.h"
+#include "io_stream.h"
+#include "resource.h"
+#include "msg.h"
+#include "log.h"
+#include <list>
+#include <set>
+#include <map>
+
+struct FileDesc
+{
+  String path;
+  std::set<FileDesc*> dependences;
+  bool mark;
+  static std::map<String, FileDesc*, String::caseless> fdmap;
+
+  FileDesc(String const &basePath) : path(basePath), mark(false) { }
+  bool addDependence(FileDesc *dep) {
+    return dependences.insert(dep).second;
+  }
+  bool addDependence(String const &deps) {
+    return addDependence(create(deps));
+  }
+  void propagateDependences() {
+    // TODO: detect circular dependences
+    if (mark) return;
+    mark = true;
+    for (std::set<FileDesc*>::iterator i = dependences.begin();
+        i != dependences.end();
+        ++i)
+      {
+       (*i)->propagateDependences();
+       for (std::set<FileDesc*>::iterator d = (*i)->dependences.begin();
+            d != (*i)->dependences.end();
+            ++d)
+         addDependence(*d);
+      }
+  }
+  static FileDesc *create(String const &path) {
+    FileDesc *&fd = fdmap[path];
+    if (fd == NULL) fd = new FileDesc(path);
+    return fd;
+  }
+  bool operator == (FileDesc const &f) const {
+    return path == f.path && dependences == f.dependences;
+  }
+  bool operator > (FileDesc &f) {
+    return (dependences.find(&f) != dependences.end());
+  }
+  bool operator < (FileDesc &f) {
+    return (f > *this);
+  }
+};
+std::map<String, FileDesc*, String::caseless> FileDesc::fdmap;
+
+#define WHITESPACE " \t"
+
+inline bool sharpbang(char *line)
+{
+  if (*line != '#') return false;
+  char *bang = strtok(line+1, WHITESPACE);
+  return (bang != NULL && *bang == '!');
+}
+
+#define DEPEND_STR "depends on: "
+
+static FileDesc *processFile(String const &basePath)
+{
+  // TODO: free unused map entries
+  FileDesc *retval = FileDesc::create(basePath);
+  char const *ext = strrchr (basePath.cstr_oneuse(), '.');
+  char const *cmt = NULL;
+  if (strcasecmp(ext, ".sh") == 0)
+    cmt = "#";
+  else if (strcasecmp(ext, ".bat") == 0)
+    cmt = "rem";
+  // Open file
+  io_stream *f = io_stream::open(String("file://") + basePath, "rt");
+  if (!f)
+    {
+      const char *err = strerror (errno);
+      if (!err)
+       err = "(unknown error)";
+      note (NULL, IDS_ERR_OPEN_READ, basePath.cstr_oneuse(), err);
+      return retval;
+    }
+
+  // Read first available line
+  char line[500];
+  f->gets(line, 500);
+  if (sharpbang(line))
+    f->gets(line, 500);
+
+  // Parse dependences
+  if (strncasecmp(line, cmt, strlen(cmt)) == 0)
+    {
+      char *dstr = strtok(line+strlen(cmt), WHITESPACE);
+      if (strncasecmp(dstr, DEPEND_STR, strlen(DEPEND_STR)) == 0)
+       {
+         // for each dependence
+         while ((dstr = strtok(NULL, WHITESPACE)) != NULL)
+           retval->addDependence(dstr);
+       }
+    }
+  delete f;
+  return retval;
+}
 
 class RunFindVisitor : public FindVisitor
 {
@@ -33,14 +139,49 @@ public:
   RunFindVisitor (){}
   virtual void visitFile(String const &basePath, const WIN32_FIND_DATA *theFile)
     {
-      run_script ("/etc/postinstall/", theFile->cFileName);
+      files.push_back(processFile(basePath));
     }
-  virtual ~ RunFindVisitor () {}
+  virtual ~ RunFindVisitor ();
+  virtual void executeAll ();
 protected:
   RunFindVisitor (RunFindVisitor const &);
   RunFindVisitor & operator= (RunFindVisitor const &);
+private:
+  std::list<FileDesc*> files;
 };
   
+RunFindVisitor::~RunFindVisitor()
+{
+  for (std::list<FileDesc*>::iterator i = files.begin(); i != files.end(); ++i)
+    delete *i;
+}
+
+inline bool lt_fd(FileDesc *f1, FileDesc *f2) { return (*f1) < (*f2); }
+
+void RunFindVisitor::executeAll()
+{
+  for (std::list<FileDesc*>::iterator i = files.begin(); i != files.end(); ++i)
+    (*i)->propagateDependences();
+  // Topological sort
+  files.sort(lt_fd);
+  for (std::list<FileDesc*>::iterator i = files.begin(); i != files.end(); ++i)
+    {
+      // Check that all dependences are executed already
+      for (std::set<FileDesc*>::iterator d = (*i)->dependences.begin();
+          d != (*i)->dependences.end();
+          ++d)
+       if (!io_stream::exists (String("file://") + (*d)->path + ".done"))
+         {
+           char const *msg = "missing";
+           if (!io_stream::exists (String("file://") + (*d)->path))
+             msg = "not executed";
+           log (LOG_TIMESTAMP, String("error: dependence ") + (*d)->path +
+                " " + msg + " for script " + (*i)->path);
+         }
+      run_script ("/etc/postinstall/", (*i)->path);
+    }
+}
+
 void
 do_postinstall (HINSTANCE h, HWND owner)
 {
@@ -50,4 +191,5 @@ do_postinstall (HINSTANCE h, HWND owner)
   RunFindVisitor myVisitor;
   String postinst = cygpath ("/etc/postinstall");
   Find (postinst).accept (myVisitor);
+  myVisitor.executeAll();
 }

Reply via email to