rpm refactor: rewrite set_tag functionality
authorMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Thu, 3 Jan 2013 10:43:08 +0000 (12:43 +0200)
committerMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Tue, 7 Jan 2014 14:21:30 +0000 (16:21 +0200)
In addition to refactoring, make sure that we keep the internal _tags
structure in sync.

Changes the default place for new tags: add new tags after the 'Name:'
tag instead of 'Release:'.

Signed-off-by: Markus Lehtonen <markus.lehtonen@linux.intel.com>
gbp/rpm/__init__.py
gbp/scripts/buildpackage_rpm.py
tests/test_rpm.py
tests/test_rpm_data/specs/gbp-test-reference2.spec
tests/test_rpm_data/specs/gbp-test-updates-reference.spec [new file with mode: 0644]
tests/test_rpm_data/specs/gbp-test-updates.spec [new file with mode: 0644]

index 1cdc3b7f60d67832fa9432c6099e383dac457ea7..a384da72dc6645f471c9ee6560e9fa70776812bb 100644 (file)
@@ -441,24 +441,85 @@ class SpecFile(object):
             if patchnum in self.ignorepatches:
                 self.patches[patchnum]['autoupdate'] = False
 
-    def set_tag(self, tag, value):
+    def _delete_tag(self, tag, num):
+        """Delete a tag"""
+        key = tag.lower()
+        tagname = '%s%s' % (tag, num) if num is not None else tag
+        if key not in self._tags:
+            gbp.log.warn("Trying to delete non-existent tag '%s:'" % tag)
+            return None
+
+        sparedlines = []
+        prev = None
+        for line in self._tags[key]['lines']:
+            if line['num'] == num:
+                gbp.log.debug("Removing '%s:' tag from spec" % tagname)
+                prev = self._content.delete(line['line'])
+            else:
+                sparedlines.append(line)
+        self._tags[key]['lines'] = sparedlines
+        if not self._tags[key]['lines']:
+            self._tags.pop(key)
+        return prev
+
+    def _set_tag(self, tag, num, value, insertafter):
+        """Set a tag value"""
+        key = tag.lower()
+        tagname = '%s%s' % (tag, num) if num is not None else tag
+        value = value.strip()
+        if not value:
+            raise GbpError("Cannot set empty value to '%s:' tag" % tag)
+
+        # Check type of tag, we don't support values for 'multivalue' tags
+        try:
+            header = self._specinfo.packages[0].header
+            tagvalue = header[getattr(rpm, 'RPMTAG_%s' % tagname.upper())]
+        except AttributeError:
+            tagvalue = None
+        tagvalue = None if type(tagvalue) is list else value
+
+        # Try to guess the correct indentation from the previous or next tag
+        indent_re = re.compile(r'^([a-z]+([0-9]+)?\s*:\s*)', flags=re.I)
+        match = indent_re.match(str(insertafter))
+        if not match:
+            match = indent_re.match(str(insertafter.next))
+        indent = 12 if not match else len(match.group(1))
+        text = '%-*s%s\n' % (indent, '%s:' % tagname, value)
+        if key in self._tags:
+            self._tags[key]['value'] = tagvalue
+            for line in reversed(self._tags[key]['lines']):
+                if line['num'] == num:
+                    gbp.log.debug("Updating '%s:' tag in spec" % tagname)
+                    line['line'].set_data(text)
+                    line['linevalue'] = value
+                    return line['line']
+
+        gbp.log.debug("Adding '%s:' tag after '%s...' line in spec" %
+                      (tagname, str(insertafter)[0:20]))
+        line = self._content.insert_after(insertafter, text)
+        linerec = {'line': line, 'num': num, 'linevalue': value}
+        if key in self._tags:
+            self._tags[key]['lines'].append(linerec)
+        else:
+            self._tags[key] = {'value': tagvalue, 'lines': [linerec]}
+        return line
+
+    def set_tag(self, tag, num, value, insertafter=None):
         """Update a tag in spec file content"""
         key = tag.lower()
-        if key == 'vcs':
+        tagname = '%s%s' % (tag, num) if num is not None else tag
+        if key in ('patch', 'vcs'):
+            if key in self._tags:
+                insertafter = key
+            elif not insertafter in self._tags:
+                insertafter = 'name'
+            after_line = self._tags[insertafter]['lines'][-1]['line']
             if value:
