rpm.SpecFile: drop the internal 'patches' structure
authorMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Fri, 11 Jan 2013 14:41:38 +0000 (16:41 +0200)
committerMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Tue, 7 Jan 2014 14:21:30 +0000 (16:21 +0200)
To get rid of duplicate data tracking. Also, add test for testing the
macro expansion of patch and source names.

Also add tests for SpecFile.patchseries.

Signed-off-by: Markus Lehtonen <markus.lehtonen@linux.intel.com>
gbp/rpm/__init__.py
gbp/scripts/import_srpm.py
gbp/scripts/pq_rpm.py
tests/test_rpm.py
tests/test_rpm_data/specs/gbp-test-tags.spec

index 25c449cb69aa6e18fc3b9f79189494cfd3c44c55..3fbab878dc6687b53a6640d9d8d4da509aca65fe 100644 (file)
@@ -152,7 +152,6 @@ class SpecFile(object):
         self.epoch = str(source_header[rpm.RPMTAG_EPOCH]) \
             if source_header[rpm.RPMTAG_EPOCH] != None else None
         self.packager = source_header[rpm.RPMTAG_PACKAGER]
-        self.patches = {}
         self.sources = {}
         self._tags = {}
         self._special_directives = defaultdict(list)
@@ -213,6 +212,12 @@ class SpecFile(object):
             return sorted([int(num) for num in data])
         return []
 
