8ddbfc4f58dc79a1a3370d59ff6d08e8cb6de3e8
[platform/upstream/rpmlint.git] / SpecCheck.py
1 # -*- coding: utf-8 -*-
2 #############################################################################
3 # File          : SpecCheck.py
4 # Package       : rpmlint
5 # Author        : Frederic Lepied
6 # Created on    : Thu Oct  7 17:06:14 1999
7 # Version       : $Id: SpecCheck.py 1892 2011-11-23 20:21:05Z scop $
8 # Purpose       : check the spec file of a source rpm.
9 #############################################################################
10
11 import re
12 try:
13     from urlparse import urlparse
14 except ImportError: # Python 3
15     from urllib.parse import urlparse
16
17 import rpm
18
19 from Filter import addDetails, printError, printWarning
20 from TagsCheck import VALID_GROUPS
21 import AbstractCheck
22 import Config
23 import Pkg
24
25
26 # Don't check for hardcoded library paths in biarch packages
27 DEFAULT_BIARCH_PACKAGES = '^(gcc|glibc)'
28
29 # Don't check for hardcoded library paths in packages which can have
30 # their noarch files in /usr/lib/<package>/*, or packages that can't
31 # be installed on biarch systems
32 DEFAULT_HARDCODED_LIB_PATH_EXCEPTIONS = '/lib/(modules|cpp|perl5|rpm|hotplug|firmware)($|[\s/,])'
33 patch_regex = re.compile("^Patch(\d*)\s*:\s*(\S+)", re.IGNORECASE)
34 applied_patch_regex = re.compile("^%patch(\d*)")
35 applied_patch_p_regex = re.compile("\s-P\s+(\d+)\\b")
36 applied_patch_pipe_regex = re.compile(r'\s%\{PATCH(\d+)\}\s*\|\s*(%\{?__)?patch\b')
37 source_dir_regex = re.compile("^[^#]*(\$RPM_SOURCE_DIR|%{?_sourcedir}?)")
38 obsolete_tags_regex = re.compile("^(Copyright|Serial)\s*:\s*(\S+)")
39 buildroot_regex = re.compile('^BuildRoot\s*:\s*(\S+)', re.IGNORECASE)
40 prefix_regex = re.compile('^Prefix\s*:\s*(\S+)', re.IGNORECASE)
41 packager_regex = re.compile('^Packager\s*:\s*(\S+)', re.IGNORECASE)
42 buildarch_regex = re.compile('^BuildArch(itectures)?\s*:\s*(.+?)\s*$', re.IGNORECASE)
43 make_check_regex = re.compile('(^|\s|%{?__)make}?\s+(check|test)')
44 rm_regex = re.compile('(^|\s)((.*/)?rm|%{?__rm}?) ')
45 rpm_buildroot_regex = re.compile('^[^#]*(?:(\\\*)\${?RPM_BUILD_ROOT}?|(%+){?buildroot}?)')
46 configure_libdir_spec_regex = re.compile('ln |\./configure[^#]*--libdir=(\S+)[^#]*')
47 lib_package_regex = re.compile('^%package.*\Wlib')
48 ifarch_regex = re.compile('^\s*%ifn?arch\s')
49 if_regex = re.compile('^\s*%if\s')
50 endif_regex = re.compile('^\s*%endif\\b')
51 biarch_package_regex = re.compile(DEFAULT_BIARCH_PACKAGES)
52 hardcoded_lib_path_exceptions_regex = re.compile(Config.getOption('HardcodedLibPathExceptions', DEFAULT_HARDCODED_LIB_PATH_EXCEPTIONS))
53 prereq_regex = re.compile('^PreReq(\(.*\))?:\s*(.+?)\s*$', re.IGNORECASE)
54 buildprereq_regex = re.compile('^BuildPreReq:\s*(.+?)\s*$', re.IGNORECASE)
55 use_utf8 = Config.getOption('UseUTF8', Config.USEUTF8_DEFAULT)
56 libdir_regex = re.compile('%{?_lib(?:dir)?\}?\\b')
57 comment_or_empty_regex = re.compile('^\s*(#|$)')
58 defattr_regex = re.compile('^\s*%defattr\\b')
59 attr_regex = re.compile('^\s*%attr\\b')
60 suse_version_regex = re.compile('%suse_version\s*[<>=]+\s*(\d+)')
61 section_regexs = dict(
62     ([x, re.compile('^%' + x + '(?:\s|$)')]
63      for x in ('build', 'changelog', 'check', 'clean', 'description', 'files',
64                'install', 'package', 'prep', 'pre', 'post', 'preun', 'postun',
65                'trigger', 'triggerin', 'triggerun', 'triggerprein',
66                'triggerpostun', 'pretrans', 'posttrans')))
67 deprecated_grep_regex = re.compile(r'\b[ef]grep\b')
68
69 # Only check for /lib, /usr/lib, /usr/X11R6/lib
70 # TODO: better handling of X libraries and modules.
71 hardcoded_library_paths = '(/lib|/usr/lib|/usr/X11R6/lib/(?!([^/]+/)+)[^/]*\\.([oa]|la|so[0-9.]*))'
72 hardcoded_library_path_regex = re.compile('^[^#]*((^|\s+|\.\./\.\.|\${?RPM_BUILD_ROOT}?|%{?buildroot}?|%{?_prefix}?)' + hardcoded_library_paths + '(?=[\s;/])([^\s,;]*))')
73
74 # Requires(pre,post) is broken in some rpm versions, see
75 # https://bugzilla.redhat.com/118780 and bugs linked to that one.
76 scriptlet_requires_regex = re.compile('^(PreReq|Requires)\([^\)]*,', re.IGNORECASE)
77
78 depscript_override_regex = re.compile('(^|\s)%(define|global)\s+__find_(requires|provides)\s')
79 depgen_disable_regex = re.compile('(^|\s)%(define|global)\s+_use_internal_dependency_generator\s+0')
80
81 # See https://bugzilla.redhat.com/488146 for details
82 indent_spaces_regex = re.compile('( \t|(^|\t)([^\t]{8})*[^\t]{4}[^\t]?([^\t][^\t.!?]|[^\t]?[.!?] )  )')
83
84 requires_regex = re.compile('^(?:Build)?(?:Pre)?Req(?:uires)?(?:\([^\)]+\))?:\s*(.*)', re.IGNORECASE)
85 provides_regex = re.compile('^Provides(?:\([^\)]+\))?:\s*(.*)', re.IGNORECASE)
86 obsoletes_regex = re.compile('^Obsoletes:\s*(.*)', re.IGNORECASE)
87 conflicts_regex = re.compile('^(?:Build)?Conflicts:\s*(.*)', re.IGNORECASE)
88
89 compop_regex = re.compile('[<>=]')
90
91 setup_q_regex = re.compile(' -[A-Za-z]*q')
92 setup_t_regex = re.compile(' -[A-Za-z]*T')
93 setup_ab_regex = re.compile(' -[A-Za-z]*[ab]')
94
95 filelist_regex = re.compile('\s+-f\s+\S+')
96 pkgname_regex = re.compile('\s+(?:-n\s+)?(\S+)')
97 tarball_regex = re.compile('\.(?:t(?:ar|[glx]z|bz2?)|zip)\\b', re.IGNORECASE)
98
99
100 def unversioned(deps):
101     '''Yield unversioned dependency names from the given list.'''
102     for dep in deps:
103         if not dep[1]:
104             yield dep[0]
105
106 def contains_buildroot(line):
107     '''Check if the given line contains use of rpm buildroot.'''
108     res = rpm_buildroot_regex.search(line)
109     if res and \
110            (not res.group(1) or len(res.group(1)) % 2 == 0) and \
111            (not res.group(2) or len(res.group(2)) % 2 != 0):
112         return True
113     return False
114
115
116 class SpecCheck(AbstractCheck.AbstractCheck):
117
118     def __init__(self):
119         AbstractCheck.AbstractCheck.__init__(self, "SpecCheck")
120         self._spec_file = None
121
122     def check(self, pkg):
123         if not pkg.isSource():
124             return
125
126         wrong_spec = False
127
128         # lookup spec file
129         for fname, pkgfile in pkg.files().items():
130             if fname.endswith('.spec'):
131                 self._spec_file = pkgfile.path
132                 if fname == pkg.name + ".spec":
133                     wrong_spec = False
134                     break
135                 else:
136                     wrong_spec = True
137         if not self._spec_file:
138             printError(pkg, "no-spec-file")
139         else:
140             if wrong_spec:
141                 printError(pkg, "invalid-spec-name")
142
143             # check content of spec file
144             self.check_spec(pkg, self._spec_file)
145
146     def check_spec(self, pkg, spec_file, spec_lines=[]):
147         self._spec_file = spec_file
148         spec_only = isinstance(pkg, Pkg.FakePkg)
149         if not spec_lines:
150             spec_lines = Pkg.readlines(spec_file)
151         patches = {}
152         applied_patches = []
153         applied_patches_ifarch = []
154         source_dir = False
155         buildroot = False
156         configure_linenum = None
157         configure_cmdline = ""
158         mklibname = False
159         is_lib_pkg = False
160         if_depth = 0
161         ifarch_depth = -1
162         current_section = 'package'
163         buildroot_clean = {'clean': False, 'install' : False}
164         depscript_override = False
165         depgen_disabled = False
166         indent_spaces = 0
167         indent_tabs = 0
168         files_has_defattr = False
169         section = {}
170         # None == main package
171         current_package = None
172         package_noarch = {}
173
174         is_utf8 = False
175         if self._spec_file and use_utf8:
176             if Pkg.is_utf8(self._spec_file):
177                 is_utf8 = True
178             else:
179                 printError(pkg, "non-utf8-spec-file", self._spec_file)
180
181         # gather info from spec lines
182
183         pkg.current_linenum = 0
184
185         nbsp = chr(0xA0)
186         if is_utf8:
187             nbsp = unichr(0xA0)
188
189         for line in spec_lines:
190
191             pkg.current_linenum += 1
192
193             if is_utf8:
194                 line = unicode(line, "utf-8", "replace")
195
196             char = line.find(nbsp)
197             if char != -1:
198                 printWarning(pkg, "non-break-space", "line %s, char %d" %
199                              (pkg.current_linenum, char))
200
201             section_marker = False
202             for sec, regex in section_regexs.items():
203                 res = regex.search(line)
204                 if res:
205                     current_section = sec
206                     section_marker = True
207                     section[sec] = section.get(sec, 0) + 1
208                     if sec in ('package', 'files'):
209                         rest = filelist_regex.sub('', line[res.end()-1:])
210                         res = pkgname_regex.search(rest)
211                         if res:
212                             current_package = res.group(1)
213                         else:
214                             current_package = None
215                     break
216
217             if section_marker:
218
219                 if current_section == 'files':
220                     files_has_defattr = False
221
222                 if not is_lib_pkg and lib_package_regex.search(line):
223                     is_lib_pkg = True
224
225                 continue
226
227             if current_section in ('prep', 'build','pre', 'post', 'postun',
228                     'trigger', 'triggerin', 'triggerprein', 'triggerun', 'triggerpostun',
229                     'pretrans', 'posttrans') and \
230                     contains_buildroot(line):
231                 printWarning(pkg, 'rpm-buildroot-usage', '%' + current_section,
232                              line[:-1].strip())
233
234             if make_check_regex.search(line) and current_section not in \
235                     ('check', 'changelog', 'package', 'description'):
236                 printWarning(pkg, 'make-check-outside-check-section', line[:-1])
237
238             if current_section in buildroot_clean and \
239                     not buildroot_clean[current_section] and \
240                     contains_buildroot(line) and rm_regex.search(line):
241                 buildroot_clean[current_section] = True
242
243             if ifarch_regex.search(line):
244                 if_depth = if_depth + 1
245                 ifarch_depth = if_depth
246
247             if if_regex.search(line):
248                 if_depth = if_depth + 1
249
250             if line.startswith('%setup'):
251                 if not setup_q_regex.search(line):
252                     # Don't warn if there's a -T without -a or -b
253                     if setup_t_regex.search(line):
254                         if setup_ab_regex.search(line):
255                             printWarning(pkg, 'setup-not-quiet')
256                     else:
257                         printWarning(pkg, 'setup-not-quiet')
258                 if current_section != 'prep':
259                     printWarning(pkg, 'setup-not-in-prep')
260
261             if endif_regex.search(line):
262                 if ifarch_depth == if_depth:
263                     ifarch_depth = -1
264                 if_depth = if_depth - 1
265
266             res = applied_patch_regex.search(line)
267             if res:
268                 pnum = res.group(1) or 0
269                 for tmp in applied_patch_p_regex.findall(line) or [pnum]:
270                     pnum = int(tmp)
271                     applied_patches.append(pnum)
272                     if ifarch_depth > 0:
273                         applied_patches_ifarch.append(pnum)
274             else:
275                 res = applied_patch_pipe_regex.search(line)
276                 if res:
277                     pnum = int(res.group(1))
278                     applied_patches.append(pnum)
279                     if ifarch_depth > 0:
280                         applied_patches_ifarch.append(pnum)
281             if not res and not source_dir:
282                 res = source_dir_regex.search(line)
283                 if res:
284                     source_dir = True
285                     printError(pkg, "use-of-RPM_SOURCE_DIR")
286
287             if configure_linenum:
288                 if configure_cmdline[-1] == "\\":
289                     configure_cmdline = configure_cmdline[:-1] + line.strip()
290                 else:
291                     res = configure_libdir_spec_regex.search(configure_cmdline)
292                     if not res:
293                         # Hack to get the correct (start of ./configure) line
294                         # number displayed:
295                         real_linenum = pkg.current_linenum
296                         pkg.current_linenum = configure_linenum
297                         printWarning(pkg, "configure-without-libdir-spec")
298                         pkg.current_linenum = real_linenum
299                     elif res.group(1):
300                         res = re.match(hardcoded_library_paths, res.group(1))
301                         if res:
302                             printError(pkg, "hardcoded-library-path",
303                                        res.group(1), "in configure options")
304                     configure_linenum = None
305
306             hashPos = line.find("#")
307
308             if current_section != 'changelog':
309                 cfgPos = line.find('./configure')
310                 if cfgPos != -1 and (hashPos == -1 or hashPos > cfgPos):
311                     # store line where it started
312                     configure_linenum = pkg.current_linenum
313                     configure_cmdline = line.strip()
314
315             res = hardcoded_library_path_regex.search(line)
316             if current_section != 'changelog' and res and not \
317                     (biarch_package_regex.match(pkg.name) or
318                      hardcoded_lib_path_exceptions_regex.search(
319                             res.group(1).lstrip())):
320                 printError(pkg, "hardcoded-library-path", "in",
321                            res.group(1).lstrip())
322
323             if '%mklibname' in line:
324                 mklibname = True
325
326             if current_section == 'package':
327
328                 # Would be cleaner to get sources and patches from the
329                 # specfile parsed in Python (see below), but we want to
330                 # catch %ifarch'd etc ones as well, and also catch these when
331                 # the specfile is not parseable.
332
333                 res = patch_regex.search(line)
334                 if res:
335                     pnum = int(res.group(1) or 0)
336                     patches[pnum] = res.group(2)
337
338                 res = obsolete_tags_regex.search(line)
339                 if res:
340                     printWarning(pkg, "obsolete-tag", res.group(1))
341
342                 res = buildroot_regex.search(line)
343                 if res:
344                     buildroot = True
345                     if res.group(1).startswith('/'):
346                         printWarning(pkg, 'hardcoded-path-in-buildroot-tag',
347                                      res.group(1))
348
349                 res = buildarch_regex.search(line)
350                 if res:
351                     if res.group(2) != "noarch":
352                         printError(pkg, 'buildarch-instead-of-exclusivearch-tag', res.group(2))
353                     else:
354                         package_noarch[current_package] = True
355
356                 res = packager_regex.search(line)
357                 if res:
358                     printWarning(pkg, 'hardcoded-packager-tag', res.group(1))
359
360                 res = prefix_regex.search(line)
361                 if res:
362                     if not res.group(1).startswith('%'):
363                         printWarning(pkg, 'hardcoded-prefix-tag', res.group(1))
364
365                 res = suse_version_regex.search(line)
366                 if res and int(res.group(1)) > 0 and int(res.group(1)) < 1130:
367                     printWarning(pkg, "obsolete-suse-version-check", res.group(1))
368                 elif res and int(res.group(1)) > 1230:
369                     printError(pkg, "invalid-suse-version-check", res.group(1))
370
371                 res = prereq_regex.search(line)
372                 if res:
373                     printError(pkg, 'prereq-use', res.group(2))
374
375                 res = buildprereq_regex.search(line)
376                 if res:
377                     printError(pkg, 'buildprereq-use', res.group(1))
378
379                 if scriptlet_requires_regex.search(line):
380                     printError(pkg, 'broken-syntax-in-scriptlet-requires',
381                                line.strip())
382
383                 res = requires_regex.search(line)
384                 if res:
385                     reqs = Pkg.parse_deps(res.group(1))
386                     for req in unversioned(reqs):
387                         if compop_regex.search(req):
388                             printWarning(pkg, 'comparison-operator-in-deptoken',
389                                          req)
390
391                 res = provides_regex.search(line)
392                 if res:
393                     provs = Pkg.parse_deps(res.group(1))
394                     for prov in unversioned(provs):
395                         printWarning(pkg, 'unversioned-explicit-provides', prov)
396                         if compop_regex.search(prov):
397                             printWarning(pkg, 'comparison-operator-in-deptoken',
398                                          prov)
399
400                 res = obsoletes_regex.search(line)
401                 if res:
402                     obses = Pkg.parse_deps(res.group(1))
403                     for obs in unversioned(obses):
404                         printWarning(pkg, 'unversioned-explicit-obsoletes', obs)
405                         if compop_regex.search(obs):
406                             printWarning(pkg, 'comparison-operator-in-deptoken',
407                                          obs)
408
409                 res = conflicts_regex.search(line)
410                 if res:
411                     confs = Pkg.parse_deps(res.group(1))
412                     for conf in unversioned(confs):
413                         if compop_regex.search(conf):
414                             printWarning(pkg, 'comparison-operator-in-deptoken',
415                                          conf)
416
417             if current_section == 'changelog':
418                 for match in AbstractCheck.macro_regex.findall(line):
419                     res = re.match('%+', match)
420                     if len(res.group(0)) % 2:
421                         printWarning(pkg, 'macro-in-%changelog', match)
422             else:
423                 if not depscript_override:
424                     depscript_override = \
425                         depscript_override_regex.search(line) is not None
426                 if not depgen_disabled:
427                     depgen_disabled = \
428                         depgen_disable_regex.search(line) is not None
429
430             if current_section == 'files':
431
432                 if not comment_or_empty_regex.search(line) and not \
433                    (ifarch_regex.search(line) or if_regex.search(line) or
434                     endif_regex.search(line)):
435                     if defattr_regex.search(line):
436                         files_has_defattr = True
437                     elif not (files_has_defattr or attr_regex.search(line)):
438                         printWarning(pkg, 'files-attr-not-set')
439
440                 # TODO: check scriptlets for these too?
441                 if package_noarch.get(current_package) or \
442                         (current_package not in package_noarch and
443                          package_noarch.get(None)):
444                     res = libdir_regex.search(line)
445                     if res:
446                         pkgname = current_package
447                         if pkgname is None:
448                             pkgname = '(main package)'
449                         printWarning(pkg, 'libdir-macro-in-noarch-package',
450                                      pkgname, line.rstrip())
451
452             if not indent_tabs and '\t' in line:
453                 indent_tabs = pkg.current_linenum
454             if not indent_spaces and indent_spaces_regex.search(line):
455                 indent_spaces = pkg.current_linenum
456
457             # Check if egrep or fgrep is used
458             if current_section not in \
459                     ('package', 'changelog', 'description', 'files'):
460                 greps = deprecated_grep_regex.findall(line)
461                 if greps:
462                     printWarning(pkg, "deprecated-grep", greps)
463
464             # If not checking spec file only, we're checking one inside a
465             # SRPM -> skip this check to avoid duplicate warnings (#167)
466             if spec_only and VALID_GROUPS and \
467                    line.lower().startswith("group:"):
468                 group = line[6:].strip()
469                 if group not in VALID_GROUPS:
470                     printWarning(pkg, 'non-standard-group', group)
471
472             # Test if there are macros in comments
473             if hashPos != -1 and \
474                     (hashPos == 0 or line[hashPos-1] in (" ", "\t")):
475                 for match in AbstractCheck.macro_regex.findall(
476                     line[hashPos+1:]):
477                     res = re.match('%+', match)
478                     if len(res.group(0)) % 2:
479                         printWarning(pkg, 'macro-in-comment', match)
480
481         # Last line read is not useful after this point
482         pkg.current_linenum = None
483
484         for sect in (x for x in buildroot_clean if not buildroot_clean[x]):
485             printWarning(pkg, 'no-cleaning-of-buildroot', '%' + sect)
486
487         if not buildroot:
488             printWarning(pkg, 'no-buildroot-tag')
489
490         for sec in ('prep', 'build', 'install', 'clean'):
491             if not section.get(sec):
492                 printWarning(pkg, 'no-%%%s-section' % sec)
493         for sec in ('changelog',):
494             # prep, build, install, clean, check prevented by rpmbuild 4.4
495             if section.get(sec, 0) > 1:
496                 printWarning(pkg, 'more-than-one-%%%s-section' % sec)
497
498         if is_lib_pkg and not mklibname:
499             printError(pkg, 'lib-package-without-%mklibname')
500
501         if depscript_override and not depgen_disabled:
502             printWarning(pkg, 'depscript-without-disabling-depgen')
503
504         if indent_spaces and indent_tabs:
505             pkg.current_linenum = max(indent_spaces, indent_tabs)
506             printWarning(pkg, 'mixed-use-of-spaces-and-tabs',
507                          '(spaces: line %d, tab: line %d)' %
508                          (indent_spaces, indent_tabs))
509             pkg.current_linenum = None
510
511         # process gathered info
512         for pnum, pfile in patches.items():
513             if pnum in applied_patches_ifarch:
514                 printWarning(pkg, "%ifarch-applied-patch", "Patch%d:" % pnum,
515                              pfile)
516             if pnum not in applied_patches:
517                 printWarning(pkg, "patch-not-applied", "Patch%d:" % pnum,
518                              pfile)
519
520         # Rest of the checks require a real spec file
521         if not self._spec_file:
522             return
523
524         # We'd like to parse the specfile only once using python bindings,
525         # but it seems errors from rpmlib get logged to stderr and we can't
526         # capture and print them nicely, so we do it once each way :P
527
528         out = Pkg.getstatusoutput(('env', 'LC_ALL=C', 'rpm', '-q',
529                                    '--qf=', '--specfile', self._spec_file))
530         parse_error = False
531         for line in out[1].splitlines():
532             # No such file or dir hack: https://bugzilla.redhat.com/487855
533             if 'No such file or directory' not in line:
534                 parse_error = True
535                 printError(pkg, 'specfile-error', line)
536
537         if not parse_error:
538             # grab sources and patches from parsed spec object to get
539             # them with macros expanded for URL checking
540
541             spec_obj = None
542             try:
543                 ts = rpm.TransactionSet()
544                 spec_obj = ts.parseSpec(self._spec_file)
545             except:
546                 # errors logged above already
547                 pass
548             if spec_obj:
549                 try:
550                     # rpm < 4.8.0
551                     sources = spec_obj.sources()
552                 except TypeError:
553                     # rpm >= 4.8.0
554                     sources = spec_obj.sources
555                 for src in sources:
556                     (url, num, flags) = src
557                     (scheme, netloc) = urlparse(url)[0:2]
558                     if flags & 1: # rpmspec.h, rpm.org ticket #123
559                         srctype = "Source"
560                     else:
561                         srctype = "Patch"
562                     tag = '%s%s' % (srctype, num)
563                     if scheme and netloc:
564                         info = self.check_url(pkg, tag, url)
565                         if not info or not hasattr(pkg, 'files'):
566                             continue
567                         clen = info.get("Content-Length")
568                         if clen is not None:
569                             clen = int(clen)
570                         cmd5 = info.get("Content-MD5")
571                         if cmd5 is not None:
572                             cmd5 = cmd5.lower()
573                         if clen is not None or cmd5 is not None:
574                             # Not using path from urlparse results to match how
575                             # rpm itself parses the basename.
576                             pkgfile = pkg.files()[url.split("/")[-1]]
577                             if pkgfile:
578                                 if clen is not None and pkgfile.size != clen:
579                                     printWarning(pkg, 'file-size-mismatch',
580                                                  '%s = %s, %s = %s' %
581                                                  (pkgfile.name, pkgfile.size,
582                                                   url, clen))
583                                 # pkgfile.md5 could be some other digest than
584                                 # MD5, treat as MD5 only if it's 32 chars long
585                                 if cmd5 and len(pkgfile.md5) == 32 \
586                                         and pkgfile.md5 != cmd5:
587                                     printWarning(pkg, 'file-md5-mismatch',
588                                                  '%s = %s, %s = %s' %
589                                                  (pkgfile.name, pkgfile.md5,
590                                                   url, cmd5))
591                     elif srctype == "Source" and tarball_regex.search(url):
592                         printWarning(pkg, 'invalid-url', '%s:' % tag, url)
593
594 # Create an object to enable the auto registration of the test
595 check = SpecCheck()
596
597 # Add information about checks
598 addDetails(
599 'no-spec-file',
600 '''No spec file was specified in your RPM building. Please specify a valid
601 SPEC file to build a valid RPM package.''',
602
603 'invalid-spec-name',
604 '''Your spec filename must end with '.spec'. If it's not the case, rename your
605 file and rebuild your package.''',
606
607 'non-utf8-spec-file',
608 '''The character encoding of the spec file is not UTF-8.  Convert it for
609 example using iconv(1).''',
610
611 'use-of-RPM_SOURCE_DIR',
612 '''You use $RPM_SOURCE_DIR or %{_sourcedir} in your spec file. If you have to
613 use a directory for building, use $RPM_BUILD_ROOT instead.''',
614
615 'patch-not-applied',
616 '''A patch is included in your package but was not applied. Refer to the patches
617 documentation to see what's wrong.''',
618
619 'obsolete-tag',
620 '''The following tags are obsolete: Copyright and Serial. They must
621 be replaced by License and Epoch respectively.''',
622
623 'deprecated-grep',
624 '''Direct use of grep as egrep or fgrep is deprecated in GNU grep and
625 historical in POSIX, use grep -E and grep -F instead.''',
626
627 'no-buildroot-tag',
628 '''The BuildRoot tag isn't used in your spec. It must be used in order to
629 allow building the package as non root on some systems. For some rpm versions
630 (e.g. rpm.org >= 4.6) the BuildRoot tag is not necessary in specfiles and is
631 ignored by rpmbuild; if your package is only going to be built with such rpm
632 versions you can ignore this warning.''',
633
634 'hardcoded-path-in-buildroot-tag',
635 '''A path is hardcoded in your Buildroot tag. It should be replaced
636 by %{_tmppath}/%{name}-%{version}-build.''',
637
638 'hardcoded-packager-tag',
639 '''The Packager tag is hardcoded in your spec file. It should be removed, so
640 as to use rebuilder's own defaults.''',
641
642 'buildarch-instead-of-exclusivearch-tag',
643 '''Use ExclusiveArch instead of BuildArch (or BuildArchitectures)
644 to restrict build on some specific architectures.
645 Only use BuildArch with noarch''',
646
647 'hardcoded-prefix-tag',
648 '''The Prefix tag is hardcoded in your spec file. It should be removed, so as
649 to allow package relocation.''',
650
651 'hardcoded-library-path',
652 '''A library path is hardcoded to one of the following paths: /lib,
653 /usr/lib. It should be replaced by something like /%{_lib} or %{_libdir}.''',
654
655 'configure-without-libdir-spec',
656 '''A configure script is run without specifying the libdir. configure
657 options must be augmented with something like --libdir=%{_libdir} whenever
658 the script supports it.''',
659
660 'no-%prep-section',
661 '''The spec file does not contain a %prep section.  Even if some packages don't
662 directly need it, section markers may be overridden in rpm's configuration
663 to provide additional "under the hood" functionality.  Add the section, even
664 if empty.''',
665
666 'no-%build-section',
667 '''The spec file does not contain a %build section.  Even if some packages
668 don't directly need it, section markers may be overridden in rpm's
669 configuration to provide additional "under the hood" functionality, such as
670 injection of automatic -debuginfo subpackages.  Add the section, even if
671 empty.''',
672
673 'no-%install-section',
674 '''The spec file does not contain an %install section.  Even if some packages
675 don't directly need it, section markers may be overridden in rpm's
676 configuration to provide additional "under the hood" functionality.  Add the
677 section, even if empty.''',
678
679 'no-%clean-section',
680 '''The spec file doesn't contain a %clean section to remove the files installed
681 by the %install section.''',
682
683 'more-than-one-%changelog-section',
684 '''The spec file unnecessarily contains more than one %changelog section;
685 remove the extra ones.''',
686
687 'lib-package-without-%mklibname',
688 '''The package name must be built using %mklibname to allow lib64 and lib32
689 coexistence.''',
690
691 '%ifarch-applied-patch',
692 '''A patch is applied inside an %ifarch block. Patches must be applied
693 on all architectures and may contain necessary configure and/or code
694 patch to be effective only on a given arch.''',
695
696 'prereq-use',
697 '''The use of PreReq is deprecated. In the majority of cases, a plain Requires
698 is enough and the right thing to do. Sometimes Requires(pre), Requires(post),
699 Requires(preun) and/or Requires(postun) can also be used instead of PreReq.''',
700
701 'buildprereq-use',
702 '''The use of BuildPreReq is deprecated, build dependencies are always required
703 before a package can be built.  Use plain BuildRequires instead.''',
704
705 'broken-syntax-in-scriptlet-requires',
706 '''Comma separated context marked dependencies are silently broken in some
707 versions of rpm.  One way to work around it is to split them into several ones,
708 eg. replace "Requires(post,preun): foo" with "Requires(post): foo" and
709 "Requires(preun): foo".''',
710
711 'setup-not-in-prep',
712 '''The %setup macro should only be used within the %prep section because it may
713 not expand to anything outside of it and can break the build in unpredictable
714 ways.''',
715
716 'setup-not-quiet',
717 '''Use the -q option to the %setup macro to avoid useless build output from
718 unpacking the sources.''',
719
720 'no-cleaning-of-buildroot',
721 '''You should clean $RPM_BUILD_ROOT in the %clean section and in the beginning
722 of the %install section. Use "rm -rf $RPM_BUILD_ROOT". Some rpm configurations
723 do this automatically; if your package is only going to be built in such
724 configurations, you can ignore this warning for the section(s) where your rpm
725 takes care of it.''',
726
727 'rpm-buildroot-usage',
728 '''$RPM_BUILD_ROOT should not be touched during %build or %prep stage, as it
729 may break short circuit builds.''',
730
731 'make-check-outside-check-section',
732 '''Make check or other automated regression test should be run in %check, as
733 they can be disabled with a rpm macro for short circuiting purposes.''',
734
735 'macro-in-%changelog',
736 '''Macros are expanded in %changelog too, which can in unfortunate cases lead
737 to the package not building at all, or other subtle unexpected conditions that
738 affect the build.  Even when that doesn\'t happen, the expansion results in
739 possibly "rewriting history" on subsequent package revisions and generally
740 odd entries eg. in source rpms, which is rarely wanted.  Avoid use of macros
741 in %changelog altogether, or use two '%'s to escape them, like '%%foo'.''',
742
743 'depscript-without-disabling-depgen',
744 '''In some common rpm configurations/versions, defining __find_provides and/or
745 __find_requires has no effect if rpm's internal dependency generator has not
746 been disabled for the build.  %define _use_internal_dependency_generator to 0
747 to disable it in the specfile, or don't define __find_provides/requires.''',
748
749 'mixed-use-of-spaces-and-tabs',
750 '''The specfile mixes use of spaces and tabs for indentation, which is a
751 cosmetic annoyance.  Use either spaces or tabs for indentation, not both.''',
752
753 'unversioned-explicit-provides',
754 '''The specfile contains an unversioned Provides: token, which will match all
755 older, equal, and newer versions of the provided thing.  This may cause
756 update problems and will make versioned dependencies, obsoletions and conflicts
757 on the provided thing useless -- make the Provides versioned if possible.''',
758
759 'unversioned-explicit-obsoletes',
760 '''The specfile contains an unversioned Obsoletes: token, which will match all
761 older, equal and newer versions of the obsoleted thing.  This may cause update
762 problems, restrict future package/provides naming, and may match something it
763 was originally not inteded to match -- make the Obsoletes versioned if
764 possible.''',
765
766 'libdir-macro-in-noarch-package',
767 '''The %{_libdir} or %{_lib} macro was found in a noarch package in a section
768 that gets included in binary packages.  This is most likely an error because
769 these macros are expanded on the build host and their values vary between
770 architectures, probably resulting in a package that does not work properly
771 on all architectures at runtime. Investigate whether the package is really
772 architecture independent or if some other dir/macro should be instead.''',
773
774 'non-break-space',
775 '''The spec file contains a non-break space, which looks like a regular space
776 in some editors but can lead to obscure errors. It should be replaced by a
777 regular space.''',
778
779 'files-attr-not-set',
780 '''A file or a directory entry in a %files section does not have attributes
781 set which may result in unexpected file permissions and thus security issues
782 in the resulting binary package depending on the build environment and rpmbuild
783 version (typically < 4.4).  Add default attributes using %defattr before it in
784 the %files section, or use per entry %attr's.''',
785
786 'obsolete-suse-version-check',
787 '''The specfile contains a comparison of %suse_version against a suse release
788 that is no longer in maintenance. Consider removing obsolete parts of your
789 spec file to make it more readable.''',
790
791 'invalid-suse-version-check',
792 '''The specfile contains a comparison of %suse_version against a suse release
793 that does not exist. Please double check.''',
794
795 'non-standard-group',
796 '''The value of the Group tag in the package is not valid.  Valid groups are:
797 "%s".''' % '", "'.join(VALID_GROUPS),
798
799 'specfile-error',
800 '''This error occurred when rpmlint used rpm to query the specfile.  The error
801 is output by rpm and the message should contain more information.''',
802
803 'comparison-operator-in-deptoken',
804 '''This dependency token contains a comparison operator (<, > or =).  This is
805 usually not intended and may be caused by missing whitespace between the token's
806 name, the comparison operator and the version string.''',
807
808 'macro-in-comment',
809 '''There is a unescaped macro after a shell style comment in the specfile.
810 Macros are expanded everywhere, so check if it can cause a problem in this
811 case and escape the macro with another leading % if appropriate.''',
812
813 'file-size-mismatch',
814 '''The size of the file in the package does not match the size indicated by
815 peeking at its URL.  Verify that the file in the package has the intended
816 contents.''',
817
818 'file-md5-mismatch',
819 '''The MD5 hash of the file in the package does not match the MD5 hash
820 indicated by peeking at its URL.  Verify that the file in the package has the
821 intended contents.''',
822 )
823
824 # SpecCheck.py ends here
825
826 # Local variables:
827 # indent-tabs-mode: nil
828 # py-indent-offset: 4
829 # End:
830 # ex: ts=4 sw=4 et