def check(self, pkg): global _policy_legacy_exceptions if pkg.isSource(): return # Only check unsuffixed lib* packages if pkg.name.endswith("-devel") or pkg.name.endswith("-doc"): return files = pkg.files() # Search for shared libraries in this package libs = set() libs_needed = set() libs_to_dir = dict() dirs = set() reqlibs = set() pkg_requires = set(map(lambda x: string.split(x[0], "(")[0], pkg.requires())) for f, pkgfile in files.items(): if f.find(".so.") != -1 or f.endswith(".so"): filename = pkg.dirName() + "/" + f try: if stat.S_ISREG(files[f].mode) and "ELF" in pkgfile.magic: bi = BinaryInfo(pkg, filename, f, False, True) libs_needed = libs_needed.union(bi.needed) if bi.soname != 0: lib_dir = string.join(f.split("/")[:-1], "/") libs.add(bi.soname) libs_to_dir[bi.soname] = lib_dir dirs.add(lib_dir) if bi.soname in pkg_requires: # But not if the library is used by the pkg itself # This avoids program packages with their own # private lib # FIXME: we'd need to check if somebody else links # to this lib reqlibs.add(bi.soname) except Exception: pass pass std_dirs = dirs.intersection(("/lib", "/lib64", "/usr/lib", "/usr/lib64", "/opt/kde3/lib", "/opt/kde3/lib64")) # If this is a program package (all libs it provides are # required by itself), bail out if not pkg.name.startswith("lib") and len(libs.difference(reqlibs)) == 0: return std_lib_package = False if pkg.name.startswith("lib") and pkg.name[-1].isdigit(): std_lib_package = True # ignore libs in a versioned non_std_dir if std_lib_package: for lib in libs.copy(): lib_dir = libs_to_dir[lib] if lib_dir.startswith("/opt/kde3"): continue for lib_part in lib_dir.split("/"): if len(lib_part) == 0: continue if lib_part[-1].isdigit() and not lib_part.endswith("lib64"): libs.remove(lib) break # Check for non-versioned libs in a std lib package if std_lib_package: for lib in libs.copy(): if not lib[-1].isdigit(): printWarning(pkg, "shlib-unversioned-lib", lib) libs.remove(lib) # If this package should be or should be splitted into shlib # package(s) if len(libs) > 0 and len(std_dirs) > 0: # If the package contains a single shlib, name after soname if len(libs) == 1: soname = libs.copy().pop() libname = libname_from_soname(soname) if libname.startswith("lib") and pkg.name != libname and pkg.name != libname + "-mini": if libname in _policy_legacy_exceptions: printWarning(pkg, "shlib-legacy-policy-name-error", libname) else: printError(pkg, "shlib-policy-name-error", libname) elif not pkg.name[-1:].isdigit(): printError(pkg, "shlib-policy-missing-suffix") if (not pkg.name.startswith("lib")) or pkg.name.endswith("-lang"): return if not libs: if pkg.name in _policy_legacy_exceptions: printWarning(pkg, "shlib-legacy-policy-missing-lib", pkg.name) else: printError(pkg, "shlib-policy-missing-lib") # Verify no non-lib stuff is in the package dirs = set() for f in files: if os.path.isdir(pkg.dirName() + f): dirs.add(f) # Verify shared lib policy package doesn't have hard dependency on non-lib packages if std_lib_package: for dep in pkg.requires(): if dep[0].startswith("rpmlib(") or dep[0].startswith("config("): continue if (dep[1] & (rpm.RPMSENSE_GREATER | rpm.RPMSENSE_EQUAL)) == rpm.RPMSENSE_EQUAL: printWarning(pkg, "shlib-fixed-dependency", Pkg.formatRequire(dep[0], dep[1], dep[2])) # Verify non-lib stuff does not add dependencies if libs: for dep in pkg_requires.difference(_essential_dependencies): if dep.find(".so.") != -1 and not dep in libs and not dep in libs_needed: printError(pkg, "shlib-policy-excessive-dependency", dep) # Check for non-versioned directories beyond sysdirs in package sysdirs = ["/lib", "/lib64", "/usr/lib", "/usr/lib64", "/usr/share/doc/packages", "/usr/share"] cdirs = set() for sysdir in sysdirs: done = set() for dir in dirs: if dir.startswith(sysdir + "/"): ssdir = string.split(dir[len(sysdir) + 1 :], "/")[0] if not ssdir[-1].isdigit(): cdirs.add(sysdir + "/" + ssdir) done.add(dir) dirs = dirs.difference(done) map(lambda dir: printError(pkg, "shlib-policy-nonversioned-dir", dir), cdirs)
def check(self, pkg): packager = pkg[rpm.RPMTAG_PACKAGER] if packager: self._unexpanded_macros(pkg, 'Packager', packager) if Config.getOption('Packager') and \ not packager_regex.search(packager): printWarning(pkg, 'invalid-packager', packager) else: printError(pkg, 'no-packager-tag') version = pkg[rpm.RPMTAG_VERSION] if version: self._unexpanded_macros(pkg, 'Version', version) res = invalid_version_regex.search(version) if res: printError(pkg, 'invalid-version', version) else: printError(pkg, 'no-version-tag') release = pkg[rpm.RPMTAG_RELEASE] if release: self._unexpanded_macros(pkg, 'Release', release) if release_ext and not extension_regex.search(release): printWarning(pkg, 'not-standard-release-extension', release) else: printError(pkg, 'no-release-tag') epoch = pkg[rpm.RPMTAG_EPOCH] if epoch is None: if use_epoch: printError(pkg, 'no-epoch-tag') else: if epoch > 99: printWarning(pkg, 'unreasonable-epoch', epoch) epoch = str(epoch) if use_epoch: for tag in ("obsoletes", "conflicts", "provides", "recommends", "suggests", "enhances", "supplements"): for x in (x for x in getattr(pkg, tag)() if x[1] and x[2][0] is None): printWarning(pkg, 'no-epoch-in-%s' % tag, Pkg.formatRequire(*x)) name = pkg.name deps = pkg.requires() + pkg.prereq() devel_depend = False is_devel = FilesCheck.devel_regex.search(name) is_source = pkg.isSource() for d in deps: value = Pkg.formatRequire(*d) if use_epoch and d[1] and d[2][0] is None and \ not d[0].startswith('rpmlib('): printWarning(pkg, 'no-epoch-in-dependency', value) for r in INVALID_REQUIRES: if r.search(d[0]): printError(pkg, 'invalid-dependency', d[0]) if d[0].startswith('/usr/local/'): printError(pkg, 'invalid-dependency', d[0]) if is_source: if lib_devel_number_regex.search(d[0]): printError(pkg, 'invalid-build-requires', d[0]) elif not is_devel: if not devel_depend and FilesCheck.devel_regex.search(d[0]): printError(pkg, 'devel-dependency', d[0]) devel_depend = True if not d[1]: res = lib_package_regex.search(d[0]) if res and not res.group(1): printError(pkg, 'explicit-lib-dependency', d[0]) if d[1] == rpm.RPMSENSE_EQUAL and d[2][2] is not None: printWarning(pkg, 'requires-on-release', value) self._unexpanded_macros(pkg, 'dependency %s' % (value,), value) self._unexpanded_macros(pkg, 'Name', name) if not name: printError(pkg, 'no-name-tag') else: if is_devel and not is_source: base = is_devel.group(1) dep = None has_so = False for fname in pkg.files(): if fname.endswith('.so'): has_so = True break if has_so: base_or_libs = base + '/' + base + '-libs/lib' + base # try to match *%_isa as well (e.g. "(x86-64)", "(x86-32)") base_or_libs_re = re.compile( r'^(lib)?%s(-libs)?(\(\w+-\d+\))?$' % re.escape(base)) for d in deps: if base_or_libs_re.match(d[0]): dep = d break if not dep: printWarning(pkg, 'no-dependency-on', base_or_libs) elif version: exp = (epoch, version, None) sexp = Pkg.versionToString(exp) if not dep[1]: printWarning(pkg, 'no-version-dependency-on', base_or_libs, sexp) elif dep[2][:2] != exp[:2]: printWarning(pkg, 'incoherent-version-dependency-on', base_or_libs, Pkg.versionToString((dep[2][0], dep[2][1], None)), sexp) res = devel_number_regex.search(name) if not res: printWarning(pkg, 'no-major-in-name', name) else: if res.group(3): prov = res.group(1) + res.group(2) + '-devel' else: prov = res.group(1) + '-devel' if prov not in (x[0] for x in pkg.provides()): printWarning(pkg, 'no-provides', prov) # List of words to ignore in spell check ignored_words = set() for pf in pkg.files(): ignored_words.update(pf.split('/')) ignored_words.update((x[0] for x in pkg.provides())) ignored_words.update((x[0] for x in pkg.requires())) ignored_words.update((x[0] for x in pkg.conflicts())) ignored_words.update((x[0] for x in pkg.obsoletes())) langs = pkg[rpm.RPMTAG_HEADERI18NTABLE] summary = pkg[rpm.RPMTAG_SUMMARY] if summary: if not langs: self._unexpanded_macros(pkg, 'Summary', Pkg.b2s(summary)) else: for lang in langs: self.check_summary(pkg, lang, ignored_words) else: printError(pkg, 'no-summary-tag') description = pkg[rpm.RPMTAG_DESCRIPTION] if description: if not langs: self._unexpanded_macros(pkg, '%description', Pkg.b2s(description)) else: for lang in langs: self.check_description(pkg, lang, ignored_words) else: printError(pkg, 'no-description-tag') group = pkg[rpm.RPMTAG_GROUP] self._unexpanded_macros(pkg, 'Group', group) if not group: printError(pkg, 'no-group-tag') elif VALID_GROUPS and group not in VALID_GROUPS: printWarning(pkg, 'non-standard-group', group) buildhost = pkg[rpm.RPMTAG_BUILDHOST] self._unexpanded_macros(pkg, 'BuildHost', buildhost) if not buildhost: printError(pkg, 'no-buildhost-tag') elif Config.getOption('ValidBuildHost') and \ not valid_buildhost_regex.search(buildhost): printWarning(pkg, 'invalid-buildhost', buildhost) changelog = pkg[rpm.RPMTAG_CHANGELOGNAME] if not changelog: printError(pkg, 'no-changelogname-tag') else: clt = pkg[rpm.RPMTAG_CHANGELOGTEXT] if use_version_in_changelog: ret = changelog_version_regex.search(Pkg.b2s(changelog[0])) if not ret and clt: # we also allow the version specified as the first # thing on the first line of the text ret = changelog_text_version_regex.search(Pkg.b2s(clt[0])) if not ret: printWarning(pkg, 'no-version-in-last-changelog') elif version and release: srpm = pkg[rpm.RPMTAG_SOURCERPM] or '' # only check when source name correspond to name if srpm[0:-8] == '%s-%s-%s' % (name, version, release): expected = [version + '-' + release] if epoch is not None: # regardless of use_epoch expected[0] = str(epoch) + ':' + expected[0] # Allow EVR in changelog without release extension, # the extension is often a macro or otherwise dynamic. if release_ext: expected.append( extension_regex.sub('', expected[0])) if ret.group(1) not in expected: if len(expected) == 1: expected = expected[0] printWarning(pkg, 'incoherent-version-in-changelog', ret.group(1), expected) if use_utf8: if clt: changelog = changelog + clt for s in changelog: if not Pkg.is_utf8_bytestr(s): printError(pkg, 'tag-not-utf8', '%changelog') break clt = pkg[rpm.RPMTAG_CHANGELOGTIME][0] if clt: clt -= clt % (24 * 3600) # roll back to 00:00:00, see #246 if clt < oldest_changelog_timestamp: printWarning(pkg, 'changelog-time-overflow', time.strftime("%Y-%m-%d", time.gmtime(clt))) elif clt > time.time(): printError(pkg, 'changelog-time-in-future', time.strftime("%Y-%m-%d", time.gmtime(clt))) # for provide_name in (x[0] for x in pkg.provides()): # if name == provide_name: # printWarning(pkg, 'package-provides-itself') # break def split_license(license): return (x.strip() for x in (l for l in license_regex.split(license) if l)) rpm_license = pkg[rpm.RPMTAG_LICENSE] if not rpm_license: printError(pkg, 'no-license') else: valid_license = True if rpm_license not in VALID_LICENSES: for l1 in split_license(rpm_license): if l1 in VALID_LICENSES: continue for l2 in split_license(l1): if l2 not in VALID_LICENSES: printWarning(pkg, 'invalid-license', l2) valid_license = False if not valid_license: self._unexpanded_macros(pkg, 'License', rpm_license) for tag in ('URL', 'DistURL', 'BugURL'): if hasattr(rpm, 'RPMTAG_%s' % tag.upper()): url = Pkg.b2s(pkg[getattr(rpm, 'RPMTAG_%s' % tag.upper())]) self._unexpanded_macros(pkg, tag, url, is_url=True) if url: (scheme, netloc) = urlparse(url)[0:2] if not scheme or not netloc or "." not in netloc or \ scheme not in ('http', 'https', 'ftp') or \ (Config.getOption('InvalidURL') and invalid_url_regex.search(url)): printWarning(pkg, 'invalid-url', tag, url) else: self.check_url(pkg, tag, url) elif tag == 'URL': printWarning(pkg, 'no-url-tag') obs_names = [x[0] for x in pkg.obsoletes()] prov_names = [x[0] for x in pkg.provides()] for o in (x for x in obs_names if x not in prov_names): printWarning(pkg, 'obsolete-not-provided', o) for o in pkg.obsoletes(): value = Pkg.formatRequire(*o) self._unexpanded_macros(pkg, 'Obsoletes %s' % (value,), value) # TODO: should take versions, <, <=, =, >=, > into account here # https://bugzilla.redhat.com/460872 useless_provides = [] for p in prov_names: if prov_names.count(p) != 1 and p not in useless_provides: useless_provides.append(p) for p in useless_provides: printError(pkg, 'useless-provides', p) for p in pkg.provides(): value = Pkg.formatRequire(*p) self._unexpanded_macros(pkg, 'Provides %s' % (value,), value) for c in pkg.conflicts(): value = Pkg.formatRequire(*c) self._unexpanded_macros(pkg, 'Conflicts %s' % (value,), value) obss = pkg.obsoletes() if obss: provs = pkg.provides() for prov in provs: for obs in obss: if Pkg.rangeCompare(obs, prov): printWarning(pkg, 'self-obsoletion', '%s obsoletes %s' % (Pkg.formatRequire(*obs), Pkg.formatRequire(*prov))) expfmt = rpm.expandMacro("%{_build_name_fmt}") if pkg.isSource(): # _build_name_fmt often (always?) ends up not outputting src/nosrc # as arch for source packages, do it ourselves expfmt = re.sub(r'(?i)%\{?ARCH\b\}?', pkg.arch, expfmt) expected = pkg.header.sprintf(expfmt).split("/")[-1] basename = os.path.basename(pkg.filename) if basename != expected: printWarning(pkg, 'non-coherent-filename', basename, expected) for tag in ('Distribution', 'DistTag', 'ExcludeArch', 'ExcludeOS', 'Vendor'): if hasattr(rpm, 'RPMTAG_%s' % tag.upper()): res = Pkg.b2s(pkg[getattr(rpm, 'RPMTAG_%s' % tag.upper())]) self._unexpanded_macros(pkg, tag, res) for path in private_so_paths: for fname, pkgfile in pkg.files().items(): if fname.startswith(path): for prov in pkgfile.provides: if so_dep_regex.search(prov[0]): printWarning(pkg, "private-shared-object-provides", fname, Pkg.formatRequire(*prov))
def check(self, pkg): global _policy_legacy_exceptions if pkg.isSource(): return # Only check unsuffixed lib* packages if pkg.name.endswith('-devel') or pkg.name.endswith('-doc'): return files = pkg.files() # Search for shared libraries in this package libs = set() libs_needed = set() libs_to_dir = dict() dirs = set() reqlibs = set() pkg_requires = set( map(lambda x: string.split(x[0], '(')[0], pkg.requires())) for f, pkgfile in files.items(): if f.find('.so.') != -1 or f.endswith('.so'): filename = pkg.dirName() + '/' + f try: if stat.S_ISREG(files[f].mode) and 'ELF' in pkgfile.magic: bi = BinaryInfo(pkg, filename, f, False, True) libs_needed = libs_needed.union(bi.needed) if bi.soname != 0: lib_dir = string.join(f.split('/')[:-1], '/') libs.add(bi.soname) libs_to_dir[bi.soname] = lib_dir dirs.add(lib_dir) if bi.soname in pkg_requires: # But not if the library is used by the pkg itself # This avoids program packages with their own private lib # FIXME: we'd need to check if somebody else links to this lib reqlibs.add(bi.soname) except: pass pass std_dirs = dirs.intersection( ('/lib', '/lib64', '/usr/lib', '/usr/lib64', '/opt/kde3/lib', '/opt/kde3/lib64')) non_std_dirs = dirs.difference(std_dirs) # If this is a program package (all libs it provides are # required by itself), bail out if not pkg.name.startswith("lib") and len( libs.difference(reqlibs)) == 0: return std_lib_package = False if pkg.name.startswith("lib") and pkg.name[-1].isdigit(): std_lib_package = True # ignore libs in a versioned non_std_dir if std_lib_package: for lib in libs.copy(): lib_dir = libs_to_dir[lib] if lib_dir.startswith("/opt/kde3"): continue for lib_part in lib_dir.split('/'): if len(lib_part) == 0: continue if lib_part[-1].isdigit( ) and not lib_part.endswith("lib64"): libs.remove(lib) break # Check for non-versioned libs in a std lib package if std_lib_package: for lib in libs.copy(): if not lib[-1].isdigit(): printWarning(pkg, "shlib-unversioned-lib", lib) libs.remove(lib) # If this package should be or should be splitted into shlib # package(s) if len(libs) > 0 and len(std_dirs) > 0: # If the package contains a single shlib, name after soname if len(libs) == 1: soname = libs.copy().pop() libname = libname_from_soname(soname) if libname.startswith('lib') and pkg.name != libname and \ pkg.name != libname + "-mini": if libname in _policy_legacy_exceptions: printWarning(pkg, 'shlib-legacy-policy-name-error', libname) else: printError(pkg, 'shlib-policy-name-error', libname) elif not pkg.name[-1:].isdigit(): printError(pkg, 'shlib-policy-missing-suffix') if (not pkg.name.startswith('lib')) or pkg.name.endswith('-lang'): return if not libs: if pkg.name in _policy_legacy_exceptions: printWarning(pkg, 'shlib-legacy-policy-missing-lib', pkg.name) else: printError(pkg, 'shlib-policy-missing-lib') # Verify no non-lib stuff is in the package dirs = set() for f in files: if os.path.isdir(pkg.dirName() + f): dirs.add(f) # Verify shared lib policy package doesn't have hard dependency on non-lib packages if std_lib_package: for dep in pkg.requires(): if (dep[0].startswith('rpmlib(') or dep[0].startswith('config(')): continue if (dep[1] & (rpm.RPMSENSE_GREATER | rpm.RPMSENSE_EQUAL)) == rpm.RPMSENSE_EQUAL: printWarning(pkg, "shlib-fixed-dependency", Pkg.formatRequire(dep[0], dep[1], dep[2])) # Verify non-lib stuff does not add dependencies if libs: for dep in pkg_requires.difference(_essential_dependencies): if dep.find( '.so.' ) != -1 and not dep in libs and not dep in libs_needed: printError(pkg, 'shlib-policy-excessive-dependency', dep) # Check for non-versioned directories beyond sysdirs in package sysdirs = [ '/lib', '/lib64', '/usr/lib', '/usr/lib64', '/usr/share/doc/packages', '/usr/share' ] cdirs = set() for sysdir in sysdirs: done = set() for dir in dirs: if dir.startswith(sysdir + '/'): ssdir = string.split(dir[len(sysdir) + 1:], '/')[0] if not ssdir[-1].isdigit(): cdirs.add(sysdir + '/' + ssdir) done.add(dir) dirs = dirs.difference(done) map(lambda dir: printError(pkg, 'shlib-policy-nonversioned-dir', dir), cdirs)
def check(self, pkg): packager = pkg[rpm.RPMTAG_PACKAGER] if packager: self._unexpanded_macros(pkg, 'Packager', packager) if Config.getOption('Packager') and \ not packager_regex.search(packager): printWarning(pkg, 'invalid-packager', packager) else: printError(pkg, 'no-packager-tag') version = pkg[rpm.RPMTAG_VERSION] if version: self._unexpanded_macros(pkg, 'Version', version) res = invalid_version_regex.search(version) if res: printError(pkg, 'invalid-version', version) else: printError(pkg, 'no-version-tag') release = pkg[rpm.RPMTAG_RELEASE] if release: self._unexpanded_macros(pkg, 'Release', release) if release_ext and not extension_regex.search(release): printWarning(pkg, 'not-standard-release-extension', release) else: printError(pkg, 'no-release-tag') epoch = pkg[rpm.RPMTAG_EPOCH] if epoch is None: if use_epoch: printError(pkg, 'no-epoch-tag') else: if epoch > 99: printWarning(pkg, 'unreasonable-epoch', epoch) epoch = str(epoch) if use_epoch: for tag in "obsoletes", "conflicts", "provides", "recommends", \ "suggests", "enhances", "supplements": for x in (x for x in getattr(pkg, tag)() if x[1] and x[2][0] is None): printWarning(pkg, 'no-epoch-in-%s' % tag, Pkg.formatRequire(*x)) name = pkg.name deps = pkg.requires() + pkg.prereq() devel_depend = False is_devel = FilesCheck.devel_regex.search(name) is_source = pkg.isSource() for d in deps: value = Pkg.formatRequire(*d) if use_epoch and d[1] and d[2][0] is None and \ not d[0].startswith('rpmlib('): printWarning(pkg, 'no-epoch-in-dependency', value) for r in INVALID_REQUIRES: if r.search(d[0]): printError(pkg, 'invalid-dependency', d[0]) if d[0].startswith('/usr/local/'): printError(pkg, 'invalid-dependency', d[0]) if is_source: if lib_devel_number_regex.search(d[0]): printError(pkg, 'invalid-build-requires', d[0]) elif not is_devel: if not devel_depend and FilesCheck.devel_regex.search(d[0]): printError(pkg, 'devel-dependency', d[0]) devel_depend = True if not d[1]: res = lib_package_regex.search(d[0]) if res and not res.group(1): printError(pkg, 'explicit-lib-dependency', d[0]) if d[1] == rpm.RPMSENSE_EQUAL and d[2][2] is not None: printWarning(pkg, 'requires-on-release', value) self._unexpanded_macros(pkg, 'dependency %s' % (value,), value) self._unexpanded_macros(pkg, 'Name', name) if not name: printError(pkg, 'no-name-tag') else: if is_devel and not is_source: base = is_devel.group(1) dep = None has_so = False for fname in pkg.files(): if fname.endswith('.so'): has_so = True break if has_so: base_or_libs = base + '/' + base + '-libs/lib' + base # try to match *%_isa as well (e.g. "(x86-64)", "(x86-32)") base_or_libs_re = re.compile( '^(lib)?%s(-libs)?(\(\w+-\d+\))?$' % re.escape(base)) for d in deps: if base_or_libs_re.match(d[0]): dep = d break if not dep: printWarning(pkg, 'no-dependency-on', base_or_libs) elif version: exp = (epoch, version, None) sexp = Pkg.versionToString(exp) if not dep[1]: printWarning(pkg, 'no-version-dependency-on', base_or_libs, sexp) elif dep[2][:2] != exp[:2]: printWarning(pkg, 'incoherent-version-dependency-on', base_or_libs, Pkg.versionToString((dep[2][0], dep[2][1], None)), sexp) res = devel_number_regex.search(name) if not res: printWarning(pkg, 'no-major-in-name', name) else: if res.group(3): prov = res.group(1) + res.group(2) + '-devel' else: prov = res.group(1) + '-devel' if prov not in (x[0] for x in pkg.provides()): printWarning(pkg, 'no-provides', prov) # List of words to ignore in spell check ignored_words = set() for pf in pkg.files(): ignored_words.update(pf.split('/')) ignored_words.update((x[0] for x in pkg.provides())) ignored_words.update((x[0] for x in pkg.requires())) ignored_words.update((x[0] for x in pkg.conflicts())) ignored_words.update((x[0] for x in pkg.obsoletes())) langs = pkg[rpm.RPMTAG_HEADERI18NTABLE] summary = pkg[rpm.RPMTAG_SUMMARY] if summary: if not langs: self._unexpanded_macros(pkg, 'Summary', Pkg.b2s(summary)) else: for lang in langs: self.check_summary(pkg, lang, ignored_words) else: printError(pkg, 'no-summary-tag') description = pkg[rpm.RPMTAG_DESCRIPTION] if description: if not langs: self._unexpanded_macros(pkg, '%description', Pkg.b2s(description)) else: for lang in langs: self.check_description(pkg, lang, ignored_words) else: printError(pkg, 'no-description-tag') group = pkg[rpm.RPMTAG_GROUP] self._unexpanded_macros(pkg, 'Group', group) if not group: printError(pkg, 'no-group-tag') elif VALID_GROUPS and group not in VALID_GROUPS: printWarning(pkg, 'non-standard-group', group) buildhost = pkg[rpm.RPMTAG_BUILDHOST] self._unexpanded_macros(pkg, 'BuildHost', buildhost) if not buildhost: printError(pkg, 'no-buildhost-tag') elif Config.getOption('ValidBuildHost') and \ not valid_buildhost_regex.search(buildhost): printWarning(pkg, 'invalid-buildhost', buildhost) changelog = pkg[rpm.RPMTAG_CHANGELOGNAME] if not changelog: printError(pkg, 'no-changelogname-tag') else: clt = pkg[rpm.RPMTAG_CHANGELOGTEXT] if use_version_in_changelog: ret = changelog_version_regex.search(Pkg.b2s(changelog[0])) if not ret and clt: # we also allow the version specified as the first # thing on the first line of the text ret = changelog_text_version_regex.search(Pkg.b2s(clt[0])) if not ret: printWarning(pkg, 'no-version-in-last-changelog') elif version and release: srpm = pkg[rpm.RPMTAG_SOURCERPM] or '' # only check when source name correspond to name if srpm[0:-8] == '%s-%s-%s' % (name, version, release): expected = [version + '-' + release] if epoch is not None: # regardless of use_epoch expected[0] = str(epoch) + ':' + expected[0] # Allow EVR in changelog without release extension, # the extension is often a macro or otherwise dynamic. if release_ext: expected.append( extension_regex.sub('', expected[0])) if ret.group(1) not in expected: if len(expected) == 1: expected = expected[0] printWarning(pkg, 'incoherent-version-in-changelog', ret.group(1), expected) if use_utf8: if clt: changelog = changelog + clt for s in changelog: if not Pkg.is_utf8_bytestr(s): printError(pkg, 'tag-not-utf8', '%changelog') break clt = pkg[rpm.RPMTAG_CHANGELOGTIME][0] if clt: clt -= clt % (24*3600) # roll back to 00:00:00, see #246 if clt < oldest_changelog_timestamp: printWarning(pkg, 'changelog-time-overflow', time.strftime("%Y-%m-%d", time.gmtime(clt))) elif clt > time.time(): printError(pkg, 'changelog-time-in-future', time.strftime("%Y-%m-%d", time.gmtime(clt))) # for provide_name in (x[0] for x in pkg.provides()): # if name == provide_name: # printWarning(pkg, 'package-provides-itself') # break def split_license(license): return (x.strip() for x in (l for l in license_regex.split(license) if l)) rpm_license = pkg[rpm.RPMTAG_LICENSE] if not rpm_license: printError(pkg, 'no-license') else: valid_license = True if rpm_license not in VALID_LICENSES: for l1 in split_license(rpm_license): if l1 in VALID_LICENSES: continue for l2 in split_license(l1): if l2 not in VALID_LICENSES: printWarning(pkg, 'invalid-license', l2) valid_license = False if not valid_license: self._unexpanded_macros(pkg, 'License', rpm_license) for tag in ('URL', 'DistURL', 'BugURL'): if hasattr(rpm, 'RPMTAG_%s' % tag.upper()): url = Pkg.b2s(pkg[getattr(rpm, 'RPMTAG_%s' % tag.upper())]) self._unexpanded_macros(pkg, tag, url, is_url=True) if url: (scheme, netloc) = urlparse(url)[0:2] if not scheme or not netloc or "." not in netloc or \ scheme not in ('http', 'https', 'ftp') or \ (Config.getOption('InvalidURL') and invalid_url_regex.search(url)): printWarning(pkg, 'invalid-url', tag, url) else: self.check_url(pkg, tag, url) elif tag == 'URL': printWarning(pkg, 'no-url-tag') obs_names = [x[0] for x in pkg.obsoletes()] prov_names = [x[0] for x in pkg.provides()] for o in (x for x in obs_names if x not in prov_names): printWarning(pkg, 'obsolete-not-provided', o) for o in pkg.obsoletes(): value = Pkg.formatRequire(*o) self._unexpanded_macros(pkg, 'Obsoletes %s' % (value,), value) # TODO: should take versions, <, <=, =, >=, > into account here # https://bugzilla.redhat.com/460872 useless_provides = [] for p in prov_names: if prov_names.count(p) != 1 and p not in useless_provides: useless_provides.append(p) for p in useless_provides: printError(pkg, 'useless-provides', p) for p in pkg.provides(): value = Pkg.formatRequire(*p) self._unexpanded_macros(pkg, 'Provides %s' % (value,), value) for c in pkg.conflicts(): value = Pkg.formatRequire(*c) self._unexpanded_macros(pkg, 'Conflicts %s' % (value,), value) obss = pkg.obsoletes() if obss: provs = pkg.provides() for prov in provs: for obs in obss: if Pkg.rangeCompare(obs, prov): printWarning(pkg, 'self-obsoletion', '%s obsoletes %s' % (Pkg.formatRequire(*obs), Pkg.formatRequire(*prov))) expfmt = rpm.expandMacro("%{_build_name_fmt}") if pkg.isSource(): # _build_name_fmt often (always?) ends up not outputting src/nosrc # as arch for source packages, do it ourselves expfmt = re.sub(r'(?i)%\{?ARCH\b\}?', pkg.arch, expfmt) expected = pkg.header.sprintf(expfmt).split("/")[-1] basename = os.path.basename(pkg.filename) if basename != expected: printWarning(pkg, 'non-coherent-filename', basename, expected) for tag in ('Distribution', 'DistTag', 'ExcludeArch', 'ExcludeOS', 'Vendor'): if hasattr(rpm, 'RPMTAG_%s' % tag.upper()): self._unexpanded_macros(pkg, tag, Pkg.b2s(pkg[getattr(rpm, 'RPMTAG_%s' % tag.upper())])) for path in private_so_paths: for fname, pkgfile in pkg.files().items(): if fname.startswith(path): for prov in pkgfile.provides: if so_dep_regex.search(prov[0]): printWarning(pkg, "private-shared-object-provides", fname, Pkg.formatRequire(*prov))