From c9c99f253824deb85461861d1ccfa71fdf1badda Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Thu, 31 Aug 1995 13:50:16 +0000 Subject: Interactively create a distribution from a sourcetree. Not yet fully tested. --- Mac/scripts/MkDistr.py | 280 ++++++++++++++++++++++++++++++++++ Mac/scripts/MkDistr.rsrc.hqx | 31 ++++ Mac/scripts/MkDistr_ui.py | 346 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 657 insertions(+) create mode 100644 Mac/scripts/MkDistr.py create mode 100644 Mac/scripts/MkDistr.rsrc.hqx create mode 100644 Mac/scripts/MkDistr_ui.py diff --git a/Mac/scripts/MkDistr.py b/Mac/scripts/MkDistr.py new file mode 100644 index 0000000..deda71b --- /dev/null +++ b/Mac/scripts/MkDistr.py @@ -0,0 +1,280 @@ +# +# Interactively decide what to distribute +# +# The distribution type is signalled by a letter. The currently +# defined letters are: +# p PPC normal distribution +# P PPC development distribution +# m 68K normal distribution +# M 68K development distribution +# +# The exclude file signals files to always exclude, +# The pattern file records are of the form +# ('pm', '*.c') +# This excludes all files ending in .c for normal distributions. +# +# The include file signals files and directories to include. +# Records are of the form +# ('pPmM', 'Lib') +# This includes the Lib dir in all distributions +# ('pPmM', 'Tools:bgen:AE:AppleEvents.py', 'Lib:MacToolbox:AppleEvents.py') +# This includes the specified file, putting it in the given place. +# +from MkDistr_ui import * +import fnmatch +import regex +import os +import sys +import macfs +import macostools + +SyntaxError='Include/exclude file syntax error' + +class Matcher: + """Include/exclude database, common code""" + + def __init__(self, type, filename): + self.type = type + self.filename = filename + self.rawdata = [] + self.parse(filename) + self.rawdata.sort() + self.rebuild() + self.modified = 0 + + def parse(self, dbfile): + try: + fp = open(dbfile) + except IOError: + return + data = fp.readlines() + fp.close() + for d in data: + d = d[:-1] + if not d or d[0] == '#': continue + pat = self.parseline(d) + self.rawdata.append(pat) + + def parseline(self, line): + try: + data = eval(line) + except: + raise SyntaxError, line + if type(data) <> type(()) or len(data) not in (2,3): + raise SyntaxError, line + if len(data) == 2: + data = data + ('',) + return data + + def save(self): + fp = open(self.filename, 'w') + for d in self.rawdata: + fp.write(`d`+'\n') + self.modified = 0 + + def add(self, value): + if len(value) == 2: + value = value + ('',) + self.rawdata.append(value) + self.rebuild1(value) + self.modified = 1 + + def delete(self, value): + key = value + for i in range(len(self.rawdata)): + if self.rawdata[i][1] == key: + del self.rawdata[i] + self.unrebuild1(i, key) + self.modified = 1 + return + print 'Not found!', key + + def getall(self): + return map(lambda x: x[1], self.rawdata) + + def get(self, value): + for t, src, dst in self.rawdata: + if src == value: + return t, src, dst + print 'Not found!', value + + def is_modified(self): + return self.modified + +class IncMatcher(Matcher): + """Include filename database and matching engine""" + + def rebuild(self): + self.idict = {} + self.edict = {} + for v in self.rawdata: + self.rebuild1(v) + + def rebuild1(self, (tp, src, dst)): + if self.type in tp: + if dst == '': + dst = src + self.idict[src] = dst + else: + self.edict[src] = '' + + def unrebuild1(self, num, src): + if self.idict.has_key(src): + del self.idict[src] + else: + del self.edict[src] + + def match(self, patharg): + removed = [] + # First check the include directory + path = patharg + while 1: + if self.idict.has_key(path): + # We know of this path (or initial piece of path) + dstpath = self.idict[path] + # We do want it distributed. Tack on the tail. + while removed: + dstpath = os.path.join(dstpath, removed[0]) + removed = removed[1:] + # Finally, if the resultant string ends in a separator + # tack on our input filename + if dstpath[-1] == os.sep: + dir, file = os.path.split(path) + dstpath = os.path.join(dstpath, path) + return dstpath + path, lastcomp = os.path.split(path) + if not path: + break + removed[0:0] = [lastcomp] + # Next check the exclude directory + path = patharg + while 1: + if self.edict.has_key(path): + return '' + path, lastcomp = os.path.split(path) + if not path: + break + removed[0:0] = [lastcomp] + return None + + def checksourcetree(self): + rv = [] + for name in self.idict.keys(): + if not os.path.exists(name): + rv.append(name) + return rv + +class ExcMatcher(Matcher): + """Exclude pattern database and matching engine""" + + def rebuild(self): + self.relist = [] + for v in self.rawdata: + self.rebuild1(v) + + def rebuild1(self, (tp, src, dst)): + if self.type in tp: + pat = fnmatch.translate(src) + self.relist.append(regex.compile(pat)) + else: + self.relist.append(None) + + def unrebuild1(self, num, src): + del self.relist[num] + + def match(self, path): + comps = os.path.split(path) + file = comps[-1] + for pat in self.relist: + if pat and pat.match(file) == len(file): + return 1 + return 0 + + +class Main: + """The main program glueing it all together""" + + def __init__(self): + InitUI() + fss, ok = macfs.GetDirectory('Source directory:') + if not ok: + sys.exit(0) + os.chdir(fss.as_pathname()) + self.typedist = GetType() + print 'TYPE', self.typedist + self.inc = IncMatcher(self.typedist, '(MkDistr.include)') + self.exc = ExcMatcher(self.typedist, '(MkDistr.exclude)') + self.ui = MkDistrUI(self) + self.ui.mainloop() + + def check(self): + return self.checkdir(':', 1) + + def checkdir(self, path, istop): + files = os.listdir(path) + rv = [] + todo = [] + for f in files: + if self.exc.match(f): + continue + fullname = os.path.join(path, f) + if self.inc.match(fullname) == None: + if os.path.isdir(fullname): + todo.append(fullname) + else: + rv.append(fullname) + for d in todo: + if len(rv) > 100: + if istop: + rv.append('... and more ...') + return rv + rv = rv + self.checkdir(d, 0) + return rv + + def run(self, destprefix): + missing = self.inc.checksourcetree() + if missing: + print '==== Missing source files ====' + for i in missing: + print i + print '==== Fix and retry ====' + return + if not self.rundir(':', destprefix, 0): + return + self.rundir(':', destprefix, 1) + + def rundir(self, path, destprefix, doit): + files = os.listdir(path) + todo = [] + rv = 1 + for f in files: + if self.exc.match(f): + continue + fullname = os.path.join(path, f) + if os.path.isdir(fullname): + todo.append(fullname) + else: + dest = self.inc.match(fullname) + if dest == None: + print 'Not yet resolved:', fullname + rv = 0 + if dest: + if doit: + print 'COPY ', fullname + print ' -> ', os.path.join(destprefix, dest) + macostools.copy(fullname, os.path.join(destprefix, dest), 1) + for d in todo: + if not self.rundir(d, destprefix, doit): + rv = 0 + return rv + + def save(self): + self.inc.save() + self.exc.save() + + def is_modified(self): + return self.inc.is_modified() or self.exc.is_modified() + +if __name__ == '__main__': + Main() + diff --git a/Mac/scripts/MkDistr.rsrc.hqx b/Mac/scripts/MkDistr.rsrc.hqx new file mode 100644 index 0000000..b8c4478 --- /dev/null +++ b/Mac/scripts/MkDistr.rsrc.hqx @@ -0,0 +1,31 @@ +(This file may be decompressed with BinHex 4.0) + +:$%eV4'PcG()ZFR0bB`"bFh*M8P0&4!%!N!F&SINN!*!%!3!!!!5[!!!$V`!!!2)8 +T8SJ&+9+%"5P5rJ6'6!)%!!!#"!!!!J3!!!)%!!!#!a0Dd4TFh4b,R*cFQ0b!J!! +!(*cFQ058d9%!3$rN!3!!(*cFQ058d9%!3$rN!3!N"+XA"N$!*!'"D(rq"rrrJ!I +rrm!(rrrJ"rrrm!IrrrJ(rrrm"rrrrJIrrri(rrrq"rrrrJIrrri(rrrq"rrrrJI +rrri(rrrq"rrrrJIrrri(rrrq"rrrrJIrrri(rrrq"rrrrJIrrri(rrrq"rrrrJI +rrri(rrrq"rrrrJIrrri(rrrq"rrrrJIrrri!!!!&3"F!$i!rJ'K!!8"!!%!N!8# +!*!&&3"F!$i!rJ'K!!8"!!%!N!8#!3#3"-`!#!#3"B)")J#@!9`%!Np,!*!&JJ!+ +!*B!4!3'3f&ZBf9X!*!&C!$F!(B"@`8'8fpeFQ0P!*!&8!$F!')"@`8,8&"$)'4P +GQ9XEh!!N!C3!'i!BJ$B"3Sf1%XJ3QPZBA*j!*!&C!"Z!(B!f!8+8&"$)'*TEQ&b +H3#3"43!EJ!M!9B3#89NDA3J9'9iG&X!N!88!!S!*!"KL!K3BA4dCA*Z1J#3"9!! +#J"J!'+)#d9iBfaeC'8JD@ik!*!%p!!+!*!&JJ%L!*B"A!3#6dX!N!@#!!S!PJ"% +"!C$B@jMC@`!N!9N!0`!GJ&E"3C6Eh9bBf8!N!93!0`!BJ&E"3Y38%-JC'9fC@a[ +F!#3"P!!EJ"L!0J&#MBi5b"#D@jKFRN!N!9N!'i!GJ$B"3T38%-JBQPZBA*j!*!& +&!"Z!#-"9K!*4@4TG#"8CAKd@`#3"6)!EJ""!9F3!*!'&!!+!#3!BBJ(8fpeFQ0P +1Jm!N!8b!!S!3J"KL!a%CA0dD@jKG'P[EMS!N!93!!S!B!"LL!Y*EQ0XG@4P)'PZ +1Q`!!!!9!#J!+!%k!F3!!!%!!3#3"3)#!*!%&3!S!#J"1J(%!!!"!!%!N!8#!`#3 +"%i!!`#3"3S!#J$G!Bi!N!I`!33""!'2"!C%C@aPG'8!N!A`!'i""!$j"!G&C'Pd +,LiZ!*!'m!!+!33!BJ3'3@4N,LiZ!!!!&3"`!'i!k`&E!!!"!!%!N!8#"!#3"+)! +"!#3"4i!&!!`!13'#dCeE'`JFfpeFQ0PG!#3"6)!&!"%!13'$e"33b"NCACPE'p` +E@9ZG1F!N!9'!"3!@!$N"Jmf1%XJBQPZBA*j,@pZE(RR!*!&@J!8!'`!j!B28&"$ +)'*TEQ&bH5e[EQajj`#3"3S!#J!D!1D)(P4jF'8JEfBJC'PcG(*TBR9dD@pZ)(4[ +)'*eD@aN1J!!!'i!"!#3"3S!#J$G!Bi!N!I`!6B""3'-"!T%DA0dFQPLGA4P!*!& +m!$5!33",J3+3fKPBfXJG(*PC3#3"I!!#J%%!')%#NPZBfaeC'8Z,Li!N!A`!'i" +"!$'"!G&H'0XG@4P!!!!!3!!!!5[!!!$V`!!!2)!cC58%83!!!!F!+B!!84-6dF! +"!!54%P86!!%!%i#!*!)cC0`!J%!$`!!!"N!cC0i!J)!(J!!!IS!cC0X!J-!)`!! +!K-!cC0S!J3!1J!!!Ri!cC0N!J$rr`!!!3)!N!3#!Irr!!!!-J#3"!)#rrm!!!-p +!-f52!)$rrm!!!)X!*!%!J6rr`!!!TF!N!315@jME(9NC5"ND@&XEfF14AKME(9N +C5"ND@&XEfF%6@&TEKC*EQ0XG@4P,f9iBfaeC'8JGfPZC'ph%84TFh4bD@*eG'P[ +EL"dHA"PJqX: diff --git a/Mac/scripts/MkDistr_ui.py b/Mac/scripts/MkDistr_ui.py new file mode 100644 index 0000000..f9192f5 --- /dev/null +++ b/Mac/scripts/MkDistr_ui.py @@ -0,0 +1,346 @@ +# +# MkDistr - User Interface. +# +# Jack Jansen, CWI, August 1995 +# +# XXXX To be done (requires mods of FrameWork and toolbox interfaces too): +# - Give dialogs titles (need dlg->win conversion) +# - Place dialogs better (???) +# - as +# - big box around ok button +# - window-close crashes on reopen (why?) +# - Box around lists (???) +# - Change cursor while busy (need cursor support in Qd) +# +import Res +import Dlg +import Ctl +import List +import Win +import Qd +from FrameWork import * +import EasyDialogs +import macfs + +# Resource IDs +ID_MAIN = 514 +MAIN_LIST=1 +MAIN_MKDISTR=2 +MAIN_CHECK=3 +MAIN_INCLUDE=4 +MAIN_EXCLUDE=5 + +ID_INCEXC=515 +INCEXC_DELETE=2 +INCEXC_CHANGE=3 +INCEXC_ADD=4 + +ID_INCLUDE=512 +ID_EXCLUDE=513 +DLG_OK=1 +DLG_CANCEL=2 +DLG_FULL=3 +DLG_PPCDEV=4 +DLG_68K=5 +DLG_PPC=6 +DLG_BUTTONS=[DLG_FULL, DLG_PPCDEV, DLG_68K, DLG_PPC] +DLG_LETTERS=['S', 'P', 'm', 'p'] +DLG_SRCPATH=7 +DLG_DSTPATH=8 + +ID_DTYPE=516 + +class EditDialogWindow(DialogWindow): + """Include/exclude editor (modeless dialog window)""" + + def open(self, id, (type, src, dst), callback, cancelrv): + self.id = id + if id == ID_INCLUDE: + title = "Include file dialog" + else: + title = "Exclude pattern dialog" + #self.wid.as_Window().SetWTitle(title) + self.callback = callback + self.cancelrv = cancelrv + DialogWindow.open(self, id) + tp, h, rect = self.wid.GetDialogItem(DLG_SRCPATH) + Dlg.SetDialogItemText(h, src) + if id == ID_INCLUDE: + tp, h, rect = self.wid.GetDialogItem(DLG_DSTPATH) + Dlg.SetDialogItemText(h, dst) + for b in range(len(DLG_BUTTONS)): + if type == None or DLG_LETTERS[b] in type: + self.setbutton(DLG_BUTTONS[b], 1) + + def setbutton(self, num, value): + tp, h, rect = self.wid.GetDialogItem(num) + h.as_Control().SetControlValue(value) + + def getbutton(self, num): + tp, h, rect = self.wid.GetDialogItem(num) + return h.as_Control().GetControlValue() + + def do_itemhit(self, item, event): + if item in (DLG_OK, DLG_CANCEL): + self.done(item) + elif item in DLG_BUTTONS: + v = self.getbutton(item) + self.setbutton(item, (not v)) + # else it is not interesting + + def done(self, item): + if item == DLG_OK: + distlist = '' + for i in range(len(DLG_BUTTONS)): + if self.getbutton(DLG_BUTTONS[i]): + distlist = distlist + DLG_LETTERS[i] + tp, h, rect = self.wid.GetDialogItem(DLG_SRCPATH) + src = Dlg.GetDialogItemText(h) + if self.id == ID_INCLUDE: + tp, h, rect = self.wid.GetDialogItem(DLG_DSTPATH) + dst = Dlg.GetDialogItemText(h) + rv = (distlist, src, dst) + else: + rv = (distlist, src) + else: + rv = self.cancelrv + self.close() + self.callback((item==DLG_OK), rv) + +class ListWindow(DialogWindow): + """A dialog window containing a list as its main item""" + + def open(self, id, contents): + self.id = id + DialogWindow.open(self, id) + tp, h, rect = self.wid.GetDialogItem(MAIN_LIST) + rect2 = rect[0], rect[1], rect[2]-16, rect[3]-16 # Scroll bar space + self.list = List.LNew(rect2, (0, 0, 1, len(contents)), (0,0), 0, self.wid, + 0, 1, 1, 1) + self.setlist(contents) + + def setlist(self, contents): + self.list.LDelRow(0, 0) + self.list.LSetDrawingMode(0) + if contents: + self.list.LAddRow(len(contents), 0) + for i in range(len(contents)): + self.list.LSetCell(contents[i], (0, i)) + self.list.LSetDrawingMode(1) + self.list.LUpdate() + + def additem(self, item): + where = self.list.LAddRow(1, 0) + self.list.LSetCell(item, (0, where)) + + def delgetitem(self, item): + data = self.list.LGetCell(1000, (0, item)) + self.list.LDelRow(1, item) + return data + + def do_listhit(self, event): + (what, message, when, where, modifiers) = event + Qd.SetPort(self.wid) + where = Qd.GlobalToLocal(where) + if self.list.LClick(where, modifiers): + self.do_dclick(self.delgetselection()) + + def delgetselection(self): + items = [] + point = (0,0) + while 1: + ok, point = self.list.LGetSelect(1, point) + if not ok: + break + items.append(point[1]) + point = point[0], point[1]+1 + values = [] + items.reverse() + for i in items: + values.append(self.delgetitem(i)) + return values + + def do_rawupdate(self, window, event): + self.list.LUpdate() + + def do_close(self): + self.close() + + def close(self): + del self.list + DialogWindow.close(self) + + def mycb_add(self, ok, item): + if item: + self.additem(item[1]) + self.cb_add(item) + +class MainListWindow(ListWindow): + """The main window""" + + def open(self, id, cb_check, cb_run, cb_add): + ListWindow.open(self, id, []) + title = "MkDistr: Unresolved files" + #self.wid.as_Window().SetWTitle(title) + self.cb_run = cb_run + self.cb_check = cb_check + self.cb_add = cb_add + + def do_itemhit(self, item, event): + if item == MAIN_LIST: + self.do_listhit(event) + if item == MAIN_MKDISTR: + fss, ok = macfs.StandardPutFile('Destination folder:') + if not ok: + return + self.cb_run(fss.as_pathname()) + if item == MAIN_CHECK: + list = self.cb_check() + self.setlist(list) + if item == MAIN_INCLUDE: + self.do_dclick(self.delgetselection()) + if item == MAIN_EXCLUDE: + for i in self.delgetselection(): + self.cb_add(('', i, '')) + + def do_dclick(self, list): + if not list: + list = [''] + for l in list: + w = EditDialogWindow(self.parent) + w.open(ID_INCLUDE, (None, l, ''), self.mycb_add, None) + + def mycb_add(self, ok, item): + if item: + self.cb_add(item) + +class IncListWindow(ListWindow): + """An include/exclude window""" + def open(self, id, editid, contents, cb_add, cb_del, cb_get): + ListWindow.open(self, id, contents) + if editid == ID_INCLUDE: + title = "MkDistr: files to include" + else: + title = "MkDistr: patterns to exclude" + #self.wid.as_Window().SetWTitle(title) + self.editid = editid + self.cb_add = cb_add + self.cb_del = cb_del + self.cb_get = cb_get + + def do_itemhit(self, item, event): + if item == MAIN_LIST: + self.do_listhit(event) + if item == INCEXC_DELETE: + old = self.delgetselection() + for i in old: + self.cb_del(i) + if item == INCEXC_CHANGE: + self.do_dclick(self.delgetselection()) + if item == INCEXC_ADD: + w = EditDialogWindow(self.parent) + w.open(self.editid, (None, '', ''), self.mycb_add, None) + + def do_dclick(self, list): + if not list: + list = [''] + for l in list: + old = self.cb_get(l) + self.cb_del(l) + w = EditDialogWindow(self.parent) + w.open(self.editid, old, self.mycb_add, old) + +class MkDistrUI(Application): + def __init__(self, main): + self.main = main + Application.__init__(self) + self.mwin = MainListWindow(self) + self.mwin.open(ID_MAIN, self.main.check, self.main.run, self.main.inc.add) + self.iwin = None + self.ewin = None + + def makeusermenus(self): + self.filemenu = m = Menu(self.menubar, "File") + self.includeitem = MenuItem(m, "Show Include window", "", self.showinc) + self.excludeitem = MenuItem(m, "Show Exclude window", "", self.showexc) + self.saveitem = MenuItem(m, "Save databases", "S", self.save) + self.quititem = MenuItem(m, "Quit", "Q", self.quit) + + def quit(self, *args): + if self.main.is_modified(): + rv = EasyDialogs.AskYesNoCancel('Database modified. Save?', -1) + if rv == -1: + return + if rv == 1: + self.main.save() + raise self + + def save(self, *args): + self.main.save() + + def showinc(self, *args): + if self.iwin: + if self._windows.has_key(self.iwin): + self.iwin.close() + del self.iwin + self.iwin = IncListWindow(self) + self.iwin.open(ID_INCEXC, ID_INCLUDE, self.main.inc.getall(), self.main.inc.add, + self.main.inc.delete, self.main.inc.get) + + def showexc(self, *args): + if self.ewin: + if self._windows.has_key(self.ewin): + self.ewin.close() + del self.ewin + self.ewin = IncListWindow(self) + self.ewin.open(ID_INCEXC, ID_EXCLUDE, self.main.exc.getall(), self.main.exc.add, + self.main.exc.delete, self.main.exc.get) + + def do_about(self, id, item, window, event): + EasyDialogs.Message("Test the MkDistr user interface.") + +def GetType(): + """Ask user for distribution type""" + d = Dlg.GetNewDialog(ID_DTYPE, -1) + while 1: + rv = ModalDialog(None) + if rv >= 1 and rv <= 4: + return DLG_LETTERS[rv-1] + +def InitUI(): + """Initialize stuff needed by UI (a resource file)""" + Res.OpenResFile('MkDistr.rsrc') + +class _testerhelp: + def __init__(self, which): + self.which = which + + def get(self): + return [self.which+'-one', self.which+'-two'] + + def add(self, value): + if value: + print 'ADD', self.which, value + + def delete(self, value): + print 'DEL', self.which, value + +class _test: + def __init__(self): + import sys + Res.OpenResFile('MkDistr.rsrc') + self.inc = _testerhelp('include') + self.exc = _testerhelp('exclude') + self.ui = MkDistrUI(self) + self.ui.mainloop() + sys.exit(1) + + def check(self): + print 'CHECK' + return ['rv1', 'rv2'] + + def run(self): + print 'RUN' + +if __name__ == '__main__': + _test() -- cgit v0.12