-                text = '%-12s%s\n' % ('VCS:', value)
-                if key in self._tags:
-                    gbp.log.info("Updating '%s' tag in spec" % tag)
-                    self._tags[key]['lines'][-1]['line'].set_data(text)
-                else:
-                    gbp.log.info("Adding '%s' tag to spec" % tag)
-                    self._content.insert_after(
-                        self._tags['release']['lines'][-1]['line'], text)
+                self._set_tag(tag, num, value, after_line)
             elif key in self._tags:
-                gbp.log.info("Removing '%s' tag from spec" % tag)
-                self._content.delete(self._tags[key]['lines'][-1]['line'])
+                self._delete_tag(tag, num)
         else:
-            raise GbpError("Setting '%s:' tag not supported")
+            raise GbpError("Setting '%s:' tag not supported" % tagname)
 
     def update_patches(self, patchfilenames):
         """Update spec with new patch tags and patch macros"""
index f63e9e5de68109133babdb85a416767268bbb5a3..75643fc4ee2878aa3fca3bafa03b294362382d00 100755 (executable)
@@ -603,7 +603,7 @@ def main(argv):
                                           abbrev=40) + '-dirty'
                 commit_sha1 = repo.rev_parse('HEAD') + '-dirty'
         # Put 'VCS:' tag to .spec
