348 lines
11 KiB
Python
348 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
|
#############################################################################
|
|
# File : rpmlint.py
|
|
# Package : rpmlint
|
|
# Author : Frederic Lepied
|
|
# Created on : Mon Sep 27 19:20:18 1999
|
|
# Version : $Id$
|
|
# Purpose : main entry point: process options, load the checks and run
|
|
# the checks.
|
|
#############################################################################
|
|
|
|
import getopt
|
|
import glob
|
|
import imp
|
|
import locale
|
|
import os
|
|
import stat
|
|
import sys
|
|
import tempfile
|
|
|
|
# Do not import anything that initializes its global variables from
|
|
# Config at load time here (or anything that imports such a thing),
|
|
# that results in those variables initialized before config files are
|
|
# loaded which is too early - settings from config files won't take
|
|
# place for those variables.
|
|
|
|
from Filter import badnessScore, badnessThreshold, printAllReasons, \
|
|
printDescriptions, printInfo, printed_messages
|
|
import AbstractCheck
|
|
import Config
|
|
import Pkg
|
|
|
|
|
|
version = '@VERSION@'
|
|
|
|
_default_user_conf = '%s/rpmlint' % \
|
|
(os.environ.get('XDG_CONFIG_HOME') or '~/.config')
|
|
|
|
# Print usage information
|
|
def usage(name):
|
|
Pkg.rlprint('''usage: %s [<options>] <rpm files|installed packages|specfiles|dirs>
|
|
options:
|
|
\t[-i|--info]
|
|
\t[-I <messageid,messageid,...>]
|
|
\t[-c|--check <check>]
|
|
\t[-a|--all]
|
|
\t[-C|--checkdir <checkdir>]
|
|
\t[-h|--help]
|
|
\t[-v|--verbose]
|
|
\t[-E|--extractdir <dir>]
|
|
\t[-V|--version]
|
|
\t[-n|--noexception]
|
|
\t[-f|--file <user config file to use instead of %s]
|
|
\t[-o|--option <key value>]''' \
|
|
% (name, _default_user_conf))
|
|
|
|
# Print version information
|
|
def printVersion():
|
|
Pkg.rlprint('rpmlint version %s Copyright (C) 1999-2007 Frederic Lepied, Mandriva' % version)
|
|
|
|
def loadCheck(name):
|
|
'''Load a (check) module by its name, unless it is already loaded.'''
|
|
# Avoid loading more than once (initialization costs)
|
|
loaded = sys.modules.get(name)
|
|
if loaded:
|
|
return loaded
|
|
(fobj, pathname, description) = imp.find_module(name)
|
|
try:
|
|
imp.load_module(name, fobj, pathname, description)
|
|
finally:
|
|
fobj.close()
|
|
|
|
#############################################################################
|
|
# main program
|
|
#############################################################################
|
|
def main():
|
|
|
|
locale.setlocale(locale.LC_COLLATE, '')
|
|
|
|
# Add check dirs to the front of load path
|
|
sys.path[0:0] = Config.checkDirs()
|
|
|
|
# Load all checks
|
|
for c in Config.allChecks():
|
|
loadCheck(c)
|
|
|
|
packages_checked = 0
|
|
specfiles_checked = 0
|
|
do_spec_check = 'SpecCheck' in Config.allChecks()
|
|
if do_spec_check:
|
|
# See comments in "top level import section" for why this isn't
|
|
# imported earlier.
|
|
import SpecCheck
|
|
|
|
try:
|
|
# Loop over all file names given in arguments
|
|
dirs = []
|
|
for arg in args:
|
|
pkgs = []
|
|
isfile = False
|
|
try:
|
|
try:
|
|
st = os.stat(arg)
|
|
isfile = True
|
|
if stat.S_ISREG(st[stat.ST_MODE]):
|
|
if not arg.endswith(".spec"):
|
|
pkgs.append(Pkg.Pkg(arg, extract_dir))
|
|
elif do_spec_check:
|
|
# Short-circuit spec file checks
|
|
pkg = Pkg.FakePkg(arg)
|
|
check = SpecCheck.SpecCheck()
|
|
check.check_spec(pkg, arg)
|
|
pkg.cleanup()
|
|
specfiles_checked += 1
|
|
|
|
elif stat.S_ISDIR(st[stat.ST_MODE]):
|
|
dirs.append(arg)
|
|
continue
|
|
else:
|
|
raise OSError
|
|
except OSError:
|
|
ipkgs = Pkg.getInstalledPkgs(arg)
|
|
if not ipkgs:
|
|
Pkg.rlwarn(
|
|
'(none): E: no installed packages by name %s'
|
|
% arg)
|
|
else:
|
|
ipkgs.sort(key = lambda x: locale.strxfrm(
|
|
x.header.sprintf("%{NAME}.%{ARCH}")))
|
|
pkgs.extend(ipkgs)
|
|
except KeyboardInterrupt:
|
|
if isfile:
|
|
arg = os.path.abspath(arg)
|
|
Pkg.rlwarn(
|
|
'(none): E: interrupted, exiting while reading %s' % arg)
|
|
sys.exit(2)
|
|
except Exception, e:
|
|
if isfile:
|
|
arg = os.path.abspath(arg)
|
|
Pkg.rlwarn(
|
|
'(none): E: error while reading %s: %s' % (arg, e))
|
|
pkgs = []
|
|
continue
|
|
|
|
for pkg in pkgs:
|
|
runChecks(pkg)
|
|
packages_checked += 1
|
|
|
|
for dname in dirs:
|
|
try:
|
|
for path, dirs, files in os.walk(dname):
|
|
for fname in files:
|
|
fname = os.path.abspath(os.path.join(path, fname))
|
|
try:
|
|
if fname.endswith('.rpm') or \
|
|
fname.endswith('.spm'):
|
|
pkg = Pkg.Pkg(fname, extract_dir)
|
|
runChecks(pkg)
|
|
packages_checked += 1
|
|
|
|
elif do_spec_check and fname.endswith('.spec'):
|
|
pkg = Pkg.FakePkg(fname)
|
|
check = SpecCheck.SpecCheck()
|
|
check.check_spec(pkg, fname)
|
|
pkg.cleanup()
|
|
specfiles_checked += 1
|
|
|
|
except KeyboardInterrupt:
|
|
Pkg.rlwarn(
|
|
'(none): E: interrupted, exiting while ' +
|
|
'reading %s' % fname)
|
|
sys.exit(2)
|
|
except Exception, e:
|
|
Pkg.rlwarn(
|
|
'(none): E: while reading %s: %s' % (fname, e))
|
|
continue
|
|
except Exception, e:
|
|
Pkg.rlwarn(
|
|
'(none): E: error while reading dir %s: %s' % (dname, e))
|
|
continue
|
|
|
|
if printAllReasons():
|
|
Pkg.rlwarn(
|
|
'(none): E: badness %d exceeds threshold %d, aborting.' %
|
|
(badnessScore(), badnessThreshold()))
|
|
sys.exit(66)
|
|
|
|
finally:
|
|
print "%d packages and %d specfiles checked; %d errors, %d warnings." \
|
|
% (packages_checked, specfiles_checked,
|
|
printed_messages["E"], printed_messages["W"])
|
|
|
|
if printed_messages["E"] > 0:
|
|
sys.exit(64)
|
|
sys.exit(0)
|
|
|
|
def runChecks(pkg):
|
|
|
|
try:
|
|
if verbose:
|
|
printInfo(pkg, 'checking')
|
|
|
|
for name in Config.allChecks():
|
|
check = AbstractCheck.AbstractCheck.known_checks.get(name)
|
|
if check:
|
|
check.check(pkg)
|
|
else:
|
|
Pkg.rlwarn('(none): W: unknown check %s, skipping' % name)
|
|
finally:
|
|
pkg.cleanup()
|
|
|
|
#############################################################################
|
|
#
|
|
#############################################################################
|
|
|
|
sys.argv[0] = os.path.basename(sys.argv[0])
|
|
|
|
# parse options
|
|
try:
|
|
(opt, args) = getopt.getopt(sys.argv[1:],
|
|
'iI:c:C:hVvanE:f:o:',
|
|
['info',
|
|
'check=',
|
|
'checkdir=',
|
|
'help',
|
|
'version',
|
|
'verbose',
|
|
'all',
|
|
'noexception',
|
|
'extractdir=',
|
|
'file=',
|
|
'option=',
|
|
])
|
|
except getopt.error, e:
|
|
Pkg.rlwarn("%s: %s" % (sys.argv[0], e))
|
|
usage(sys.argv[0])
|
|
sys.exit(1)
|
|
|
|
# process options
|
|
checkdir = '/usr/share/rpmlint'
|
|
checks = []
|
|
verbose = False
|
|
extract_dir = None
|
|
conf_file = _default_user_conf
|
|
if not os.path.exists(os.path.expanduser(conf_file)):
|
|
# deprecated backwards compatibility with < 0.88
|
|
conf_file = '~/.rpmlintrc'
|
|
info_error = None
|
|
|
|
# load global config files
|
|
configs = glob.glob('/etc/rpmlint/*config')
|
|
configs.sort()
|
|
configs.insert(0, '/usr/share/rpmlint/config')
|
|
for f in configs:
|
|
try:
|
|
execfile(f)
|
|
except IOError:
|
|
pass
|
|
except Exception, E:
|
|
Pkg.rlwarn('(none): W: error loading %s, skipping: %s' % (f, E))
|
|
# pychecker fix
|
|
del f
|
|
|
|
config_overrides = {}
|
|
|
|
# process command line options
|
|
for o in opt:
|
|
if o[0] in ('-c', '--check'):
|
|
checks.append(o[1])
|
|
elif o[0] in('-i', '--info'):
|
|
Config.info = True
|
|
elif o[0] == '-I':
|
|
info_error = o[1]
|
|
elif o[0] in ('-h', '--help'):
|
|
usage(sys.argv[0])
|
|
sys.exit(0)
|
|
elif o[0] in ('-C', '--checkdir'):
|
|
Config.addCheckDir(o[1])
|
|
elif o[0] in ('-v', '--verbose'):
|
|
verbose = True
|
|
elif o[0] in ('-V', '--version'):
|
|
printVersion()
|
|
sys.exit(0)
|
|
elif o[0] in ('-E', '--extractdir'):
|
|
extract_dir = o[1]
|
|
Config.setOption('ExtractDir', extract_dir)
|
|
elif o[0] in ('-n', '--noexception'):
|
|
Config.no_exception = True
|
|
elif o[0] in ('-a', '--all'):
|
|
if '*' not in args:
|
|
args.append('*')
|
|
elif o[0] in ('-f', '--file'):
|
|
conf_file = o[1]
|
|
elif o[0] in ('-o', '--option'):
|
|
kv = o[1].split(None, 1)
|
|
if len(kv) == 1:
|
|
config_overrides[kv[0]] = None
|
|
else:
|
|
config_overrides[kv[0]] = eval(kv[1])
|
|
else:
|
|
print 'unknown option', o
|
|
|
|
# load user config file
|
|
try:
|
|
execfile(os.path.expanduser(conf_file))
|
|
except IOError:
|
|
pass
|
|
except Exception,E:
|
|
Pkg.rlwarn('(none): W: error loading %s, skipping: %s' % (conf_file, E))
|
|
|
|
# apply config overrides
|
|
for key, value in config_overrides.items():
|
|
Config.setOption(key, value)
|
|
|
|
if not extract_dir:
|
|
extract_dir = Config.getOption('ExtractDir', tempfile.gettempdir())
|
|
|
|
if info_error:
|
|
Config.info = True
|
|
for c in checks:
|
|
Config.addCheck(c)
|
|
for c in Config.allChecks():
|
|
loadCheck(c)
|
|
for e in info_error.split(','):
|
|
print "%s:" % e
|
|
printDescriptions(e)
|
|
sys.exit(0)
|
|
|
|
# if no argument print usage
|
|
if not args:
|
|
usage(sys.argv[0])
|
|
sys.exit(1)
|
|
|
|
if __name__ == '__main__':
|
|
if checks:
|
|
Config.resetChecks()
|
|
for check in checks:
|
|
Config.addCheck(check)
|
|
main()
|
|
|
|
# rpmlint.py ends here
|
|
|
|
# Local variables:
|
|
# indent-tabs-mode: nil
|
|
# py-indent-offset: 4
|
|
# End:
|
|
# ex: ts=4 sw=4 et
|