/[soft]/mga-gnome/trunk/mga-gnome
ViewVC logotype

Contents of /mga-gnome/trunk/mga-gnome

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3037 - (show annotations) (download)
Thu Feb 23 19:43:09 2012 UTC (12 years, 7 months ago) by ovitters
File size: 14897 byte(s)
nicer output
1 #!/usr/bin/python
2
3 import os
4 import os.path
5 import sys
6 import re
7 import subprocess
8 import urllib2
9 import urlparse
10 import argparse
11 import errno
12 import tempfile
13 import shutil
14 from sgmllib import SGMLParser
15
16 MEDIA="Core Release Source"
17 URL="http://download.gnome.org/sources/"
18 PKGROOT='~/pkgs'
19
20 re_version = re.compile(r'([-.]|\d+|[^-.\d]+)')
21
22 def version_cmp(a, b):
23 """Compares two versions
24
25 Returns
26 -1 if a < b
27 0 if a == b
28 1 if a > b
29
30 Logic from Bugzilla::Install::Util::vers_cmp"""
31 A = re_version.findall(a.lstrip('0'))
32 B = re_version.findall(b.lstrip('0'))
33
34 while A and B:
35 a = A.pop(0)
36 b = B.pop(0)
37
38 if a == b:
39 continue
40 elif a == '-':
41 return -1
42 elif b == '-':
43 return 1
44 elif a == '.':
45 return -1
46 elif b == '.':
47 return 1
48 elif a.isdigit() and b.isdigit():
49 c = cmp(a, b) if (a.startswith('0') or b.startswith('0')) else cmp(int(a, 10), int(b, 10))
50 if c:
51 return c
52 else:
53 c = cmp(a.upper(), b.upper())
54 if c:
55 return c
56
57 return cmp(len(A), len(B))
58
59 def get_latest_version(versions, max_version=None):
60 """Gets the latest version number
61
62 if max_version is specified, gets the latest version number before
63 max_version"""
64 latest = None
65 for version in versions:
66 if ( latest is None or version_cmp(version, latest) > 0 ) \
67 and ( max_version is None or version_cmp(version, max_version) < 0 ):
68 latest = version
69 return latest
70
71 def line_input (file):
72 for line in file:
73 if line[-1] == '\n':
74 yield line[:-1]
75 else:
76 yield line
77
78 def call_editor(filename):
79 """Return a sequence of possible editor binaries for the current platform"""
80
81 editors = []
82
83 for varname in 'VISUAL', 'EDITOR':
84 if varname in os.environ:
85 editors.append(os.environ[varname])
86
87 editors.extend(('/usr/bin/editor', 'vi', 'pico', 'nano', 'joe'))
88
89 for editor in editors:
90 try:
91 ret = subprocess.call([editor, filename])
92 except OSError, e:
93 if e.errno == 2:
94 continue
95 raise
96
97 if ret == 127:
98 continue
99
100 return True
101
102 class urllister(SGMLParser):
103 def reset(self):
104 SGMLParser.reset(self)
105 self.urls = []
106
107 def start_a(self, attrs):
108 href = [v for k, v in attrs if k=='href']
109 if href:
110 self.urls.extend(href)
111
112 class SpecFile(object):
113 re_update_version = re.compile(r'^(?P<pre>Version:\s*)(?P<version>.+)(?P<post>\s*)$', re.MULTILINE + re.IGNORECASE)
114 re_update_release = re.compile(r'^(?P<pre>Release:\s*)(?P<release>%mkrel \d+)(?P<post>\s*)$', re.MULTILINE + re.IGNORECASE)
115
116 def __init__(self, path):
117 self.path = path
118 self.cwd = os.path.dirname(path)
119
120 @property
121 def version(self):
122 return subprocess.check_output(["rpm", "--specfile", self.path, "--queryformat", "%{VERSION}\n"]).splitlines()[0]
123
124 def update(self, version):
125 """Update specfile (increase version)"""
126 cur_version = self.version
127
128 compare = version_cmp(version, cur_version)
129
130 if compare == 0:
131 print >>sys.stderr, "ERROR: Already at version %s!" % (cur_version)
132 return False
133
134 if compare != 1:
135 print >>sys.stderr, "ERROR: Version %s is older than current version %s!" % (version, cur_version)
136 return False
137
138 with open(self.path, "rw") as f:
139 data = f.read()
140
141 if data.count("%mkrel") != 1:
142 print >>sys.stderr, "ERROR: Multiple %mkrel found; don't know what to do!"
143 return False
144
145 data, nr = self.re_update_version.subn(r'\g<pre>%s\g<post>' % version, data, 1)
146 if nr != 1:
147 print >>sys.stderr, "ERROR: Could not increase version!"
148 return False
149
150 data, nr = self.re_update_release.subn(r'\g<pre>%mkrel 1\g<post>', data, 1)
151 if nr != 1:
152 print >>sys.stderr, "ERROR: Could not reset release!"
153 return False
154
155 # Overwrite file with new version number
156 write_file(self.path, data)
157
158
159 # Check RPM also agrees that version number has increased
160 if self.version != version:
161 print "ERROR: Increased version to %s, but RPM doesn't agree!?!" % version
162 return False
163
164 try:
165 # Download new tarball
166 subprocess.check_call(['mgarepo', 'sync', '-d'], cwd=self.cwd)
167 # Check patches still apply
168 subprocess.check_call(['bm', '-p', '--nodeps'], cwd=self.cwd)
169 except subprocess.CalledProcessError:
170 return False
171
172 return True
173
174 class Patch(object):
175 """Do things with patches"""
176
177 re_dep3 = re.compile(r'^(?:#\s*)?(?P<header>[-A-Za-z0-9]+?):\s*(?P<data>.*)$')
178 re_dep3_cont = re.compile(r'^#?\s+(?P<data>.*)$')
179
180 def __init__(self, path, show_path=False):
181 """Path: path to patch (might not exist)"""
182 self.path = path
183 self.show_path = show_path
184
185 def __str__(self):
186 return self.path if self.show_path else os.path.basename(self.path)
187
188 def add_dep3(self):
189 """Add DEP-3 headers to a patch file"""
190 if self.dep3['valid']:
191 return False
192
193 new_headers = (
194 ('Author', self.svn_author),
195 ('Subject', ''),
196 ('Applied-Upstream', ''),
197 ('Forwarded', ''),
198 ('Bug', ''),
199 )
200
201 with tempfile.NamedTemporaryFile(dir=os.path.dirname(self.path), delete=False) as fdst:
202 with open(self.path, "r") as fsrc:
203 # Start with any existing DEP3 headers
204 for i in range(self.dep3['last_nr']):
205 fdst.write(fsrc.read())
206
207 # After that add the DEP3 headers
208 add_line = False
209 for header, data in new_headers:
210 if header in self.dep3['headers']:
211 continue
212
213 # XXX - wrap this at 80 chars
214 add_line = True
215 print >>fdst, "%s: %s" % (header, "" if data is None else data)
216
217 if add_line: print >>fdst, ""
218 # Now copy any other data and the patch
219 shutil.copyfileobj(fsrc, fdst)
220
221 fdst.flush()
222 os.rename(fdst.name, self.path)
223
224 call_editor(self.path)
225
226 #Author: fwang
227 #Subject: Build fix: Fix glib header inclusion
228 #Applied-Upstream: commit:30602
229 #Forwarded: yes
230 #Bug: http://bugzilla.abisource.com/show_bug.cgi?id=13247
231
232 def _read_dep3(self):
233 """Read DEP-3 headers from an existing patch file
234
235 This will also parse git headers"""
236 dep3 = {}
237 headers = {}
238
239 last_header = None
240 last_nr = 0
241 nr = 0
242 try:
243 with open(self.path, "r") as f:
244 for line in line_input(f):
245 nr += 1
246 # stop trying to parse when real patch begins
247 if line == '---':
248 break
249
250 r = self.re_dep3.match(line)
251 if r:
252 info = r.groupdict()
253
254 # Avoid matching URLS
255 if info['data'].startswith('//') and info['header'].lower () == info['header']:
256 continue
257
258 headers[info['header']] = info['data']
259 last_header = info['header']
260 last_nr = nr
261 continue
262
263 r = self.re_dep3_cont.match(line)
264 if r:
265 info = r.groupdict()
266 if last_header:
267 headers[last_header] = " ".join((headers[last_header], info['data']))
268 last_nr = nr
269 continue
270
271 last_header = None
272 except IOError:
273 pass
274
275 dep3['valid'] = \
276 (('Description' in headers and headers['Description'].strip() != '')
277 or ('Subject' in headers and headers['Subject'].strip() != '')) \
278 and (('Origin' in headers and headers['Origin'].strip() != '') \
279 or ('Author' in headers and headers['Author'].strip() != '') \
280 or ('From' in headers and headers['From'].strip() != ''))
281 dep3['last_nr'] = last_nr
282 dep3['headers'] = headers
283
284 self._dep3 = dep3
285
286 @property
287 def dep3(self):
288 if not hasattr(self, '_dep3'):
289 self._read_dep3()
290
291 return self._dep3
292
293 @property
294 def svn_author(self):
295 if not hasattr(self, '_svn_author'):
296 p = subprocess.Popen(['svn', 'log', '-q', "--", self.path], stdout=subprocess.PIPE, close_fds=True)
297 contents = p.stdout.read().strip("\n").splitlines()
298 ecode = p.wait()
299 if ecode == 0:
300 for line in contents:
301 if ' | ' not in line:
302 continue
303
304 fields = line.split(' | ')
305 if len(fields) >= 3:
306 self._svn_author = fields[1]
307
308 if not hasattr(self, '_svn_author'):
309 return None
310
311 return self._svn_author
312
313 def get_upstream_names():
314 urlopen = urllib2.build_opener()
315
316 good_dir = re.compile('^[-A-Za-z0-9_+.]+/$')
317
318 # Get the files
319 usock = urlopen.open(URL)
320 parser = urllister()
321 parser.feed(usock.read())
322 usock.close()
323 parser.close()
324 files = parser.urls
325
326 tarballs = set([filename.replace('/', '') for filename in files if good_dir.search(filename)])
327
328 return tarballs
329
330 def get_downstream_names():
331 re_file = re.compile(r'^(?P<module>.*?)[_-](?:(?P<oldversion>([0-9]+[\.])*[0-9]+)-)?(?P<version>([0-9]+[\.\-])*[0-9]+)\.(?P<format>(?:tar\.|diff\.)?[a-z][a-z0-9]*)$')
332
333 p = subprocess.Popen(['urpmf', '--files', '.', "--media", MEDIA], stdout=subprocess.PIPE, close_fds=True)
334 contents = p.stdout.read().strip("\n").splitlines()
335 ecode = p.wait()
336 if ecode != 0:
337 sys.exit(1)
338
339 FILES = {}
340 TARBALLS = {}
341
342 for line in contents:
343 try:
344 srpm, filename = line.split(":")
345 except ValueError:
346 print >>sys.stderr, line
347 continue
348
349 if '.tar' in filename:
350 r = re_file.match(filename)
351 if r:
352 fileinfo = r.groupdict()
353 module = fileinfo['module']
354
355 if module not in TARBALLS:
356 TARBALLS[module] = set()
357 TARBALLS[module].add(srpm)
358
359 if srpm not in FILES:
360 FILES[srpm] = set()
361 FILES[srpm].add(filename)
362
363 return TARBALLS, FILES
364
365
366 def write_file(path, data):
367 with tempfile.NamedTemporaryFile(dir=os.path.dirname(path), delete=False) as fdst:
368 fdst.write(data)
369 fdst.flush()
370 os.rename(fdst.name, path)
371
372 def cmd_co(options, parser):
373 upstream = get_upstream_names()
374 downstream, downstream_files = get_downstream_names()
375
376 cwd = os.path.expanduser(PKGROOT)
377
378 matches = upstream & set(downstream.keys())
379 for module in matches:
380 print module, "\t".join(downstream[module])
381 for package in downstream[module]:
382 subprocess.call(['mgarepo', 'co', package], cwd=cwd)
383
384 def cmd_ls(options, parser):
385 upstream = get_upstream_names()
386 downstream, downstream_files = get_downstream_names()
387
388 matches = upstream & set(downstream.keys())
389 for module in matches:
390 print "\n".join(downstream[module])
391
392 def cmd_patches(options, parser):
393 upstream = get_upstream_names()
394 downstream, downstream_files = get_downstream_names()
395
396 path = os.path.expanduser(PKGROOT)
397
398 import pprint
399
400 matches = upstream & set(downstream.keys())
401 for module in sorted(matches):
402 for srpm in downstream[module]:
403 for filename in downstream_files[srpm]:
404 if '.patch' in filename or '.diff' in filename:
405
406 p = Patch(os.path.join(path, srpm, "SOURCES", filename), show_path=options.path)
407 valid = ""
408 forwarded = ""
409 if p.dep3['headers']:
410 forwarded = p.dep3['headers'].get('Forwarded', "no")
411 if p.dep3['valid']:
412 valid="VALID"
413 print "\t".join((module, srpm, str(p), forwarded, valid))
414
415 def cmd_dep3(options, parser):
416 p = Patch(options.patch)
417 p.add_dep3()
418
419 def cmd_package_new_version(options, parser):
420 cwd = os.path.expanduser(PKGROOT)
421 package = options.package
422
423 subprocess.call(['mgarepo', 'co', package], cwd=cwd)
424 s = SpecFile(os.path.join(cwd, package, "SPECS", "%s.spec" % package))
425 print "%s => %s" % (s.version, options.version)
426 if not s.update(options.version):
427 sys.exit(1)
428
429
430 def main():
431 description = """Mageia GNOME commands."""
432 epilog="""Report bugs to Olav Vitters"""
433 parser = argparse.ArgumentParser(description=description,epilog=epilog)
434
435 # SUBPARSERS
436 subparsers = parser.add_subparsers(title='subcommands')
437 # install
438 subparser = subparsers.add_parser('co', help='checkout all GNOME modules')
439 subparser.set_defaults(
440 func=cmd_co
441 )
442
443 subparser = subparsers.add_parser('packages', help='list all GNOME packages')
444 subparser.set_defaults(
445 func=cmd_ls
446 )
447
448 subparser = subparsers.add_parser('patches', help='list all GNOME patches')
449 subparser.add_argument("-p", "--path", action="store_true", dest="path",
450 help="Show full path to patch")
451 subparser.set_defaults(
452 func=cmd_patches, path=False
453 )
454
455 subparser = subparsers.add_parser('dep3', help='Add dep3 headers')
456 subparser.add_argument("patch", help="Patch")
457 subparser.set_defaults(
458 func=cmd_dep3, path=False
459 )
460
461 subparser = subparsers.add_parser('increase', help='Increase version number')
462 subparser.add_argument("package", help="Package name")
463 subparser.add_argument("version", help="Version number")
464 subparser.set_defaults(
465 func=cmd_package_new_version, path=False
466 )
467
468 if len(sys.argv) == 1:
469 parser.print_help()
470 sys.exit(2)
471
472 options = parser.parse_args()
473
474 try:
475 options.func(options, parser)
476 except KeyboardInterrupt:
477 print('Interrupted')
478 sys.exit(1)
479 except EOFError:
480 print('EOF')
481 sys.exit(1)
482 except IOError, e:
483 if e.errno != errno.EPIPE:
484 raise
485 sys.exit(0)
486
487 if __name__ == "__main__":
488 main()

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.30