-        spec.set_tag('vcs',
+        spec.set_tag('VCS', None,
                      options.spec_vcs_tag % {'tagname': tree_name,
                                              'commit': commit_sha1})
         spec.write_spec_file()
index 55bb415931cb7dc8ffae125b87cc182d95834039..88eb571fc2d90f6bb957742362ec4812a3d3c3ee 100644 (file)
@@ -30,6 +30,14 @@ DATA_DIR = os.path.abspath(os.path.splitext(__file__)[0] + '_data')
 SRPM_DIR = os.path.join(DATA_DIR, 'srpms')
 SPEC_DIR = os.path.join(DATA_DIR, 'specs')
 
+class SpecFileTester(SpecFile):
+    """Helper class for testing"""
+
+    def protected(self, name):
+        """Get a protected member"""
+        return super(SpecFileTester, self).__getattribute__(name)
+
+
 class TestSrcRpmFile(object):
     """Test L{gbp.rpm.SrcRpmFile}"""
 
@@ -77,7 +85,7 @@ class TestSpecFile(object):
     def test_spec(self):
         """Test parsing of a valid spec file"""
         spec_filepath = os.path.join(SPEC_DIR, 'gbp-test.spec')
-        spec = SpecFile(spec_filepath)
+        spec = SpecFileTester(spec_filepath)
 
         # Test basic properties
         assert spec.specfile == spec_filepath
@@ -154,7 +162,7 @@ class TestSpecFile(object):
 
         # Test adding the VCS tag
         reference_spec = os.path.join(SPEC_DIR, 'gbp-test-reference2.spec')
-        spec.set_tag('vcs', 'myvcstag')
+        spec.set_tag('VCS', None, 'myvcstag')
         spec.write_spec_file()
         assert filecmp.cmp(tmp_spec, reference_spec) is True
 
@@ -166,14 +174,41 @@ class TestSpecFile(object):
         reference_spec = os.path.join(SPEC_DIR, 'gbp-test2-reference2.spec')
         spec = SpecFile(tmp_spec)
         spec.update_patches(['1.patch', '2.patch'])
-        spec.set_tag('vcs', 'myvcstag')
+        spec.set_tag('VCS', None, 'myvcstag')
         spec.update_patches(['new.patch'])
         spec.write_spec_file()
         assert filecmp.cmp(tmp_spec, reference_spec) is True
 
         # Test removing the VCS tag
         reference_spec = os.path.join(SPEC_DIR, 'gbp-test2-reference.spec')
-        spec.set_tag('vcs', '')
+        spec.set_tag('VCS', None, '')
+        spec.write_spec_file()
+        assert filecmp.cmp(tmp_spec, reference_spec) is True
+
+    def test_modifying(self):
+        """Test updating/deleting of tags and macros"""
+        tmp_spec = os.path.join(self.tmpdir, 'gbp-test.spec')
+        shutil.copy2(os.path.join(SPEC_DIR, 'gbp-test-updates.spec'), tmp_spec)
+        reference_spec = os.path.join(SPEC_DIR,
+                                      'gbp-test-updates-reference.spec')
+        spec = SpecFileTester(tmp_spec)
+
+        # Mangle tags
+        prev = spec.protected('_delete_tag')('Vendor', None)
+        spec.protected('_set_tag')('License', None, 'new license', prev)
+        spec.protected('_delete_tag')('source', 0)
+        spec.protected('_delete_tag')('patch', 0)
+        spec.protected('_delete_tag')('patch', -1)
+        prev = spec.protected('_delete_tag')('invalidtag', None)
+
+        with assert_raises(GbpError):
+            # Check that setting empty value fails
+            spec.protected('_set_tag')('Version', None, '', prev)
+        with assert_raises(GbpError):
+            # Check that setting invalid tag with public method fails
+            spec.set_tag('invalidtag', None, 'value')
+
+        # Check resulting spec file
         spec.write_spec_file()
         assert filecmp.cmp(tmp_spec, reference_spec) is True
 
index f006fc77da4a0cef84dc30dda11aef9cb294714d..1cd922b27943d10cd7c64ea6bc061b4f7249f99c 100644 (file)
@@ -1,8 +1,8 @@
 Name:       gbp-test
+VCS:        myvcstag
 Summary:    Test package for git-buildpackage
 Version:    1.0
 Release:    1
-VCS:        myvcstag
 Group:      Development/Libraries
 License:    GPLv2
 Source:     %{name}-%{version}.tar.bz2
diff --git a/tests/test_rpm_data/specs/gbp-test-updates-reference.spec b/tests/test_rpm_data/specs/gbp-test-updates-reference.spec
new file mode 100644 (file)
index 0000000..557f8b7
--- /dev/null
@@ -0,0 +1,39 @@
+#
+# Spec file for testing deleting/adding/updating tags and macros
+#
+
+# Gbp-Undefined-Tag: foobar
+
+# Test that we accept different cases
+Name:           my_name
+Version:        0
+Release:        1
+Summary:        my_summary
+License:        new license
+Distribution:   my_distribution
+Group:          my_group
+Packager:       my_packager
+Url:            my_url
+Vcs:            my_vcs
+BuildRoot:      my_buildroot
+Provides:       my_provides
+Requires:       my_requires
+Conflicts:      my_conflicts
+Obsoletes:      my_obsoletes
+BuildConflicts: my_buildconflicts
+BuildRequires:  my_buildrequires
+AutoReqProv:    No
+AutoReq:        No
+AutoProv:       No
+DistTag:        my_disttag
+BugUrl:         my_bugurl
+Collections:    my_collections
+
+%description
+Package for testing GBP.
+
+%prep
+
+%build
+
+%install
diff --git a/tests/test_rpm_data/specs/gbp-test-updates.spec b/tests/test_rpm_data/specs/gbp-test-updates.spec
new file mode 100644 (file)
index 0000000..1cdcb21
--- /dev/null
@@ -0,0 +1,43 @@
+#
+# Spec file for testing deleting/adding/updating tags and macros
+#
+
+# Gbp-Undefined-Tag: foobar
+
+# Test that we accept different cases
+Name:           my_name
+Version:        0
+Release:        1
+Summary:        my_summary
+License:        my_license
+Distribution:   my_distribution
+Vendor:         my_vendor
+Group:          my_group
+Packager:       my_packager
+Url:            my_url
+Vcs:            my_vcs
+Source:         my_source
+Patch:          my_%patch_fn_base
+Patch0:         my_%{patch_fn_base}0
+BuildRoot:      my_buildroot
+Provides:       my_provides
+Requires:       my_requires
+Conflicts:      my_conflicts
+Obsoletes:      my_obsoletes
+BuildConflicts: my_buildconflicts
+BuildRequires:  my_buildrequires
+AutoReqProv:    No
+AutoReq:        No
+AutoProv:       No
+DistTag:        my_disttag
+BugUrl:         my_bugurl
+Collections:    my_collections
+
+%description
+Package for testing GBP.
+
+%prep
+
+%build
+
+%install