add Development Groups
[platform/upstream/rpmlint.git] / rpmlint
1 #!/usr/bin/python -ttOu
2 # -*- coding: utf-8 -*-
3 #############################################################################
4 # File          : rpmlint
5 # Package       : rpmlint
6 # Author        : Frederic Lepied
7 # Created on    : Mon Sep 27 19:20:18 1999
8 # Version       : $Id: rpmlint 1870 2011-06-18 09:19:24Z scop $
9 # Purpose       : main entry point: process options, load the checks and run
10 #                 the checks.
11 #############################################################################
12
13 import getopt
14 import glob
15 import imp
16 import locale
17 import os
18 import re
19 import stat
20 import sys
21 import tempfile
22
23 # 1 instead of 0 here because we want the script dir to be looked up first,
24 # e.g. for in-place from tarball or SCM checkout
25 sys.path.insert(1, '/usr/share/rpmlint')
26
27 # Do not import anything that initializes its global variables from
28 # Config at load time here (or anything that imports such a thing),
29 # that results in those variables initialized before config files are
30 # loaded which is too early - settings from config files won't take
31 # place for those variables.
32
33 from Filter import badnessScore, badnessThreshold, printAllReasons, \
34      printDescriptions, printInfo, printed_messages, setRawOut
35 import AbstractCheck
36 import Config
37 import Pkg
38
39
40 _default_user_conf = '%s/rpmlint' % \
41     (os.environ.get('XDG_CONFIG_HOME') or '~/.config')
42
43 # Print usage information
44 def usage(name):
45     print ('''usage: %s [<options>] <rpm files|installed packages|specfiles|dirs>
46   options:
47 \t[-i|--info]
48 \t[-I|--explain <messageid>]
49 \t[-c|--check <check>]
50 \t[-a|--all]
51 \t[-C|--checkdir <checkdir>]
52 \t[-h|--help]
53 \t[-v|--verbose]
54 \t[-E|--extractdir <dir>]
55 \t[-V|--version]
56 \t[-n|--noexception]
57 \t[   --rawout <file>]
58 \t[-f|--file <user config file to use instead of %s]
59 \t[-o|--option <key value>]''' \
60         % (name, _default_user_conf))
61
62 # Print version information
63 def printVersion():
64     print ('rpmlint version %s Copyright (C) 1999-2007 Frederic Lepied, Mandriva' % Config.__version__)
65
66 def loadCheck(name):
67     '''Load a (check) module by its name, unless it is already loaded.'''
68     # Avoid loading more than once (initialization costs)
69     loaded = sys.modules.get(name)
70     if loaded:
71         return loaded
72     (fobj, pathname, description) = imp.find_module(name)
73     try:
74         imp.load_module(name, fobj, pathname, description)
75     finally:
76         fobj.close()
77
78 #############################################################################
79 # main program
80 #############################################################################
81 def main():
82
83     locale.setlocale(locale.LC_COLLATE, '')
84
85     # Add check dirs to the front of load path
86     sys.path[0:0] = Config.checkDirs()
87
88     # Load all checks
89     for c in Config.allChecks():
90         loadCheck(c)
91
92     packages_checked = 0
93     specfiles_checked = 0
94     do_spec_check = 'SpecCheck' in Config.allChecks()
95     if do_spec_check:
96         # See comments in "top level import section" for why this isn't
97         # imported earlier.
98         import SpecCheck
99
100     try:
101         # Loop over all file names given in arguments
102         dirs = []
103         for arg in args:
104             pkgs = []
105             isfile = False
106             try:
107                 if arg == "-":
108                     arg = "(standard input)"
109                     # Short-circuit stdin spec file check
110                     if do_spec_check:
111                         stdin = sys.stdin.readlines()
112                         if not stdin:
113                             continue
114                         pkg = Pkg.FakePkg(arg)
115                         check = SpecCheck.SpecCheck()
116                         check.verbose = verbose
117                         check.check_spec(pkg, None, spec_lines=stdin)
118                         pkg.cleanup()
119                         specfiles_checked += 1
120                     continue
121
122                 try:
123                     st = os.stat(arg)
124                     isfile = True
125                     if stat.S_ISREG(st[stat.ST_MODE]):
126                         if arg.endswith(".spec"):
127                             if do_spec_check:
128                                 # Short-circuit spec file checks
129                                 pkg = Pkg.FakePkg(arg)
130                                 check = SpecCheck.SpecCheck()
131                                 check.verbose = verbose
132                                 check.check_spec(pkg, arg)
133                                 pkg.cleanup()
134                                 specfiles_checked += 1
135                         elif "/" in arg or arg.endswith(".rpm") or \
136                                 arg.endswith(".spm"):
137                             pkgs.append(Pkg.Pkg(arg, extract_dir))
138                         else:
139                             raise OSError
140
141                     elif stat.S_ISDIR(st[stat.ST_MODE]):
142                         dirs.append(arg)
143                         continue
144                     else:
145                         raise OSError
146                 except OSError:
147                     ipkgs = Pkg.getInstalledPkgs(arg)
148                     if not ipkgs:
149                         Pkg.warn(
150                             '(none): E: no installed packages by name %s' % arg)
151                     else:
152                         ipkgs.sort(key = lambda x: locale.strxfrm(
153                                 x.header.sprintf("%{NAME}.%{ARCH}")))
154                         pkgs.extend(ipkgs)
155             except KeyboardInterrupt:
156                 if isfile:
157                     arg = os.path.abspath(arg)
158                 Pkg.warn(
159                     '(none): E: interrupted, exiting while reading %s' % arg)
160                 sys.exit(2)
161             except Exception, e:
162                 if isfile:
163                     arg = os.path.abspath(arg)
164                 Pkg.warn('(none): E: error while reading %s: %s' % (arg, e))
165                 pkgs = []
166                 continue
167
168             for pkg in pkgs:
169                 runChecks(pkg)
170                 packages_checked += 1
171
172         for dname in dirs:
173             try:
174                 for path, dirs, files in os.walk(dname):
175                     for fname in files:
176                         fname = os.path.abspath(os.path.join(path, fname))
177                         try:
178                             if fname.endswith('.rpm') or \
179                                fname.endswith('.spm'):
180                                 pkg = Pkg.Pkg(fname, extract_dir)
181                                 runChecks(pkg)
182                                 packages_checked += 1
183
184                             elif do_spec_check and fname.endswith('.spec'):
185                                 pkg = Pkg.FakePkg(fname)
186                                 check = SpecCheck.SpecCheck()
187                                 check.verbose = verbose
188                                 check.check_spec(pkg, fname)
189                                 pkg.cleanup()
190                                 specfiles_checked += 1
191
192                         except KeyboardInterrupt:
193                             Pkg.warn('(none): E: interrupted, exiting while ' +
194                                      'reading %s' % fname)
195                             sys.exit(2)
196                         except Exception, e:
197                             Pkg.warn(
198                                 '(none): E: while reading %s: %s' % (fname, e))
199                             continue
200             except Exception, e:
201                 Pkg.warn(
202                     '(none): E: error while reading dir %s: %s' % (dname, e))
203                 continue
204
205         if printAllReasons():
206             Pkg.warn('(none): E: badness %d exceeds threshold %d, aborting.' %
207                      (badnessScore(), badnessThreshold()))
208             sys.exit(66)
209
210     finally:
211         print "%d packages and %d specfiles checked; %d errors, %d warnings." \
212               % (packages_checked, specfiles_checked,
213                  printed_messages["E"], printed_messages["W"])
214
215     if badnessThreshold() < 0 and printed_messages["E"] > 0:
216         sys.exit(64)
217     sys.exit(0)
218
219 def runChecks(pkg):
220
221     try:
222         if verbose:
223             printInfo(pkg, 'checking')
224
225         for name in Config.allChecks():
226             check = AbstractCheck.AbstractCheck.known_checks.get(name)
227             if check:
228                 check.verbose = verbose
229                 check.check(pkg)
230             else:
231                 Pkg.warn('(none): W: unknown check %s, skipping' % name)
232     finally:
233         pkg.cleanup()
234
235 #############################################################################
236 #
237 #############################################################################
238
239 sys.argv[0] = os.path.basename(sys.argv[0])
240
241 # parse options
242 try:
243     (opt, args) = getopt.getopt(sys.argv[1:],
244                               'iI:c:C:hVvanE:f:o:',
245                               ['info',
246                                'explain=',
247                                'check=',
248                                'checkdir=',
249                                'help',
250                                'version',
251                                'verbose',
252                                'all',
253                                'noexception',
254                                'extractdir=',
255                                'file=',
256                                'option=',
257                                'rawout=',
258                                ])
259 except getopt.GetoptError, e:
260     Pkg.warn("%s: %s" % (sys.argv[0], e))
261     usage(sys.argv[0])
262     sys.exit(1)
263
264 # process options
265 checkdir = '/usr/share/rpmlint'
266 checks = []
267 verbose = False
268 extract_dir = None
269 conf_file = _default_user_conf
270 if not os.path.exists(os.path.expanduser(conf_file)):
271     # deprecated backwards compatibility with < 0.88
272     conf_file = '~/.rpmlintrc'
273 info_error = set()
274
275 # load global config files
276 configs = glob.glob('/etc/rpmlint/*config')
277 configs.sort()
278
279 # Was rpmlint invoked as a prefixed variant?
280 m = re.match(r"(?P<prefix>[\w-]+)-rpmlint(\.py)?", sys.argv[0])
281 if m:
282     # Okay, we're a prefixed variant. Look for the variant config.
283     # If we find it, use it. If not, fallback to the default.
284     prefix = m.group('prefix')
285     if os.path.isfile('/usr/share/rpmlint/config.%s' % prefix):
286         configs.insert(0, '/usr/share/rpmlint/config.%s' % prefix)
287     else:
288         configs.insert(0, '/usr/share/rpmlint/config')
289 else:
290     configs.insert(0, '/usr/share/rpmlint/config')
291
292 for f in configs:
293     try:
294         execfile(f)
295     except IOError:
296         pass
297     except Exception, E:
298         Pkg.warn('(none): W: error loading %s, skipping: %s' % (f, E))
299 # pychecker fix
300 del f
301
302 config_overrides = {}
303
304 # process command line options
305 for o in opt:
306     if o[0] in ('-c', '--check'):
307         checks.append(o[1])
308     elif o[0] in ('-i', '--info'):
309         Config.info = True
310     elif o[0] in ('-I', '--explain'):
311         # split by comma for deprecated backwards compatibility with < 1.2
312         info_error.update(o[1].split(','))
313     elif o[0] in ('-h', '--help'):
314         usage(sys.argv[0])
315         sys.exit(0)
316     elif o[0] in ('-C', '--checkdir'):
317         Config.addCheckDir(o[1])
318     elif o[0] in ('-v', '--verbose'):
319         verbose = True
320     elif o[0] in ('-V', '--version'):
321         printVersion()
322         sys.exit(0)
323     elif o[0] in ('-E', '--extractdir'):
324         extract_dir = o[1]
325         Config.setOption('ExtractDir', extract_dir)
326     elif o[0] in ('-n', '--noexception'):
327         Config.no_exception = True
328     elif o[0] in ('-a', '--all'):
329         if '*' not in args:
330             args.append('*')
331     elif o[0] in ('-f', '--file'):
332         conf_file = o[1]
333     elif o[0] in ('-o', '--option'):
334         kv = o[1].split(None, 1)
335         if len(kv) == 1:
336             config_overrides[kv[0]] = None
337         else:
338             config_overrides[kv[0]] = eval(kv[1])
339     elif o[0] in ('--rawout',):
340         setRawOut(o[1])
341
342 # load user config file
343 try:
344     execfile(os.path.expanduser(conf_file))
345 except IOError:
346     pass
347 except Exception,E:
348     Pkg.warn('(none): W: error loading %s, skipping: %s' % (conf_file, E))
349
350 # apply config overrides
351 for key, value in config_overrides.items():
352     Config.setOption(key, value)
353
354 if not extract_dir:
355     extract_dir = Config.getOption('ExtractDir', tempfile.gettempdir())
356
357 if info_error:
358     Config.info = True
359     sys.path[0:0] = Config.checkDirs()
360     for c in checks:
361         Config.addCheck(c)
362     for c in Config.allChecks():
363         loadCheck(c)
364     for e in sorted(info_error):
365         print "%s:" % e
366         printDescriptions(e)
367     sys.exit(0)
368
369 # if no argument print usage
370 if not args:
371     usage(sys.argv[0])
372     sys.exit(1)
373
374 if __name__ == '__main__':
375     if checks:
376         Config.resetChecks()
377         for check in checks:
378             Config.addCheck(check)
379     main()
380
381 # rpmlint ends here
382
383 # Local variables:
384 # indent-tabs-mode: nil
385 # py-indent-offset: 4
386 # End:
387 # ex: ts=4 sw=4 et