blob: c132200351f5e60dff11763c62a3943c60afc67b [file] [log] [blame]
Jack Jansenc9c99f21995-08-31 13:50:16 +00001#
2# Interactively decide what to distribute
3#
Jack Jansenc9c99f21995-08-31 13:50:16 +00004#
5# The exclude file signals files to always exclude,
Jack Jansen86288971996-08-28 14:18:58 +00006# The pattern file lines are of the form
7# *.c
8# This excludes all files ending in .c.
Jack Jansenc9c99f21995-08-31 13:50:16 +00009#
10# The include file signals files and directories to include.
11# Records are of the form
Jack Jansen86288971996-08-28 14:18:58 +000012# ('Tools:bgen:AE:AppleEvents.py', 'Lib:MacToolbox:AppleEvents.py')
13# This includes the specified file, putting it in the given place, or
14# ('Tools:bgen:AE:AppleEvents.py', None)
15# This excludes the specified file.
Jack Jansenc9c99f21995-08-31 13:50:16 +000016#
17from MkDistr_ui import *
18import fnmatch
19import regex
20import os
21import sys
22import macfs
23import macostools
24
25SyntaxError='Include/exclude file syntax error'
26
27class Matcher:
28 """Include/exclude database, common code"""
29
Jack Jansen86288971996-08-28 14:18:58 +000030 def __init__(self, filename):
Jack Jansenc9c99f21995-08-31 13:50:16 +000031 self.filename = filename
32 self.rawdata = []
33 self.parse(filename)
34 self.rawdata.sort()
35 self.rebuild()
36 self.modified = 0
37
38 def parse(self, dbfile):
39 try:
40 fp = open(dbfile)
41 except IOError:
42 return
43 data = fp.readlines()
44 fp.close()
45 for d in data:
46 d = d[:-1]
47 if not d or d[0] == '#': continue
48 pat = self.parseline(d)
49 self.rawdata.append(pat)
50
Jack Jansenc9c99f21995-08-31 13:50:16 +000051 def save(self):
52 fp = open(self.filename, 'w')
Jack Jansen86288971996-08-28 14:18:58 +000053 self.savedata(fp, self.rawdata)
Jack Jansenc9c99f21995-08-31 13:50:16 +000054 self.modified = 0
55
56 def add(self, value):
Jack Jansen86288971996-08-28 14:18:58 +000057 if len(value) == 1:
Jack Jansenc9c99f21995-08-31 13:50:16 +000058 value = value + ('',)
59 self.rawdata.append(value)
60 self.rebuild1(value)
61 self.modified = 1
62
63 def delete(self, value):
64 key = value
65 for i in range(len(self.rawdata)):
Jack Jansen86288971996-08-28 14:18:58 +000066 if self.rawdata[i][0] == key:
Jack Jansenc9c99f21995-08-31 13:50:16 +000067 del self.rawdata[i]
68 self.unrebuild1(i, key)
69 self.modified = 1
70 return
71 print 'Not found!', key
72
73 def getall(self):
Jack Jansen86288971996-08-28 14:18:58 +000074 return map(lambda x: x[0], self.rawdata)
Jack Jansenc9c99f21995-08-31 13:50:16 +000075
76 def get(self, value):
Jack Jansen86288971996-08-28 14:18:58 +000077 for src, dst in self.rawdata:
Jack Jansenc9c99f21995-08-31 13:50:16 +000078 if src == value:
Jack Jansen86288971996-08-28 14:18:58 +000079 return src, dst
Jack Jansenc9c99f21995-08-31 13:50:16 +000080 print 'Not found!', value
81
82 def is_modified(self):
83 return self.modified
84
85class IncMatcher(Matcher):
86 """Include filename database and matching engine"""
87
88 def rebuild(self):
89 self.idict = {}
90 self.edict = {}
91 for v in self.rawdata:
92 self.rebuild1(v)
93
Jack Jansen86288971996-08-28 14:18:58 +000094 def parseline(self, line):
95 try:
96 data = eval(line)
97 except:
98 raise SyntaxError, line
99 if type(data) <> type(()) or len(data) not in (1,2):
100 raise SyntaxError, line
101 if len(data) == 1:
102 data = data + ('',)
103 return data
104
105 def savedata(self, fp, data):
106 for d in self.rawdata:
107 fp.write(`d`+'\n')
108
109 def rebuild1(self, (src, dst)):
110 if dst == '':
111 dst = src
112 if dst == None:
113 self.edict[src] = None
Jack Jansenc9c99f21995-08-31 13:50:16 +0000114 else:
Jack Jansen86288971996-08-28 14:18:58 +0000115 self.idict[src] = dst
Jack Jansenc9c99f21995-08-31 13:50:16 +0000116
117 def unrebuild1(self, num, src):
118 if self.idict.has_key(src):
119 del self.idict[src]
120 else:
121 del self.edict[src]
122
123 def match(self, patharg):
124 removed = []
125 # First check the include directory
126 path = patharg
127 while 1:
128 if self.idict.has_key(path):
129 # We know of this path (or initial piece of path)
130 dstpath = self.idict[path]
131 # We do want it distributed. Tack on the tail.
132 while removed:
133 dstpath = os.path.join(dstpath, removed[0])
134 removed = removed[1:]
135 # Finally, if the resultant string ends in a separator
136 # tack on our input filename
137 if dstpath[-1] == os.sep:
138 dir, file = os.path.split(path)
Jack Jansen86288971996-08-28 14:18:58 +0000139 dstpath = os.path.join(dstpath, file)
Jack Jansenc9c99f21995-08-31 13:50:16 +0000140 return dstpath
141 path, lastcomp = os.path.split(path)
142 if not path:
143 break
144 removed[0:0] = [lastcomp]
145 # Next check the exclude directory
146 path = patharg
147 while 1:
148 if self.edict.has_key(path):
149 return ''
150 path, lastcomp = os.path.split(path)
151 if not path:
152 break
153 removed[0:0] = [lastcomp]
154 return None
155
156 def checksourcetree(self):
157 rv = []
158 for name in self.idict.keys():
159 if not os.path.exists(name):
160 rv.append(name)
161 return rv
162
163class ExcMatcher(Matcher):
164 """Exclude pattern database and matching engine"""
165
166 def rebuild(self):
167 self.relist = []
168 for v in self.rawdata:
169 self.rebuild1(v)
170
Jack Jansen86288971996-08-28 14:18:58 +0000171 def parseline(self, data):
172 return (data, None)
173
174 def savedata(self, fp, data):
175 for d in self.rawdata:
176 fp.write(d[0]+'\n')
177
178 def rebuild1(self, (src, dst)):
179 pat = fnmatch.translate(src)
180 self.relist.append(regex.compile(pat))
181
Jack Jansenc9c99f21995-08-31 13:50:16 +0000182 def unrebuild1(self, num, src):
183 del self.relist[num]
184
185 def match(self, path):
186 comps = os.path.split(path)
187 file = comps[-1]
188 for pat in self.relist:
189 if pat and pat.match(file) == len(file):
190 return 1
191 return 0
192
193
194class Main:
195 """The main program glueing it all together"""
196
197 def __init__(self):
198 InitUI()
199 fss, ok = macfs.GetDirectory('Source directory:')
200 if not ok:
201 sys.exit(0)
202 os.chdir(fss.as_pathname())
Jack Jansen230ad2c1996-10-23 15:52:56 +0000203 if not os.path.isdir(':Mac:Distributions'):
204 os.mkdir(':Mac:Distributions')
Jack Jansen86288971996-08-28 14:18:58 +0000205 typedist = GetType()
Jack Jansen230ad2c1996-10-23 15:52:56 +0000206 self.inc = IncMatcher(':Mac:Distributions:%s.include'%typedist)
207 self.exc = ExcMatcher(':Mac:Distributions:%s.exclude'%typedist)
Jack Jansenc9c99f21995-08-31 13:50:16 +0000208 self.ui = MkDistrUI(self)
209 self.ui.mainloop()
210
211 def check(self):
212 return self.checkdir(':', 1)
213
214 def checkdir(self, path, istop):
215 files = os.listdir(path)
216 rv = []
217 todo = []
218 for f in files:
219 if self.exc.match(f):
220 continue
221 fullname = os.path.join(path, f)
222 if self.inc.match(fullname) == None:
223 if os.path.isdir(fullname):
224 todo.append(fullname)
225 else:
226 rv.append(fullname)
227 for d in todo:
Jack Jansen6f69c501997-05-23 15:41:15 +0000228 if len(rv) > 500:
Jack Jansenc9c99f21995-08-31 13:50:16 +0000229 if istop:
230 rv.append('... and more ...')
231 return rv
232 rv = rv + self.checkdir(d, 0)
233 return rv
234
235 def run(self, destprefix):
236 missing = self.inc.checksourcetree()
237 if missing:
238 print '==== Missing source files ===='
239 for i in missing:
240 print i
241 print '==== Fix and retry ===='
242 return
243 if not self.rundir(':', destprefix, 0):
244 return
245 self.rundir(':', destprefix, 1)
246
247 def rundir(self, path, destprefix, doit):
248 files = os.listdir(path)
249 todo = []
250 rv = 1
251 for f in files:
252 if self.exc.match(f):
253 continue
254 fullname = os.path.join(path, f)
255 if os.path.isdir(fullname):
256 todo.append(fullname)
257 else:
258 dest = self.inc.match(fullname)
259 if dest == None:
260 print 'Not yet resolved:', fullname
261 rv = 0
262 if dest:
263 if doit:
264 print 'COPY ', fullname
265 print ' -> ', os.path.join(destprefix, dest)
266 macostools.copy(fullname, os.path.join(destprefix, dest), 1)
267 for d in todo:
268 if not self.rundir(d, destprefix, doit):
269 rv = 0
270 return rv
271
272 def save(self):
273 self.inc.save()
274 self.exc.save()
275
276 def is_modified(self):
277 return self.inc.is_modified() or self.exc.is_modified()
278
279if __name__ == '__main__':
280 Main()
281