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

Diff of /mga-gnome/trunk/mga-gnome

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 2932 by ovitters, Sat Feb 11 17:54:41 2012 UTC revision 3037 by ovitters, Thu Feb 23 19:43:09 2012 UTC
# Line 8  import subprocess Line 8  import subprocess
8  import urllib2  import urllib2
9  import urlparse  import urlparse
10  import argparse  import argparse
11    import errno
12    import tempfile
13    import shutil
14  from sgmllib import SGMLParser  from sgmllib import SGMLParser
15    
16  MEDIA="Core Release Source"  MEDIA="Core Release Source"
17  URL="http://download.gnome.org/sources/"  URL="http://download.gnome.org/sources/"
18  PKGROOT='~/pkgs'  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):  class urllister(SGMLParser):
103      def reset(self):      def reset(self):
104          SGMLParser.reset(self)          SGMLParser.reset(self)
# Line 24  class urllister(SGMLParser): Line 109  class urllister(SGMLParser):
109          if href:          if href:
110              self.urls.extend(href)              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():  def get_upstream_names():
314      urlopen = urllib2.build_opener()      urlopen = urllib2.build_opener()
315    
# Line 76  def get_downstream_names(): Line 362  def get_downstream_names():
362    
363      return TARBALLS, FILES      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):  def cmd_co(options, parser):
373      upstream = get_upstream_names()      upstream = get_upstream_names()
374      downstream, downstream_files = get_downstream_names()      downstream, downstream_files = get_downstream_names()
# Line 102  def cmd_patches(options, parser): Line 395  def cmd_patches(options, parser):
395    
396      path = os.path.expanduser(PKGROOT)      path = os.path.expanduser(PKGROOT)
397    
398        import pprint
399    
400      matches = upstream & set(downstream.keys())      matches = upstream & set(downstream.keys())
401      for module in sorted(matches):      for module in sorted(matches):
402          for srpm in downstream[module]:          for srpm in downstream[module]:
403              for filename in downstream_files[srpm]:              for filename in downstream_files[srpm]:
404                  if '.patch' in filename or '.diff' in filename:                  if '.patch' in filename or '.diff' in filename:
405                      print "\t".join((module,srpm, os.path.join(path, srpm, "SOURCES", filename) if options.path else filename))  
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():  def main():
431      description = """Mageia GNOME commands."""      description = """Mageia GNOME commands."""
# Line 129  def main(): Line 447  def main():
447    
448      subparser = subparsers.add_parser('patches', help='list all GNOME patches')      subparser = subparsers.add_parser('patches', help='list all GNOME patches')
449      subparser.add_argument("-p", "--path", action="store_true", dest="path",      subparser.add_argument("-p", "--path", action="store_true", dest="path",
450                                         help="Full path to patch")                                         help="Show full path to patch")
451      subparser.set_defaults(      subparser.set_defaults(
452          func=cmd_patches, path=False          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:      if len(sys.argv) == 1:
469          parser.print_help()          parser.print_help()

Legend:
Removed from v.2932  
changed lines
  Added in v.3037

  ViewVC Help
Powered by ViewVC 1.1.30