Added ref to Misc/NEWS file; added hint to run regen script on Linux.
[python/dscho.git] / Mac / scripts / MkDistr.py
blobdeda71b20f4992ac28e4ef8b036eb804fd8cbeb2
2 # Interactively decide what to distribute
4 # The distribution type is signalled by a letter. The currently
5 # defined letters are:
6 # p PPC normal distribution
7 # P PPC development distribution
8 # m 68K normal distribution
9 # M 68K development distribution
11 # The exclude file signals files to always exclude,
12 # The pattern file records are of the form
13 # ('pm', '*.c')
14 # This excludes all files ending in .c for normal distributions.
16 # The include file signals files and directories to include.
17 # Records are of the form
18 # ('pPmM', 'Lib')
19 # This includes the Lib dir in all distributions
20 # ('pPmM', 'Tools:bgen:AE:AppleEvents.py', 'Lib:MacToolbox:AppleEvents.py')
21 # This includes the specified file, putting it in the given place.
23 from MkDistr_ui import *
24 import fnmatch
25 import regex
26 import os
27 import sys
28 import macfs
29 import macostools
31 SyntaxError='Include/exclude file syntax error'
33 class Matcher:
34 """Include/exclude database, common code"""
36 def __init__(self, type, filename):
37 self.type = type
38 self.filename = filename
39 self.rawdata = []
40 self.parse(filename)
41 self.rawdata.sort()
42 self.rebuild()
43 self.modified = 0
45 def parse(self, dbfile):
46 try:
47 fp = open(dbfile)
48 except IOError:
49 return
50 data = fp.readlines()
51 fp.close()
52 for d in data:
53 d = d[:-1]
54 if not d or d[0] == '#': continue
55 pat = self.parseline(d)
56 self.rawdata.append(pat)
58 def parseline(self, line):
59 try:
60 data = eval(line)
61 except:
62 raise SyntaxError, line
63 if type(data) <> type(()) or len(data) not in (2,3):
64 raise SyntaxError, line
65 if len(data) == 2:
66 data = data + ('',)
67 return data
69 def save(self):
70 fp = open(self.filename, 'w')
71 for d in self.rawdata:
72 fp.write(`d`+'\n')
73 self.modified = 0
75 def add(self, value):
76 if len(value) == 2:
77 value = value + ('',)
78 self.rawdata.append(value)
79 self.rebuild1(value)
80 self.modified = 1
82 def delete(self, value):
83 key = value
84 for i in range(len(self.rawdata)):
85 if self.rawdata[i][1] == key:
86 del self.rawdata[i]
87 self.unrebuild1(i, key)
88 self.modified = 1
89 return
90 print 'Not found!', key
92 def getall(self):
93 return map(lambda x: x[1], self.rawdata)
95 def get(self, value):
96 for t, src, dst in self.rawdata:
97 if src == value:
98 return t, src, dst
99 print 'Not found!', value
101 def is_modified(self):
102 return self.modified
104 class IncMatcher(Matcher):
105 """Include filename database and matching engine"""
107 def rebuild(self):
108 self.idict = {}
109 self.edict = {}
110 for v in self.rawdata:
111 self.rebuild1(v)
113 def rebuild1(self, (tp, src, dst)):
114 if self.type in tp:
115 if dst == '':
116 dst = src
117 self.idict[src] = dst
118 else:
119 self.edict[src] = ''
121 def unrebuild1(self, num, src):
122 if self.idict.has_key(src):
123 del self.idict[src]
124 else:
125 del self.edict[src]
127 def match(self, patharg):
128 removed = []
129 # First check the include directory
130 path = patharg
131 while 1:
132 if self.idict.has_key(path):
133 # We know of this path (or initial piece of path)
134 dstpath = self.idict[path]
135 # We do want it distributed. Tack on the tail.
136 while removed:
137 dstpath = os.path.join(dstpath, removed[0])
138 removed = removed[1:]
139 # Finally, if the resultant string ends in a separator
140 # tack on our input filename
141 if dstpath[-1] == os.sep:
142 dir, file = os.path.split(path)
143 dstpath = os.path.join(dstpath, path)
144 return dstpath
145 path, lastcomp = os.path.split(path)
146 if not path:
147 break
148 removed[0:0] = [lastcomp]
149 # Next check the exclude directory
150 path = patharg
151 while 1:
152 if self.edict.has_key(path):
153 return ''
154 path, lastcomp = os.path.split(path)
155 if not path:
156 break
157 removed[0:0] = [lastcomp]
158 return None
160 def checksourcetree(self):
161 rv = []
162 for name in self.idict.keys():
163 if not os.path.exists(name):
164 rv.append(name)
165 return rv
167 class ExcMatcher(Matcher):
168 """Exclude pattern database and matching engine"""
170 def rebuild(self):
171 self.relist = []
172 for v in self.rawdata:
173 self.rebuild1(v)
175 def rebuild1(self, (tp, src, dst)):
176 if self.type in tp:
177 pat = fnmatch.translate(src)
178 self.relist.append(regex.compile(pat))
179 else:
180 self.relist.append(None)
182 def unrebuild1(self, num, src):
183 del self.relist[num]
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
194 class Main:
195 """The main program glueing it all together"""
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())
203 self.typedist = GetType()
204 print 'TYPE', self.typedist
205 self.inc = IncMatcher(self.typedist, '(MkDistr.include)')
206 self.exc = ExcMatcher(self.typedist, '(MkDistr.exclude)')
207 self.ui = MkDistrUI(self)
208 self.ui.mainloop()
210 def check(self):
211 return self.checkdir(':', 1)
213 def checkdir(self, path, istop):
214 files = os.listdir(path)
215 rv = []
216 todo = []
217 for f in files:
218 if self.exc.match(f):
219 continue
220 fullname = os.path.join(path, f)
221 if self.inc.match(fullname) == None:
222 if os.path.isdir(fullname):
223 todo.append(fullname)
224 else:
225 rv.append(fullname)
226 for d in todo:
227 if len(rv) > 100:
228 if istop:
229 rv.append('... and more ...')
230 return rv
231 rv = rv + self.checkdir(d, 0)
232 return rv
234 def run(self, destprefix):
235 missing = self.inc.checksourcetree()
236 if missing:
237 print '==== Missing source files ===='
238 for i in missing:
239 print i
240 print '==== Fix and retry ===='
241 return
242 if not self.rundir(':', destprefix, 0):
243 return
244 self.rundir(':', destprefix, 1)
246 def rundir(self, path, destprefix, doit):
247 files = os.listdir(path)
248 todo = []
249 rv = 1
250 for f in files:
251 if self.exc.match(f):
252 continue
253 fullname = os.path.join(path, f)
254 if os.path.isdir(fullname):
255 todo.append(fullname)
256 else:
257 dest = self.inc.match(fullname)
258 if dest == None:
259 print 'Not yet resolved:', fullname
260 rv = 0
261 if dest:
262 if doit:
263 print 'COPY ', fullname
264 print ' -> ', os.path.join(destprefix, dest)
265 macostools.copy(fullname, os.path.join(destprefix, dest), 1)
266 for d in todo:
267 if not self.rundir(d, destprefix, doit):
268 rv = 0
269 return rv
271 def save(self):
272 self.inc.save()
273 self.exc.save()
275 def is_modified(self):
276 return self.inc.is_modified() or self.exc.is_modified()
278 if __name__ == '__main__':
279 Main()