+    def _patches(self):
+        """Get all patch tags as a dict"""
+        if 'patch' not in self._tags:
+            return {}
+        return {patch['num']: patch for patch in self._tags['patch']['lines']}
+
     def _macro_replace(self, matchobj):
         macro_dict = {'name': self.name,
                       'version': self.upstreamversion,
@@ -270,14 +275,6 @@ class SpecFile(object):
         # 'Patch:' tags
         elif tagname == 'patch':
             tagnum = -1 if tagnum is None else tagnum
-            new_patch = {'name': matchobj.group('name').strip(),
-                         'filename': matchobj.group('name'),
-                         'apply': False,
-                         'strip': '0',
-                         'macro_line': None,
-                         'autoupdate': True,
-                         'tag_line': lineobj}
-            self.patches[tagnum] = new_patch
 
         # Record all tag locations
         try:
@@ -308,16 +305,21 @@ class SpecFile(object):
 
         return tagname
 
-    def _parse_directive(self, lineobj):
-        """Parse special directive/scriptlet/macro lines"""
+    @staticmethod
+    def _patch_macro_opts(args):
+        """Parse arguments of the '%patch' macro"""
 
-        # Parser for '%patch' macros
         patchparser = OptionParser()
         patchparser.add_option("-p", dest="strip")
         patchparser.add_option("-s", dest="silence")
         patchparser.add_option("-P", dest="patchnum")
         patchparser.add_option("-b", dest="backup")
         patchparser.add_option("-E", dest="removeempty")
+        arglist = args.split()
+        return patchparser.parse_args(arglist)[0]
+
+    def _parse_directive(self, lineobj):
+        """Parse special directive/scriptlet/macro lines"""
 
         # Parser for '%setup' macros
         setupparser = OptionParser()
@@ -339,19 +341,13 @@ class SpecFile(object):
         # '%patch' macros
         directiveid = None
         if directivename == 'patch':
-            arglist = matchobj.group('args').split()
-            (opts, args) = patchparser.parse_args(arglist)
+            opts = self._patch_macro_opts(matchobj.group('args'))
             if matchobj.group('num'):
                 directiveid = int(matchobj.group('num'))
             elif opts.patchnum:
                 directiveid = int(opts.patchnum)
             else:
                 directiveid = -1
-
-            if opts.strip:
-                self.patches[directiveid]['strip'] = opts.strip
-            self.patches[directiveid]['macro_line'] = lineobj
-            self.patches[directiveid]['apply'] = True
         # '%setup' macros
         elif directivename == 'setup':
             arglist = matchobj.group('args').split()
@@ -418,6 +414,7 @@ class SpecFile(object):
 
         # Update sources info (basically possible macros expanded by rpm)
         # And, double-check that we parsed spec content correctly
+        patches = self._patches()
         for (name, num, typ) in self._specinfo.sources:
             # workaround rpm parsing bug
             if typ == 1 or typ == 9:
@@ -441,16 +438,11 @@ class SpecFile(object):
                 # having number (2^31-1), we use number -1
                 if num >= pow(2,30):
                     num = -1
-                if num in self.patches:
-                    self.patches[num]['filename'] = name
+                if num in patches:
+                    patches[num]['linevalue'] = name
                 else:
                     gbp.log.err("BUG: we didn't correctly parse all 'Patch' tags!")
 
-        # Mark ignored patches
-        for patchnum in self.patches:
-            if patchnum in self.ignorepatches:
-                self.patches[patchnum]['autoupdate'] = False
-
     def _delete_tag(self, tag, num):
         """Delete a tag"""
         key = tag.lower()
@@ -582,7 +574,7 @@ class SpecFile(object):
         macro_prev = None
         ignored = self.ignorepatches
         # Remove 'Patch:̈́' tags
-        for tag in self._tags['patch']['lines']:
+        for tag in self._patches().values():
             if not tag['num'] in ignored:
                 tag_prev = self._delete_tag('patch', tag['num'])
                 # Remove a preceding comment if it seems to originate from GBP
@@ -656,20 +648,26 @@ class SpecFile(object):
             macro_line = self._set_special_macro('patch', patchnum, '-p1',
                                                  macro_line)
 
-    def patchseries(self):
-        """
-        Return patches of the RPM as a gbp patchseries
-        """
+    def patchseries(self, unapplied=False, ignored=False):
+        """Return non-ignored patches of the RPM as a gbp patchseries"""
         series = PatchSeries()
-        patchdir = self.specdir
-        for n, p in sorted(self.patches.iteritems()):
-            if p['autoupdate'] and p['apply']:
-                fname = os.path.basename(p['filename'])
-                series.append(Patch(os.path.join(patchdir, fname),
-                                    strip = int(p['strip'])))
+        if 'patch' in self._tags:
+            tags = self._patches()
+            macros = {macro['id']: macro['args']
+                      for macro in self._special_directives['patch']}
+            ignored = [] if ignored else self.ignorepatches
+
+            for num, tag in sorted(tags.iteritems()):
+                strip = 0
+                if num in macros:
+                    opts = self._patch_macro_opts(macros[num])
+                    strip = int(opts.strip) if opts.strip else 0
+                if (unapplied or (num in macros)) and num not in ignored:
+                    filename = os.path.basename(tag['linevalue'])
+                    series.append(Patch(os.path.join(self.specdir, filename),
+                                        strip=strip))
         return series
 
-
     def guess_orig_file(self):
         """
         Try to guess the name of the primary upstream/source archive
index e57ddcc4de890dad9f20b1d1e681def528d72a64..e404278518ef5e18393de0e6e4a3799b3e8aeeb4 100755 (executable)
@@ -319,8 +319,8 @@ def main(argv):
                 raise
 
         # Need to copy files to the packaging directory given by caller
-        files = [os.path.basename(patch['filename']) \
-                for patch in spec.patches.itervalues()]
+        files = [os.path.basename(patch.path) \
+                for patch in spec.patchseries(unapplied=True, ignored=True)]
         for num, src in spec.sources.iteritems():
             if num != spec.orig_src_num:
                 files.append(src['filename'])
index d86d654a93cde63c74a3309e50a7f34831ee049d..0d5f78d2e708df176d8a1163e3c558cbc729b671 100755 (executable)
@@ -139,17 +139,15 @@ def rm_patch_files(spec):
     marked as not maintained by gbp.
     """
     # Remove all old patches from the spec dir
-    for n, p in spec.patches.iteritems():
-        if p['autoupdate']:
-            f = os.path.join(spec.specdir, p['filename'])
-            gbp.log.debug("Removing '%s'" % f)
-            try:
-                os.unlink(f)
-            except OSError, (e, msg):
-                if e != errno.ENOENT:
-                    raise GbpError, "Failed to remove patch: %s" % msg
-                else:
-                    gbp.log.debug("%s does not exist." % f)
+    for patch in spec.patchseries(unapplied=True):
+        gbp.log.debug("Removing '%s'" % patch.path)
+        try:
+            os.unlink(patch.path)
+        except OSError as err:
+            if err.errno != errno.ENOENT:
+                raise GbpError("Failed to remove patch: %s" % err)
+            else:
+                gbp.log.debug("Patch %s does not exist." % patch.path)
 
 
 def update_patch_series(repo, spec, start, end, options):
index 01d8b27798ab0e940278a45cdd6de22f4b86f738..830e45060fbeeab714acb95ab51ff54b63e3e4c4 100644 (file)
@@ -199,6 +199,7 @@ class TestSpecFile(object):
         spec.protected('_delete_tag')('source', 0)
         spec.protected('_delete_tag')('patch', 0)
         spec.protected('_delete_tag')('patch', -1)
+        assert spec.protected('_patches')() == {}
         prev = spec.protected('_delete_tag')('invalidtag', None)
 
         with assert_raises(GbpError):
@@ -248,8 +249,28 @@ class TestSpecFile(object):
                 assert val['value'] == rval, ("'%s:' is '%s', expecting '%s'" %
                                               (name, val['value'], rval))
             assert spec.ignorepatches == []
+            # Check patch numbers and patch filenames
+            patches = {patch['num']: patch['linevalue'] for patch in
+                       spec.protected('_tags')['patch']['lines']}
+            assert patches == {0: 'my_patch0', -1: 'my_patch'}
 
-            assert spec.patches.keys() == [0, -1]
+    def test_patch_series(self):
+        """Test the getting the patches as a patchseries"""
+        spec_filepath = os.path.join(SPEC_DIR, 'gbp-test-native.spec')
+        spec = SpecFileTester(spec_filepath)
+
+        assert len(spec.patchseries()) == 0
+        spec.update_patches(['1.patch', '2.patch', '3.patch'])
+        assert len(spec.patchseries()) == 3
+        spec.protected('_gbp_tags')['ignore-patches'].append({'args': "0"})
+        spec.update_patches(['4.patch'])
+        assert len(spec.patchseries()) == 1
+        assert len(spec.patchseries(ignored=True)) == 2
+        spec.protected('_delete_special_macro')('patch', 0)
+        assert len(spec.patchseries(ignored=True)) == 1
+        series = spec.patchseries(unapplied=True, ignored=True)
+        assert len(series) == 2
+        assert os.path.basename(series[-1].path) == '4.patch'
 
 
 class TestUtilityFunctions(object):
index 47421a9db592e734ebfeacdb30bbbd95adefc727..ee4c2b94aaa1733888dcc2494363af9f634fc587 100644 (file)
@@ -9,6 +9,9 @@
 
 %define test_arch_os_tags %(test -n "$GBP_SKIP_ARCH_OS_TAGS" && echo 0 || echo 1)
 
+%define source_fn_base source
+%define patch_fn_base patch
+
 # Gbp-Undefined-Tag: foobar
 
 # Test that we accept different cases
@@ -27,8 +30,8 @@ Packager:       my_packager
 Url:            my_url
 Vcs:            my_vcs
 Source:         my_source
-Patch:          my_patch
-Patch0:         my_patch0
+Patch:          my_%patch_fn_base
+Patch0:         my_%{patch_fn_base}0
 Nosource:       0
 Nopatch:        0
 #Icon:           my_icon