Hello community, here is the log from the commit of package spec-cleaner for openSUSE:Factory checked in at 2017-05-17 17:19:22 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/spec-cleaner (Old) and /work/SRC/openSUSE:Factory/.spec-cleaner.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "spec-cleaner" Wed May 17 17:19:22 2017 rev:41 rq:495599 version:0.9.5 Changes: -------- --- /work/SRC/openSUSE:Factory/spec-cleaner/spec-cleaner.changes 2017-04-13 10:45:21.071042089 +0200 +++ /work/SRC/openSUSE:Factory/.spec-cleaner.new/spec-cleaner.changes 2017-05-17 17:20:24.691666785 +0200 @@ -1,0 +2,8 @@ +Wed May 17 12:41:28 UTC 2017 - tchva...@suse.com + +- Version update to 0.9.5: + * More fixes for the dep_parser + * Convert pypy urls to new format + * Start of rpmpreamble cleanup + +------------------------------------------------------------------- Old: ---- spec-cleaner-0.9.4.tar.gz New: ---- spec-cleaner-0.9.5.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ spec-cleaner.spec ++++++ --- /var/tmp/diff_new_pack.n1soEK/_old 2017-05-17 17:20:25.435561871 +0200 +++ /var/tmp/diff_new_pack.n1soEK/_new 2017-05-17 17:20:25.439561308 +0200 @@ -20,7 +20,7 @@ # This is used for Fedora, we need to sync this %{!?py3_ver: %define py3_ver %{python3_version}} Name: spec-cleaner -Version: 0.9.4 +Version: 0.9.5 Release: 0 Summary: .spec file cleaner License: BSD-3-Clause @@ -95,9 +95,11 @@ %{python3_sitelib}/spec_cleaner/rpmhelpers.py %{python3_sitelib}/spec_cleaner/rpminstall.py %{python3_sitelib}/spec_cleaner/rpmpreamble.py +%{python3_sitelib}/spec_cleaner/rpmpreambleelements.py %{python3_sitelib}/spec_cleaner/rpmprep.py %{python3_sitelib}/spec_cleaner/rpmprune.py %{python3_sitelib}/spec_cleaner/rpmregexp.py +%{python3_sitelib}/spec_cleaner/rpmrequirestoken.py %{python3_sitelib}/spec_cleaner/rpmpackage.py %{python3_sitelib}/spec_cleaner/rpmscriplets.py %{python3_sitelib}/spec_cleaner/rpmsection.py ++++++ spec-cleaner-0.9.4.tar.gz -> spec-cleaner-0.9.5.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/.landscape.yaml new/spec-cleaner-spec-cleaner-0.9.5/.landscape.yaml --- old/spec-cleaner-spec-cleaner-0.9.4/.landscape.yaml 2017-04-08 11:05:38.000000000 +0200 +++ new/spec-cleaner-spec-cleaner-0.9.5/.landscape.yaml 2017-05-17 14:22:32.000000000 +0200 @@ -6,3 +6,5 @@ python-targets: - 2 - 3 +pep8: + full: true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/__init__.py new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/__init__.py --- old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/__init__.py 2017-04-08 11:05:38.000000000 +0200 +++ new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/__init__.py 2017-05-17 14:22:32.000000000 +0200 @@ -12,7 +12,7 @@ from .rpmcleaner import RpmSpecCleaner -__version__ = '0.9.4' +__version__ = '0.9.5' def process_args(argv): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/dependency_parser.py new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/dependency_parser.py --- old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/dependency_parser.py 2017-04-08 11:05:38.000000000 +0200 +++ new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/dependency_parser.py 2017-05-17 14:22:32.000000000 +0200 @@ -33,7 +33,7 @@ r')' ) -re_name = re.compile(r'[-A-Za-z0-9_~():.+/*\[\]]+') +re_name = re.compile(r'[-A-Za-z0-9_~():;.+/*\[\]]+') re_version = re.compile(r'[-A-Za-z0-9_~():.+]+') re_spaces = re.compile(r'\s+') re_macro_unbraced = re.compile('%[A-Za-z0-9_]{3,}') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/rpmbuild.py new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/rpmbuild.py --- old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/rpmbuild.py 2017-04-08 11:05:38.000000000 +0200 +++ new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/rpmbuild.py 2017-05-17 14:22:32.000000000 +0200 @@ -16,7 +16,6 @@ return # if user uses cmake/configure directly just recommend him using the macros - # but check on the multiline entry and do not ammend that if not self.minimal: self._comment_macro_calls(line) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/rpmcleaner.py new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/rpmcleaner.py --- old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/rpmcleaner.py 2017-04-08 11:05:38.000000000 +0200 +++ new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/rpmcleaner.py 2017-05-17 14:22:32.000000000 +0200 @@ -92,7 +92,13 @@ self._load_licenses() # Determine if we need to skip the spec self._find_skip_parser() + # set the filemode + self._select_mode() + def _select_mode(self): + """ + Set up input and output based on the options + """ if self.options['output']: self.fout = open(self.options['output'], 'w') elif self.options['inline']: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/rpmhelpers.py new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/rpmhelpers.py --- old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/rpmhelpers.py 2017-04-08 11:05:38.000000000 +0200 +++ new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/rpmhelpers.py 2017-05-17 14:22:32.000000000 +0200 @@ -4,6 +4,7 @@ import os from .fileutils import FileUtils +from .rpmexception import RpmException LICENSES_CHANGES = 'licenses_changes.txt' TEX_CONVERSIONS = 'tex_conversions.txt' @@ -116,6 +117,31 @@ files.close() return groups +def fix_license(value, conversions): + # license ; should be replaced by ands so find it + re_license_semicolon = re.compile(r'\s*;\s*') + # split using 'or', 'and' and parenthesis, ignore empty strings + licenses = [] + for a in re.split(r'(\(|\)| and | or (?!later))', value): + if a != '': + licenses.append(a) + if not licenses: + licenses.append(value) + + for (index, my_license) in enumerate(licenses): + my_license = ' '.join(my_license.split()) + my_license = my_license.replace('ORlater', 'or later') + my_license = my_license.replace('ORsim', 'or similar') + my_license = my_license.rstrip(';') + my_license = re_license_semicolon.sub(' and ', my_license) + if my_license in conversions: + my_license = conversions[my_license] + licenses[index] = my_license + + # create back new string with replaced licenses + s = ' '.join(licenses).replace("( ", "(").replace(" )", ")") + return s + def sort_uniq(seq): def _check_list(x): @@ -161,3 +187,38 @@ seen[marker] = 1 result.append(item) return result + + +def add_group(group): + """ + Flatten the lines of the group from sublits to one simple list + """ + if isinstance(group, str): + return [group] + elif isinstance(group, list): + x = [] + for subgroup in group: + x += add_group(subgroup) + return x + else: + raise RpmException('Unknown type of group in preamble: %s' % type(group)) + + +def find_pkgconfig_statement(elements): + """ + Find pkgconfig() statement in the list and return true if matched + """ + for i in elements: + if 'pkgconfig(' in i and not find_pkgconfig_declaration(elements): + return True + return False + + +def find_pkgconfig_declaration(elements): + """ + Find if there is direct pkgconfig dependency in the paragraph + """ + for i in elements: + if 'pkgconfig ' in i or i.endswith('pkgconfig'): + return True + return False diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/rpmpreamble.py new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/rpmpreamble.py --- old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/rpmpreamble.py 2017-04-08 11:05:38.000000000 +0200 +++ new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/rpmpreamble.py 2017-05-17 14:22:32.000000000 +0200 @@ -1,11 +1,17 @@ # vim: set ts=4 sw=4 et: coding=UTF-8 +import os.path import re +try: + from urllib import parse as urlparse +except ImportError: + import urlparse + from .rpmsection import Section -from .rpmexception import RpmException -from .rpmhelpers import sort_uniq +from .rpmpreambleelements import RpmPreambleElements from .dependency_parser import DependencyParser +from .rpmhelpers import fix_license class RpmPreamble(Section): @@ -30,89 +36,6 @@ line, even if we reorder the lines. """ - category_to_key = { - 'name': 'Name', - 'version': 'Version', - 'release': 'Release', - 'license': 'License', - 'summary': 'Summary', - # The localized summary can contain various values, so it can't be here - 'url': 'Url', - 'group': 'Group', - 'source': 'Source', - 'nosource': 'NoSource', - 'patch': 'Patch', - 'buildrequires': 'BuildRequires', - 'conflicts': 'Conflicts', - 'prereq': 'PreReq', - 'requires': 'Requires', - 'requires_eq': '%requires_eq', - 'recommends': 'Recommends', - 'suggests': 'Suggests', - 'enhances': 'Enhances', - 'supplements': 'Supplements', - # Provides/Obsoletes cannot be part of this since we want to keep them - # mixed, so we'll have to specify the key when needed - 'buildroot': 'BuildRoot', - 'buildarch': 'BuildArch', - 'exclusivearch': 'ExclusiveArch', - 'excludearch': 'ExcludeArch', - } - - categories_order = [ - 'define', - 'bconds', - 'bcond_conditions', - 'name', - 'version', - 'release', - 'summary', - 'summary_localized', - 'license', - 'group', - 'url', - 'source', - 'nosource', - 'patch', - 'buildrequires', - 'requires', - 'requires_eq', - 'prereq', - 'requires_phase', # this is Requires(pre/post/...) - 'recommends', - 'suggests', - 'enhances', - 'supplements', - 'conflicts', - 'provides_obsoletes', - 'buildroot', - 'buildarch', - 'exclusivearch', - 'excludearch', - 'misc', - 'build_conditions', - 'conditions', - ] - - # categories that are sorted based on value in them - categories_with_sorted_package_tokens = [ - 'buildrequires', - 'prereq', - 'requires', - 'requires_eq', - 'recommends', - 'suggests', - 'enhances', - 'supplements', - 'conflicts', - ] - - # categories that are sorted based on key value (eg Patch0 before Patch1) - categories_with_sorted_keyword_tokens = [ - 'source', - 'patch', - ] - def __init__(self, options): Section.__init__(self, options) # Old storage @@ -125,6 +48,7 @@ self._condition_define = False # Is the condition based probably on bcond evaluation self._condition_bcond = False + self.options = options # do we want pkgconfig and others? self.pkgconfig = options['pkgconfig'] self.perl = options['perl'] @@ -142,17 +66,16 @@ # list of allowed groups self.allowed_groups = options['allowed_groups'] # start the object - self._start_paragraph() + self.paragraph = RpmPreambleElements(options) # initialize list of groups that need to pass over conversion fixer - self.categories_with_package_tokens = self.categories_with_sorted_package_tokens[:] + self.categories_with_package_tokens = self.paragraph.categories_with_sorted_package_tokens[:] # these packages actually need fixing after we sent the values to # reorder them self.categories_with_package_tokens.append('provides_obsoletes') # license handling self.subpkglicense = options['subpkglicense'] - self.license = options['license'] - # pkgconfig requirement detection - self.br_pkgconfig_required = False + # modname detection + self.modname = None # simple categories matching self.category_to_re = { @@ -192,186 +115,100 @@ 'prefix': self.reg.re_preamble_prefix, } - def _start_paragraph(self): - self.paragraph = {} - for i in self.categories_order: - self.paragraph[i] = [] - self.current_group = [] - def start_subparagraph(self): - # store the main content and clean up + # Backup the list and start a new one self._oldstore.append(self.paragraph) - self._start_paragraph() + self.paragraph = RpmPreambleElements(self.options) - def _add_group(self, group): + def _prune_ppc_condition(self): """ - Actually store the lines from groups to resulting output + Check if we have ppc64 obsolete and delete it """ - t = type(group) - if t == str: - return [group] - elif t == list: - x = [] - for subgroup in group: - x += self._add_group(subgroup) - return x - else: - raise RpmException('Unknown type of group in preamble: %s' % t) + if not self.minimal and \ + isinstance(self.paragraph.items['conditions'][0], list) and \ + len(self.paragraph.items['conditions']) == 3 and \ + self.paragraph.items['conditions'][0][0] == '# bug437293' and \ + self.paragraph.items['conditions'][1].endswith('64bit'): + self.paragraph.items['conditions'] = [] - def _sort_helper_key(self, a): - t = type(a) - if t == str: - key = a - elif t == list: - # if this is a list then all items except last are comment or whitespace - key = a[-1] - else: - raise RpmException('Unknown type during sort: %s' % t) + PYPI_SOURCE_HOSTS = ("pypi.io", "files.pythonhosted.org", "pypi.python.org") - # Special case is the category grouping where we have to get the number in - # after the value - if self.reg.re_patch.match(key): - match = self.reg.re_patch.match(key) - key = int(match.group(2)) - elif self.reg.re_source.match(key): - match = self.reg.re_source.match(key) - value = match.group(1) - if not value: - value = '0' - key = int(value) - # Put brackety ()-style deps at the end of the list, after all other - elif self.reg.re_brackety_requires.search(key): - key = '1' + key - else: - key = '0' + key - return key + def _fix_pypi_source(self, url): + """ + Check if the source is URL that points to PyPI and if it is, return + the canonical version. + + This function is almost completely self-contained and only processes + the URL structure itself. On PyPI, the structure is predictable. + The only bad thing that can happen is the packager choosing to use + a macro instead of an explicit name of the file. + (which doesn't really make much sense, given that the url contains + the first letter of the name, so that is going to be explicit anyway) + """ + parsed = urlparse.urlparse(url) + if not parsed.scheme: # not a URL + return url + + if parsed.netloc not in self.PYPI_SOURCE_HOSTS: # not pypi + return url + + filename = os.path.basename(parsed.path) + modname = filename[:filename.rfind("-")] + + # TODO the following condition checks if the filename starts with a macro, + # and expects that if it does, the macro is called "modname". This is not + # always the case. It would be better to detect the name of the macro and + # browse local definitions to find its value. + if modname[0] == "%": + if (modname == "%modname" or modname == "%{modname}") \ + and self.modname: + modname = self.modname + else: + # don't know what to do + return url + + return urlparse.urlunparse(('https', 'files.pythonhosted.org', + '/packages/source/{}/{}/{}'.format(modname[0], modname, filename), + '', '', '')) def end_subparagraph(self, endif=False): - lines = self._end_paragraph() - if len(self.paragraph['define']) > 0 or \ - len(self.paragraph['bconds']) > 0: + if not self._oldstore: + nested = False + else: + nested = True + lines = self.paragraph.flatten_output(False, nested) + if len(self.paragraph.items['define']) > 0 or \ + len(self.paragraph.items['bconds']) > 0: self._condition_define = True self.paragraph = self._oldstore.pop(-1) - self.paragraph['conditions'] += lines + self.paragraph.items['conditions'] += lines # If we are on endif we check the condition content # and if we find the defines we put it on top. if endif or not self.condition: - # check if we are doing the ppc64 migration and delete it - if not self.minimal and \ - isinstance(self.paragraph['conditions'][0], list) and \ - len(self.paragraph['conditions']) == 3 and \ - self.paragraph['conditions'][0][0] == '# bug437293' and \ - self.paragraph['conditions'][1].endswith('64bit'): - self.paragraph['conditions'] = [] + self._prune_ppc_condition() if self._condition_define: # If we have define conditions and possible bcond start # we need to put it bellow bcond definitions as otherwise # the switches do not have any effect if self._condition_bcond: - self.paragraph['bcond_conditions'] += self.paragraph['conditions'] - elif len(self.paragraph['define']) == 0: - self.paragraph['bconds'] += self.paragraph['conditions'] + self.paragraph.items['bcond_conditions'] += self.paragraph.items['conditions'] + elif len(self.paragraph.items['define']) == 0: + self.paragraph.items['bconds'] += self.paragraph.items['conditions'] else: - self.paragraph['define'] += self.paragraph['conditions'] + self.paragraph.items['define'] += self.paragraph.items['conditions'] # in case the nested condition contains define we consider all parents # to require to be on top too; if len(self._oldstore) == 0: self._condition_define = False else: - self.paragraph['build_conditions'] += self.paragraph['conditions'] + self.paragraph.items['build_conditions'] += self.paragraph.items['conditions'] # bcond must be reseted when on top and can be set even outside of the # define scope. So reset it here always if len(self._oldstore) == 0: self._condition_bcond = False - self.paragraph['conditions'] = [] - - def _find_pkgconfig_statements(self, listname): - for i in self.paragraph[listname]: - if isinstance(i, str): - if 'pkgconfig(' in i and not self._find_pkgconfig_declarations(listname): - return True - elif isinstance(i, list): - for j in i: - if 'pkgconfig(' in j and not self._find_pkgconfig_declarations(listname): - return True - return False - - def _find_pkgconfig_declarations(self, listname): - for i in self.paragraph[listname]: - if isinstance(i, str): - if 'pkgconfig ' in i or i.endswith('pkgconfig'): - return True - elif isinstance(i, list): - for j in i: - if 'pkgconfig ' in j or j.endswith('pkgconfig'): - return True - return False - - def _end_paragraph(self, needs_license=False): - lines = [] - - # add license to the package if missing and needed - if needs_license: - if not self.paragraph['license']: - self.license = self._fix_license(self.license) - self._add_line_value_to('license', self.license) - - # Check if we need the pkgconfig - if not self.br_pkgconfig_required and \ - self._find_pkgconfig_statements('buildrequires'): - self.br_pkgconfig_required = True - # only in case we are in main scope - if not self._oldstore: - if self.br_pkgconfig_required and not self._find_pkgconfig_declarations('buildrequires'): - self._add_line_value_to('buildrequires', 'pkgconfig') - - # sort based on category order - for i in self.categories_order: - sorted_list = [] - # sort-out within the ordered groups based on the key - if i in self.categories_with_sorted_package_tokens: - self.paragraph[i].sort(key=self._sort_helper_key) - self.paragraph[i] = sort_uniq(self.paragraph[i]) - # sort-out within the ordered groups based on the keyword - if i in self.categories_with_sorted_keyword_tokens: - self.paragraph[i].sort(key=self._sort_helper_key) - for group in self.paragraph[i]: - sorted_list += self._add_group(group) - # now check if we need to add comment for the prereq - if i == 'prereq' and not self.minimal: - sorted_list = self._verify_prereq_message(sorted_list) - lines += sorted_list - if self.current_group: - # the current group was not added to any category. It's just some - # random stuff that should be at the end anyway. - lines += self._add_group(self.current_group) - self.current_group = [] - return lines - - def _fix_license(self, value): - # split using 'or', 'and' and parenthesis, ignore empty strings - licenses = [] - for a in re.split(r'(\(|\)| and | or (?!later))', value): - if a != '': - licenses.append(a) - if not licenses: - licenses.append(value) - - for (index, my_license) in enumerate(licenses): - my_license = self.strip_useless_spaces(my_license) - my_license = my_license.replace('ORlater', 'or later') - my_license = my_license.replace('ORsim', 'or similar') - my_license = my_license.rstrip(';') - my_license = self.reg.re_license_semicolon.sub(' and ', my_license) - if my_license in self.license_conversions: - my_license = self.license_conversions[my_license] - licenses[index] = my_license - - # create back new string with replaced licenses - s = ' '.join(licenses).replace("( ", "(").replace(" )", ")") - return s + self.paragraph.items['conditions'] = [] def _split_name_and_version(self, value): # split the name and version from the requires element @@ -393,32 +230,14 @@ else: return value - def _pkgname_to_pkgconfig(self, value): - # we just want the pkgname if we have version string there - # and for the pkgconfig deps we need to put the version into - # the braces - pkgname, version = self._split_name_and_version(value) - pkgconfig = [] - if pkgname == 'pkgconfig': - return [value] - if pkgname not in self.pkgconfig_conversions: - # first check if the package is in the replacements - return [value] - else: - # first split the pkgconfig data - pkgconf_list = self.pkgconfig_conversions[pkgname].split() - # then add each pkgconfig to the list - # print pkgconf_list - for j in pkgconf_list: - pkgconfig.append('pkgconfig({0}){1}'.format(j, version)) - return pkgconfig - def _pkgname_to_brackety(self, value, name, conversions): # we just want the pkgname if we have version string there # and for the pkgconfig deps we need to put the version into # the braces pkgname, version = self._split_name_and_version(value) converted = [] + if pkgname == 'pkgconfig': + return [value] if pkgname not in conversions: # first check if the package is in the replacements return [value] @@ -435,8 +254,8 @@ # we do fix the package list only if there is no rpm call there on line # otherwise print there warning about nicer content and skip if self.reg.re_rpm_command.search(value): - if not self.previous_line.startswith('#') and not self.minimal: - self.current_group.append('# FIXME: Use %requires_eq macro instead') + if category == 'requires' and not self.previous_line.startswith('#') and not self.minimal: + self.paragraph.current_group.append('# FIXME: Use %requires_eq macro instead') return [value] tokens = DependencyParser(value).flat_out() # loop over all and do formatting as we can get more deps for one @@ -459,19 +278,19 @@ # replace pkgconfig name first token = self._fix_pkgconfig_name(token) # in scriptlets we most probably do not want the converted deps - if category != 'prereq': + if category != 'prereq' and category != 'requires_phase': # here we go with descending priority to find match and replace # the strings by some optimistic value of brackety dep # priority is based on the first come first serve if self.pkgconfig: - token = self._pkgname_to_pkgconfig(token) + token = self._pkgname_to_brackety(token, 'pkgconfig', self.pkgconfig_conversions) # checking if it is not list is simple avoidance of running # over already converted values - if type(token) is not list and self.perl: + if not isinstance(token, list) and self.perl: token = self._pkgname_to_brackety(token, 'perl', self.perl_conversions) - if type(token) is not list and self.tex: + if not isinstance(token, list) and self.tex: token = self._pkgname_to_brackety(token, 'tex', self.tex_conversions) - if type(token) is not list and self.cmake: + if not isinstance(token, list) and self.cmake: token = self._pkgname_to_brackety(token, 'cmake', self.cmake_conversions) if isinstance(token, str): expanded.append(token) @@ -481,52 +300,14 @@ expanded.sort() return expanded - def _verify_prereq_message(self, elements): - """ - Verify if the prereq is present in the Requires(*) and add the fixme - comment if needed - """ - message = '# FIXME: use proper Requires(pre/post/preun/...)' - - # Check first if we have prereq values included - if not any("PreReq" in s for s in elements): - return elements - - # Verify the message is not already present - if any(message in s for s in elements): - return elements - - # add the message on the first position after any whitespace - location = next(i for i, j in enumerate(elements) if j) - elements.insert(location, message) - - return elements - def _add_line_value_to(self, category, value, key=None): """ - Change a key-value line, to make sure we have the right spacing. + Change a key-value line, to make sure we have the right spacing. - Note: since we don't have a key <-> category matching, we need to - redo one. (Eg: Provides and Obsoletes are in the same category) + Note: since we don't have a key <-> category matching, we need to + redo one. (Eg: Provides and Obsoletes are in the same category) """ - keylen = len('BuildRequires: ') - - if key: - pass - elif category in self.category_to_key: - key = self.category_to_key[category] - else: - raise RpmException('Unhandled category in preamble: %s' % category) - - # append : only if the thing is not known macro - if not key.startswith('%'): - key += ':' - # if the key is already longer then just add one space - if len(key) >= keylen: - key += ' ' - # fillup rest of the alignment if key is shorter than muster - while len(key) < keylen: - key += ' ' + key = self.paragraph.compile_category_prefix(category, key) if category in self.categories_with_package_tokens: values = self._fix_list_of_packages(value, category) @@ -538,12 +319,12 @@ self._add_line_to(category, line) def _add_line_to(self, category, line): - if self.current_group: - self.current_group.append(line) - self.paragraph[category].append(self.current_group) - self.current_group = [] + if self.paragraph.current_group: + self.paragraph.current_group.append(line) + self.paragraph.items[category].append(self.paragraph.current_group) + self.paragraph.current_group = [] else: - self.paragraph[category].append(line) + self.paragraph.items[category].append(line) self.previous_line = line @@ -599,13 +380,16 @@ elif self.reg.re_comment.match(line): if line or self.previous_line: - self.current_group.append(line) + self.paragraph.current_group.append(line) self.previous_line = line return elif self.reg.re_source.match(line): match = self.reg.re_source.match(line) - self._add_line_value_to('source', match.group(2), key='Source%s' % match.group(1)) + source = match.group(2) + if not self.minimal: + source = self._fix_pypi_source(source) + self._add_line_value_to('source', source, key='Source%s' % match.group(1)) return elif self.reg.re_patch.match(line): @@ -631,6 +415,12 @@ self._add_line_to('misc', line) else: self._add_line_to('define', line) + + # catch "modname" for use in pypi url rewriting + define, name, value = line.split(None, 2) + if name == "modname": + self.modname = value + return elif self.reg.re_requires_eq.match(line): @@ -646,7 +436,7 @@ elif self.reg.re_requires_phase.match(line): match = self.reg.re_requires_phase.match(line) # Put the requires content properly as key for formatting - self._add_line_value_to('prereq', match.group(2), key='Requires{0}'.format(match.group(1))) + self._add_line_value_to('requires_phase', match.group(2), key='Requires{0}'.format(match.group(1))) return elif self.reg.re_provides.match(line): @@ -661,7 +451,7 @@ elif self.reg.re_buildroot.match(line): # we only are fine with buildroot only once - if len(self.paragraph['buildroot']) == 0: + if len(self.paragraph.items['buildroot']) == 0: self._add_line_value_to('buildroot', '%{_tmppath}/%{name}-%{version}-build') return @@ -669,7 +459,7 @@ # first convert the license string to proper format and then append match = self.reg.re_license.match(line) value = match.groups()[len(match.groups()) - 1] - value = self._fix_license(value) + value = fix_license(value, self.license_conversions) # only store subpkgs if they have different licenses if not (type(self).__name__ == 'RpmPackage' and not self.subpkglicense): self._add_line_value_to('license', value) @@ -698,7 +488,7 @@ value = match.group(1) if not self.minimal: if self.previous_line and not self.previous_line.startswith('# FIXME') and value not in self.allowed_groups: - self.current_group.append('# FIXME: use correct group, see "https://en.opensuse.org/openSUSE:Package_group_guidelines"') + self.paragraph.current_group.append('# FIXME: use correct group, see "https://en.opensuse.org/openSUSE:Package_group_guidelines"') self._add_line_value_to('group', value) return @@ -724,6 +514,6 @@ self._add_line_to('misc', line) def output(self, fout, newline=True, new_class=None): - lines = self._end_paragraph(self.subpkglicense) + lines = self.paragraph.flatten_output(self.subpkglicense) self.lines += lines Section.output(self, fout, newline, new_class) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/rpmpreambleelements.py new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/rpmpreambleelements.py --- old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/rpmpreambleelements.py 1970-01-01 01:00:00.000000000 +0100 +++ new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/rpmpreambleelements.py 2017-05-17 14:22:32.000000000 +0200 @@ -0,0 +1,254 @@ +# vim: set ts=4 sw=4 et: coding=UTF-8 + +from .rpmhelpers import sort_uniq, add_group, find_pkgconfig_statement, find_pkgconfig_declaration, fix_license +from .rpmexception import RpmException + +class RpmPreambleElements(object): + """ + Class containing structure used in rpmpreamble. + List of all the elements possible to be provided in dict and list forms. + """ + + category_to_key = { + 'name': 'Name', + 'version': 'Version', + 'release': 'Release', + 'license': 'License', + 'summary': 'Summary', + # The localized summary can contain various values, so it can't be here + 'url': 'Url', + 'group': 'Group', + 'source': 'Source', + 'nosource': 'NoSource', + 'patch': 'Patch', + 'buildrequires': 'BuildRequires', + 'conflicts': 'Conflicts', + 'prereq': 'PreReq', + 'requires': 'Requires', + 'requires_eq': '%requires_eq', + 'recommends': 'Recommends', + 'suggests': 'Suggests', + 'enhances': 'Enhances', + 'supplements': 'Supplements', + # Provides/Obsoletes cannot be part of this since we want to keep them + # mixed, so we'll have to specify the key when needed + 'buildroot': 'BuildRoot', + 'buildarch': 'BuildArch', + 'exclusivearch': 'ExclusiveArch', + 'excludearch': 'ExcludeArch', + } + + categories_order = [ + 'define', + 'bconds', + 'bcond_conditions', + 'name', + 'version', + 'release', + 'summary', + 'summary_localized', + 'license', + 'group', + 'url', + 'source', + 'nosource', + 'patch', + 'buildrequires', + 'requires', + 'requires_eq', + 'prereq', + 'requires_phase', # this is Requires(pre/post/...) + 'recommends', + 'suggests', + 'enhances', + 'supplements', + 'conflicts', + 'provides_obsoletes', + 'buildroot', + 'buildarch', + 'exclusivearch', + 'excludearch', + 'misc', + 'build_conditions', + 'conditions', + ] + + # categories that are sorted based on value in them + categories_with_sorted_package_tokens = [ + 'buildrequires', + 'prereq', + 'requires', + 'requires_eq', + 'requires_phase', + 'recommends', + 'suggests', + 'enhances', + 'supplements', + 'conflicts', + ] + + # categories that are sorted based on key value (eg Patch0 before Patch1) + categories_with_sorted_keyword_tokens = [ + 'source', + 'patch', + ] + + def __init__(self, options): + self.items = {} + for i in self.categories_order: + self.items[i] = [] + self.current_group = [] + # minimal mode + self.minimal = options['minimal'] + # regexp object + self.reg = options['reg'] + # pkgconfig requirement detection + self.br_pkgconfig_required = False + # license string + self.license = options['license'] + # dict of license replacement options + self.license_conversions = options['license_conversions'] + + def _sort_helper_key(self, a): + t = type(a) + if t == str: + key = a + elif t == list: + # if this is a list then all items except last are comment or whitespace + key = a[-1] + else: + raise RpmException('Unknown type during sort: %s' % t) + + # Special case is the category grouping where we have to get the number in + # after the value + if self.reg.re_patch.match(key): + match = self.reg.re_patch.match(key) + key = int(match.group(2)) + elif self.reg.re_source.match(key): + match = self.reg.re_source.match(key) + value = match.group(1) + if not value: + value = '0' + key = int(value) + # Put brackety ()-style deps at the end of the list, after all other + elif self.reg.re_brackety_requires.search(key): + key = '1' + key + else: + key = '0' + key + return key + + def _insert_value(self, category, value, key = None): + """ + Add value to specified keystore + """ + key = self.compile_category_prefix(category, key) + line = key + value + self.items[category].append(line) + + def _add_pkgconfig_buildrequires(self, nested): + """ + Check the content of buildrequires and add pkgconfig as an item + in case there are any pkgconfig() style dependencies present + + If we are in the top level object for preamble we append the BR, + otherwise we do just verify if there are nay dependencies + """ + # first generate flat list from the BR + buildrequires = [] + for group in self.items['buildrequires']: + buildrequires += add_group(group) + # Check if we need the pkgconfig + if not self.br_pkgconfig_required and \ + find_pkgconfig_statement(buildrequires): + self.br_pkgconfig_required = True + # only in case we are in main scope + if not nested: + if self.br_pkgconfig_required and not find_pkgconfig_declaration(buildrequires): + self._insert_value('buildrequires', 'pkgconfig') + + def _verify_prereq_message(self, elements): + """ + Verify if the prereq is present in the Requires(*) and add the fixme + comment if needed + """ + message = '# FIXME: use proper Requires(pre/post/preun/...)' + + # Check first if we have prereq values included + if not any("PreReq" in s for s in elements): + return elements + + # Verify the message is not already present + if any(message in s for s in elements): + return elements + + # add the message on the first position after any whitespace + location = next(i for i, j in enumerate(elements) if j) + elements.insert(location, message) + + return elements + + def _run_global_list_operations(self, phase, elements): + """ + Run all the checks that need to be run on the finalized sorted list + rather than on invidiual value + """ + # check if we need to add comment for the prereq + if not self.minimal and phase == 'prereq': + elements = self._verify_prereq_message(elements) + + return elements + + def compile_category_prefix(self, category, key=None): + """ + Simply compile the category key and provide enough whitespace for the + values to be alligned + """ + keylen = len('BuildRequires: ') + + if key: + pass + elif category in self.category_to_key: + key = self.category_to_key[category] + else: + raise RpmException('Unhandled category in preamble: %s' % category) + + # append : only if the thing is not known macro + if not key.startswith('%'): + key += ':' + # if the key is already longer then just add one space + if len(key) >= keylen: + key += ' ' + # fillup rest of the alignment if key is shorter than muster + while len(key) < keylen: + key += ' ' + return key + + def flatten_output(self, needs_license=False, nested = False): + """ + Do the finalized output for the itemlist. + """ + lines = [] + + # add license to the package if missing and needed + if needs_license and not self.items['license']: + self.license = fix_license(self.license, self.license_conversions) + self._insert_value('license', self.license) + # add pkgconfig dep + self._add_pkgconfig_buildrequires(nested) + for i in self.categories_order: + sorted_list = [] + # sort-out within the ordered groups based on the key + if i in self.categories_with_sorted_package_tokens + self.categories_with_sorted_keyword_tokens: + self.items[i].sort(key=self._sort_helper_key) + self.items[i] = sort_uniq(self.items[i]) + # flatten the list from list of lists as no reordering is planned + for group in self.items[i]: + sorted_list += add_group(group) + # now do all sorts of operations where we needed sorted lists + lines += self._run_global_list_operations(i, sorted_list) + if self.current_group: + # the current group was not added to any category. It's just some + # random stuff that should be at the end anyway. + lines += add_group(self.current_group) + self.current_group = [] + return lines diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/rpmprep.py new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/rpmprep.py --- old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/rpmprep.py 2017-04-08 11:05:38.000000000 +0200 +++ new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/rpmprep.py 2017-05-17 14:22:32.000000000 +0200 @@ -12,8 +12,8 @@ def add(self, line): line = self._complete_cleanup(line) + line = self._cleanup_setup(line) if not self.minimal: - line = self._cleanup_setup(line) line = self._prepare_patch(line) Section.add(self, line) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/rpmregexp.py new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/rpmregexp.py --- old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/rpmregexp.py 2017-04-08 11:05:38.000000000 +0200 +++ new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/rpmregexp.py 2017-05-17 14:22:32.000000000 +0200 @@ -71,8 +71,6 @@ re_rpm_command = re.compile(r'%\(.*\)') re_requires_eq = re.compile(r'^\s*%requires_eq\s*(.*)') re_onelinecond = re.compile(r'^\s*%{!?[^?]*\?[^:]+:[^}]+}') - # license ; should be replaced by ands so find it - re_license_semicolon = re.compile(r'\s*;\s*') # Special bracketed deps dection re_brackety_requires = re.compile(r'(pkgconfig|cmake|perl|tex|rubygem)\(') re_version_separator = re.compile(r'(\S+)((\s*[<>=\s]+)(\S+))*') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/rpmrequirestoken.py new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/rpmrequirestoken.py --- old/spec-cleaner-spec-cleaner-0.9.4/spec_cleaner/rpmrequirestoken.py 1970-01-01 01:00:00.000000000 +0100 +++ new/spec-cleaner-spec-cleaner-0.9.5/spec_cleaner/rpmrequirestoken.py 2017-05-17 14:22:32.000000000 +0200 @@ -0,0 +1,39 @@ +from .rpmexception import RpmException + +class RpmRequiresToken(object): + """ + Class containing informations about the dependency token + Can be used to specify all the values present on the line + Later on we use this to do various conversions + + prefix name comparator version + BuildRequires: boringpackage >= 5.2.8 + """ + + name = None + comparator = None + version = None + prefix = None + + def __init__(self, name, comparator = None, version = None, prefix = None): + self.prefix = prefix + self.name = name + self.comparator = comparator + self.version = version + + def dump_token(self): + """ + Output it all on nice pretty line + """ + + if not self.prefix: + raise RpmException('No defined prefix in RequiresToken') + if not self.name: + raise RpmException('No defined name in RequiresToken') + string = self.prefix + self.name + if self.version and not self.comparator: + raise RpmException('Have defined version and no comparator %s' % self.version) + if self.version: + string += ' ' + self.comparator + ' ' + self.version + + return string diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/tests/acceptance-tests.py new/spec-cleaner-spec-cleaner-0.9.5/tests/acceptance-tests.py --- old/spec-cleaner-spec-cleaner-0.9.4/tests/acceptance-tests.py 2017-04-08 11:05:38.000000000 +0200 +++ new/spec-cleaner-spec-cleaner-0.9.5/tests/acceptance-tests.py 2017-05-17 14:22:32.000000000 +0200 @@ -69,7 +69,7 @@ testglob = os.path.join('tests', directory, '*.spec') return [os.path.basename(f) for f in glob.glob(testglob)] - def _run_individual_test(self, test, compare_dir, infile=None, outfile=None, options={}, **kwargs): + def _run_individual_test(self, test, compare_dir, infile=None, outfile=None, options=None, **kwargs): """ Run the cleaner as specified and store the output for further comparison. """ @@ -86,7 +86,7 @@ } full_options.update(self.option_presets) full_options.update(kwargs) - full_options.update(options) + full_options.update(options or {}) cleaner = RpmSpecCleaner(full_options) cleaner.run() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/tests/in/pypi-url-modname.spec new/spec-cleaner-spec-cleaner-0.9.5/tests/in/pypi-url-modname.spec --- old/spec-cleaner-spec-cleaner-0.9.4/tests/in/pypi-url-modname.spec 1970-01-01 01:00:00.000000000 +0100 +++ new/spec-cleaner-spec-cleaner-0.9.5/tests/in/pypi-url-modname.spec 2017-05-17 14:22:32.000000000 +0200 @@ -0,0 +1,3 @@ +%define modname idna +Name: python-%{modname} +Source3: http://pypi.python.org/packages/source/i/%{modname}/%{modname}-%{version}.tar.bz2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/tests/in/pypi-url.spec new/spec-cleaner-spec-cleaner-0.9.5/tests/in/pypi-url.spec --- old/spec-cleaner-spec-cleaner-0.9.4/tests/in/pypi-url.spec 1970-01-01 01:00:00.000000000 +0100 +++ new/spec-cleaner-spec-cleaner-0.9.5/tests/in/pypi-url.spec 2017-05-17 14:22:32.000000000 +0200 @@ -0,0 +1,4 @@ +Source0: http://pypi.python.org/packages/source/s/src/src-%{version}.tar.bz2 +Source1: http://github.com/release/src-%{version}.tar.bz2 +Source2: src-%{version}.tar.bz2 +Source3: http://pypi.python.org/packages/source/s/src/%{modname}-%{version}.tar.bz2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/tests/in/requires.spec new/spec-cleaner-spec-cleaner-0.9.5/tests/in/requires.spec --- old/spec-cleaner-spec-cleaner-0.9.4/tests/in/requires.spec 2017-04-08 11:05:38.000000000 +0200 +++ new/spec-cleaner-spec-cleaner-0.9.5/tests/in/requires.spec 2017-05-17 14:22:32.000000000 +0200 @@ -15,7 +15,7 @@ BuildRequires: %{rubygem rails >= 3.2} Requires: php5 => %{phpversion} - +Provides: locale(ru;bg) Requires: %{libname} >= %{version} libcurl-devel Provides: %{name} = 0.3.0~gitbcaa Obsoletes: %{name} = 0.3.0~gitbcaa diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/tests/in/rpmcmd.spec new/spec-cleaner-spec-cleaner-0.9.5/tests/in/rpmcmd.spec --- old/spec-cleaner-spec-cleaner-0.9.4/tests/in/rpmcmd.spec 2017-04-08 11:05:38.000000000 +0200 +++ new/spec-cleaner-spec-cleaner-0.9.5/tests/in/rpmcmd.spec 2017-05-17 14:22:32.000000000 +0200 @@ -6,3 +6,4 @@ Requires: mozilla-nss-devel >= %(rpm -q --queryformat '%{VERSION}' mozilla-nss-devel) Requires: ant = %(echo `rpm -q --queryformat '%{VERSION}' ant`) Requires: akonadi-runtime >= %( echo `rpm -q --queryformat '%{VERSION}' akonadi-runtime`) +Provides: NetworkManager-lang = %(rpm -q --queryformat '%{VERSION}' NetworkManager-lang) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/tests/in/slowparse.spec new/spec-cleaner-spec-cleaner-0.9.5/tests/in/slowparse.spec --- old/spec-cleaner-spec-cleaner-0.9.4/tests/in/slowparse.spec 1970-01-01 01:00:00.000000000 +0100 +++ new/spec-cleaner-spec-cleaner-0.9.5/tests/in/slowparse.spec 2017-05-17 14:22:32.000000000 +0200 @@ -0,0 +1,12 @@ +Name: bundle-lang-other +BuildArch: noarch +Provides: locale(aa;af;am;ang;as;az;be;bg;bn;bo;br;bs;byn) +Provides: locale(csb;cy;dv;dz;ee;eo;et;eu) +Provides: locale(fa;fo;fy;ga;gd;gez;gl;gn;gu;gv;haw;he;hi;hr) +Provides: locale(hy;ia;id;is;iu;ka;kk;kl;km) +Provides: locale(kn;kok;ku;kw;ky;lg;li;lo;lt;lv;mg;mi;mk;ml;mn;mr;ms;mt;my;nds;ne) +Provides: locale(nn;nso;oc;om;or;pa;ps;rm;ro) +Provides: locale(rw;sa;se;si;sid;sk;sl;so;sp;sq;sr) +Provides: locale(ss;st;sw;syr;ta;te;tg;th;ti;tig;tk;tl;tr;tt;ug;uk;ur;urd;uz) +Provides: locale(ve;ven;vi;wa;wal;wo;xh;yi;yo;zu) +Source0: README.other diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/tests/out/pypi-url-modname.spec new/spec-cleaner-spec-cleaner-0.9.5/tests/out/pypi-url-modname.spec --- old/spec-cleaner-spec-cleaner-0.9.4/tests/out/pypi-url-modname.spec 1970-01-01 01:00:00.000000000 +0100 +++ new/spec-cleaner-spec-cleaner-0.9.5/tests/out/pypi-url-modname.spec 2017-05-17 14:22:32.000000000 +0200 @@ -0,0 +1,5 @@ +%define modname idna +Name: python-%{modname} +Source3: https://files.pythonhosted.org/packages/source/i/idna/%{modname}-%{version}.tar.bz2 + +%changelog diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/tests/out/pypi-url.spec new/spec-cleaner-spec-cleaner-0.9.5/tests/out/pypi-url.spec --- old/spec-cleaner-spec-cleaner-0.9.4/tests/out/pypi-url.spec 1970-01-01 01:00:00.000000000 +0100 +++ new/spec-cleaner-spec-cleaner-0.9.5/tests/out/pypi-url.spec 2017-05-17 14:22:32.000000000 +0200 @@ -0,0 +1,6 @@ +Source0: https://files.pythonhosted.org/packages/source/s/src/src-%{version}.tar.bz2 +Source1: http://github.com/release/src-%{version}.tar.bz2 +Source2: src-%{version}.tar.bz2 +Source3: http://pypi.python.org/packages/source/s/src/%{modname}-%{version}.tar.bz2 + +%changelog diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/tests/out/requires.spec new/spec-cleaner-spec-cleaner-0.9.5/tests/out/requires.spec --- old/spec-cleaner-spec-cleaner-0.9.4/tests/out/requires.spec 2017-04-08 11:05:38.000000000 +0200 +++ new/spec-cleaner-spec-cleaner-0.9.5/tests/out/requires.spec 2017-05-17 14:22:32.000000000 +0200 @@ -32,6 +32,7 @@ PreReq: kkk PreReq: rrr >= %{version} PreReq: zzz +Provides: locale(ru;bg) Provides: %{name} = 0.3.0~gitbcaa Obsoletes: %{name} = 0.3.0~gitbcaa Provides: %{name} = 0.3.0+gitbcaa diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/tests/out/rpmcmd.spec new/spec-cleaner-spec-cleaner-0.9.5/tests/out/rpmcmd.spec --- old/spec-cleaner-spec-cleaner-0.9.4/tests/out/rpmcmd.spec 2017-04-08 11:05:38.000000000 +0200 +++ new/spec-cleaner-spec-cleaner-0.9.5/tests/out/rpmcmd.spec 2017-05-17 14:22:32.000000000 +0200 @@ -12,5 +12,6 @@ # FIXME: Use %requires_eq macro instead Requires: mozilla-nss-devel >= %(rpm -q --queryformat '%{VERSION}' mozilla-nss-devel) %requires_eq vlc +Provides: NetworkManager-lang = %(rpm -q --queryformat '%{VERSION}' NetworkManager-lang) %changelog diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/tests/out/slowparse.spec new/spec-cleaner-spec-cleaner-0.9.5/tests/out/slowparse.spec --- old/spec-cleaner-spec-cleaner-0.9.4/tests/out/slowparse.spec 1970-01-01 01:00:00.000000000 +0100 +++ new/spec-cleaner-spec-cleaner-0.9.5/tests/out/slowparse.spec 2017-05-17 14:22:32.000000000 +0200 @@ -0,0 +1,14 @@ +Name: bundle-lang-other +Source0: README.other +Provides: locale(aa;af;am;ang;as;az;be;bg;bn;bo;br;bs;byn) +Provides: locale(csb;cy;dv;dz;ee;eo;et;eu) +Provides: locale(fa;fo;fy;ga;gd;gez;gl;gn;gu;gv;haw;he;hi;hr) +Provides: locale(hy;ia;id;is;iu;ka;kk;kl;km) +Provides: locale(kn;kok;ku;kw;ky;lg;li;lo;lt;lv;mg;mi;mk;ml;mn;mr;ms;mt;my;nds;ne) +Provides: locale(nn;nso;oc;om;or;pa;ps;rm;ro) +Provides: locale(rw;sa;se;si;sid;sk;sl;so;sp;sq;sr) +Provides: locale(ss;st;sw;syr;ta;te;tg;th;ti;tig;tk;tl;tr;tt;ug;uk;ur;urd;uz) +Provides: locale(ve;ven;vi;wa;wal;wo;xh;yi;yo;zu) +BuildArch: noarch + +%changelog diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/tests/out-minimal/pypi-url-modname.spec new/spec-cleaner-spec-cleaner-0.9.5/tests/out-minimal/pypi-url-modname.spec --- old/spec-cleaner-spec-cleaner-0.9.4/tests/out-minimal/pypi-url-modname.spec 1970-01-01 01:00:00.000000000 +0100 +++ new/spec-cleaner-spec-cleaner-0.9.5/tests/out-minimal/pypi-url-modname.spec 2017-05-17 14:22:32.000000000 +0200 @@ -0,0 +1,5 @@ +%define modname idna +Name: python-%{modname} +Source3: http://pypi.python.org/packages/source/i/%{modname}/%{modname}-%{version}.tar.bz2 + +%changelog diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/tests/out-minimal/pypi-url.spec new/spec-cleaner-spec-cleaner-0.9.5/tests/out-minimal/pypi-url.spec --- old/spec-cleaner-spec-cleaner-0.9.4/tests/out-minimal/pypi-url.spec 1970-01-01 01:00:00.000000000 +0100 +++ new/spec-cleaner-spec-cleaner-0.9.5/tests/out-minimal/pypi-url.spec 2017-05-17 14:22:32.000000000 +0200 @@ -0,0 +1,6 @@ +Source0: http://pypi.python.org/packages/source/s/src/src-%{version}.tar.bz2 +Source1: http://github.com/release/src-%{version}.tar.bz2 +Source2: src-%{version}.tar.bz2 +Source3: http://pypi.python.org/packages/source/s/src/%{modname}-%{version}.tar.bz2 + +%changelog diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/tests/out-minimal/requires.spec new/spec-cleaner-spec-cleaner-0.9.5/tests/out-minimal/requires.spec --- old/spec-cleaner-spec-cleaner-0.9.4/tests/out-minimal/requires.spec 2017-04-08 11:05:38.000000000 +0200 +++ new/spec-cleaner-spec-cleaner-0.9.5/tests/out-minimal/requires.spec 2017-05-17 14:22:32.000000000 +0200 @@ -31,6 +31,7 @@ PreReq: kkk PreReq: rrr >= %{version} PreReq: zzz +Provides: locale(ru;bg) Provides: %{name} = 0.3.0~gitbcaa Obsoletes: %{name} = 0.3.0~gitbcaa Provides: %{name} = 0.3.0+gitbcaa diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/tests/out-minimal/rpmcmd.spec new/spec-cleaner-spec-cleaner-0.9.5/tests/out-minimal/rpmcmd.spec --- old/spec-cleaner-spec-cleaner-0.9.4/tests/out-minimal/rpmcmd.spec 2017-04-08 11:05:38.000000000 +0200 +++ new/spec-cleaner-spec-cleaner-0.9.5/tests/out-minimal/rpmcmd.spec 2017-05-17 14:22:32.000000000 +0200 @@ -6,5 +6,6 @@ Requires: mozilla-nss >= %(rpm -q --queryformat '%{VERSION}' mozilla-nss) Requires: mozilla-nss-devel >= %(rpm -q --queryformat '%{VERSION}' mozilla-nss-devel) %requires_eq vlc +Provides: NetworkManager-lang = %(rpm -q --queryformat '%{VERSION}' NetworkManager-lang) %changelog diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/tests/out-minimal/slowparse.spec new/spec-cleaner-spec-cleaner-0.9.5/tests/out-minimal/slowparse.spec --- old/spec-cleaner-spec-cleaner-0.9.4/tests/out-minimal/slowparse.spec 1970-01-01 01:00:00.000000000 +0100 +++ new/spec-cleaner-spec-cleaner-0.9.5/tests/out-minimal/slowparse.spec 2017-05-17 14:22:32.000000000 +0200 @@ -0,0 +1,14 @@ +Name: bundle-lang-other +Source0: README.other +Provides: locale(aa;af;am;ang;as;az;be;bg;bn;bo;br;bs;byn) +Provides: locale(csb;cy;dv;dz;ee;eo;et;eu) +Provides: locale(fa;fo;fy;ga;gd;gez;gl;gn;gu;gv;haw;he;hi;hr) +Provides: locale(hy;ia;id;is;iu;ka;kk;kl;km) +Provides: locale(kn;kok;ku;kw;ky;lg;li;lo;lt;lv;mg;mi;mk;ml;mn;mr;ms;mt;my;nds;ne) +Provides: locale(nn;nso;oc;om;or;pa;ps;rm;ro) +Provides: locale(rw;sa;se;si;sid;sk;sl;so;sp;sq;sr) +Provides: locale(ss;st;sw;syr;ta;te;tg;th;ti;tig;tk;tl;tr;tt;ug;uk;ur;urd;uz) +Provides: locale(ve;ven;vi;wa;wal;wo;xh;yi;yo;zu) +BuildArch: noarch + +%changelog diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec-cleaner-spec-cleaner-0.9.4/tests/out-minimal/sourcespatches.spec new/spec-cleaner-spec-cleaner-0.9.5/tests/out-minimal/sourcespatches.spec --- old/spec-cleaner-spec-cleaner-0.9.4/tests/out-minimal/sourcespatches.spec 2017-04-08 11:05:38.000000000 +0200 +++ new/spec-cleaner-spec-cleaner-0.9.5/tests/out-minimal/sourcespatches.spec 2017-05-17 14:22:32.000000000 +0200 @@ -16,9 +16,9 @@ Patch10: test2 %prep -%setup -qn %name-%version +%setup -q -n %name-%version %setup -q -n "%name-%version" -a1 -%setup -n "%name-%version" -q -b2 +%setup -q -n "%name-%version" -b2 %setup -q -n %{name}-%{version}-src %patch10 -p4 %patch -p1 ++++++ spec-cleaner.dsc ++++++ --- /var/tmp/diff_new_pack.n1soEK/_old 2017-05-17 17:20:25.755516747 +0200 +++ /var/tmp/diff_new_pack.n1soEK/_new 2017-05-17 17:20:25.755516747 +0200 @@ -1,6 +1,6 @@ Format: 3.0 (quilt) Source: spec-cleaner -Version: 0.9.4-1 +Version: 0.9.5-1 Binary: spec-cleaner Maintainer: Přemysl Janouch <pjano...@suse.com> Architecture: all