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(); }