[PATCH] Postinstall script ordering in setup - take 3
Igor Pechtchanski
pechtcha@cs.nyu.edu
Fri Mar 14 18:06:00 GMT 2003
On 5 Mar 2003, Robert Collins wrote:
> [snip]
> Any, as I don't have time to complete implementing the dpkg or rpm
> behaviour in this regard for setup until the end of the current ice age,
> it would be silly to not let in a reasonably implemented alternative.
>
> Thus - I think this is a short term bandaid, because it increases work,
> not decreases it, and there is a better solution out there, as shown by
> the other package managers.
>
> Rob
Ok. Here's that alternative, more reasonably implemented, and *tested*
this time :-) (at least as far as reading files, sorting them, and
executing scripts is concerned). The dependence-extraction mechanism
still needs to be verified, though. Comments and suggestions for
improvement are welcome.
Igor
==============================================================================
ChangeLog:
2003-03-03 Igor Pechtchanski <pechtcha@cs.nyu.edu>
* postinstall.cc (RunFindVisitor::executeAll): New
member function that propagates script dependencies,
topologically sorts the script list, and then executes
the scripts (via other calls).
(RunFindVisitor::visitFile): Change to add script to
list instead of running it immediately.
(RunFindVisitor::files): New member variable.
(RunFindVisitor::checkAndLogMissingDependencies): New
member function.
(FileDesc): New class that extracts and stores
dependencies from a script file.
(DEPEND_STR): New #define.
(do_postinstall): Add executeAll() call.
--
http://cs.nyu.edu/~pechtcha/
|\ _,,,---,,_ pechtcha@cs.nyu.edu
ZZZzz /,`.-'`' -. ;-;;,_ igor@watson.ibm.com
|,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
-------------- next part --------------
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 14 Mar 2003 17:40:09 -0000
@@ -26,6 +26,144 @@ 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>
+
+#define DEPEND_STR "depends on: "
+#define BUFLEN 500
+
+class FileDesc
+{
+public:
+ FileDesc(String const &filename);
+ String const &path() { return _path; }
+ std::set<FileDesc*> const & dependencies() { return _dependencies; }
+ bool addDependency(FileDesc *dep) {
+ return _dependencies.insert(dep).second;
+ }
+ bool addDependency(String const &deps) {
+ // TODO: free unused map entries
+ return addDependency(create(deps));
+ }
+ void propagateDependencies() {
+ // TODO: detect circular dependencies
+ if (_mark) return;
+ _mark = true;
+ for (std::set<FileDesc*>::iterator i = _dependencies.begin();
+ i != _dependencies.end();
+ ++i)
+ {
+ (*i)->propagateDependencies();
+ for (std::set<FileDesc*>::iterator d = (*i)->_dependencies.begin();
+ d != (*i)->_dependencies.end();
+ ++d)
+ addDependency(*d);
+ }
+ }
+ bool operator == (FileDesc const &f) const {
+ return _path == f._path && _dependencies == f._dependencies;
+ }
+ bool operator > (FileDesc &f) {
+ return (_dependencies.find(&f) != _dependencies.end());
+ }
+ bool operator < (FileDesc &f) {
+ return (f > *this);
+ }
+
+ static FileDesc *create(String const &path) {
+ FileDesc *&fd = fdmap[path];
+ if (fd == NULL) fd = new FileDesc(path);
+ return fd;
+ }
+private:
+ String _path;
+ std::set<FileDesc*> _dependencies;
+ bool _mark;
+ char _buf[BUFLEN];
+ char const *commentString();
+ char *readDependencyLine();
+
+ static std::map<String, FileDesc*, String::caseless> fdmap;
+};
+
+char const *FileDesc::commentString()
+{
+ char const *ext = strrchr (_path.cstr_oneuse(), '.');
+ char const *cmt = NULL;
+ if (strcasecmp(ext, ".sh") == 0)
+ cmt = "#";
+ else if (strcasecmp(ext, ".bat") == 0)
+ cmt = "rem";
+ return cmt;
+}
+
+#define WHITESPACE " \t"
+
+char *FileDesc::readDependencyLine()
+{
+ io_stream *f = io_stream::open(String("cygfile://") + _path, "rt");
+ if (!f)
+ {
+ const char *err = strerror (errno);
+ if (!err)
+ err = "(unknown error)";
+ log (LOG_TIMESTAMP, String("error: unable to open script ") +
+ _path + " for reading: " + err);
+ return NULL;
+ }
+
+ // Read first line after shbang (if any)
+ f->gets(_buf, BUFLEN);
+ if (*_buf == '#')
+ {
+ char *bang = strtok(_buf+1, WHITESPACE);
+ if (bang != NULL && *bang == '!')
+ f->gets(_buf, BUFLEN);
+ else
+ *(bang+strlen(bang)) = ' ';
+ }
+
+ delete f;
+
+ // Check if a dependency line
+ char const *cmt = commentString();
+
+ // - has to be a known type of script
+ if (cmt == NULL)
+ return NULL;
+ // - has to start with a comment
+ if (strncasecmp(_buf, cmt, strlen(cmt)) != 0)
+ return NULL;
+ // - has to contain a marker after whitespace
+ char *mstr = strtok(_buf+strlen(cmt), WHITESPACE);
+ if (mstr == NULL)
+ return NULL;
+ *(mstr+strlen(mstr)) = ' ';
+ if (strncasecmp(mstr, DEPEND_STR, strlen(DEPEND_STR)) != 0)
+ return NULL;
+
+ return strchr(mstr, ':')+1;
+}
+
+FileDesc::FileDesc(String const &filename) : _path(filename), _mark(false)
+{
+ char *line = readDependencyLine();
+ if (line == NULL) // Unable to open file or no dependencies
+ return;
+
+ // Parse dependencies
+ for (char *dstr = strtok(line, WHITESPACE);
+ dstr == NULL;
+ dstr = strtok(NULL, WHITESPACE))
+ addDependency(dstr); // for each dependency
+}
+
+std::map<String, FileDesc*, String::caseless> FileDesc::fdmap;
class RunFindVisitor : public FindVisitor
{
@@ -33,14 +171,62 @@ public:
RunFindVisitor (){}
virtual void visitFile(String const &basePath, const WIN32_FIND_DATA *theFile)
{
- run_script ("/etc/postinstall/", theFile->cFileName);
+ files.push_back(FileDesc::create(String ("/etc/postinstall/") +
+ theFile->cFileName));
}
- virtual ~ RunFindVisitor () {}
+ virtual ~ RunFindVisitor ();
+ virtual void executeAll ();
protected:
RunFindVisitor (RunFindVisitor const &);
RunFindVisitor & operator= (RunFindVisitor const &);
+private:
+ std::list<FileDesc*> files;
+ void checkAndLogMissingDependencies(FileDesc *);
};
+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::checkAndLogMissingDependencies(FileDesc *f)
+{
+ // Check that all dependencies are executed already
+ for (std::set<FileDesc*>::iterator d = f->dependencies().begin();
+ d != f->dependencies().end();
+ ++d)
+ if (!io_stream::exists (String("cygfile://") + (*d)->path() + ".done"))
+ {
+ char const *msg = "missing";
+ if (!io_stream::exists (String("cygfile://") + (*d)->path()))
+ msg = "not executed";
+ log (LOG_TIMESTAMP, String("error: dependency ") + (*d)->path() +
+ msg + " for script " + f->path());
+ }
+}
+
+void RunFindVisitor::executeAll()
+{
+ for (std::list<FileDesc*>::iterator i = files.begin(); i != files.end(); ++i)
+ (*i)->propagateDependencies();
+ // Topological sort
+ files.sort(lt_fd);
+
+ String all_files = "";
+ for (std::list<FileDesc*>::iterator i = files.begin(); i != files.end(); ++i)
+ all_files += String(" ") + (*i)->path();
+ log (LOG_TIMESTAMP, String("Script order:") + all_files);
+
+ for (std::list<FileDesc*>::iterator i = files.begin(); i != files.end(); ++i)
+ {
+ checkAndLogMissingDependencies(*i);
+ run_script ("", (*i)->path());
+ }
+}
+
void
do_postinstall (HINSTANCE h, HWND owner)
{
@@ -50,4 +236,5 @@ do_postinstall (HINSTANCE h, HWND owner)
RunFindVisitor myVisitor;
String postinst = cygpath ("/etc/postinstall");
Find (postinst).accept (myVisitor);
+ myVisitor.executeAll();
}
More information about the Cygwin-apps
mailing list