Gentoo Archives: gentoo-commits

From: Zac Medico <zmedico@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/portage:repoman commit in: repoman/pym/repoman/modules/scan/ebuild/
Date: Fri, 30 Mar 2018 05:21:25
Message-Id: 1522381880.ac86784b164c8705c45f79d3418d783e59d99a9a.zmedico@gentoo
1 commit: ac86784b164c8705c45f79d3418d783e59d99a9a
2 Author: Brian Dolbec <dolsen <AT> gentoo <DOT> org>
3 AuthorDate: Sat Jul 15 01:10:13 2017 +0000
4 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org>
5 CommitDate: Fri Mar 30 03:51:20 2018 +0000
6 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=ac86784b
7
8 repoman: Remove the no longer used modules/scan/ebuild/checks.py
9
10 repoman/pym/repoman/modules/scan/ebuild/checks.py | 1044 ---------------------
11 1 file changed, 1044 deletions(-)
12
13 diff --git a/repoman/pym/repoman/modules/scan/ebuild/checks.py b/repoman/pym/repoman/modules/scan/ebuild/checks.py
14 deleted file mode 100644
15 index de03bedd2..000000000
16 --- a/repoman/pym/repoman/modules/scan/ebuild/checks.py
17 +++ /dev/null
18 @@ -1,1044 +0,0 @@
19 -# -*- coding:utf-8 -*-
20 -# repoman: Checks
21 -# Copyright 2007-2017 Gentoo Foundation
22 -# Distributed under the terms of the GNU General Public License v2
23 -
24 -"""This module contains functions used in Repoman to ascertain the quality
25 -and correctness of an ebuild."""
26 -
27 -from __future__ import unicode_literals
28 -
29 -from itertools import chain
30 -import operator
31 -import re
32 -import time
33 -
34 -# import our initialized portage instance
35 -from repoman._portage import portage
36 -
37 -from portage.eapi import (
38 - eapi_supports_prefix, eapi_has_implicit_rdepend,
39 - eapi_has_src_prepare_and_src_configure, eapi_has_dosed_dohard,
40 - eapi_exports_AA, eapi_has_pkg_pretend)
41 -
42 -from . import errors
43 -
44 -
45 -class LineCheck(object):
46 - """Run a check on a line of an ebuild."""
47 - """A regular expression to determine whether to ignore the line"""
48 - ignore_line = False
49 - """True if lines containing nothing more than comments with optional
50 - leading whitespace should be ignored"""
51 - ignore_comment = True
52 -
53 - def new(self, pkg):
54 - pass
55 -
56 - def check_eapi(self, eapi):
57 - """Returns if check should be run in the given EAPI (default: True)"""
58 - return True
59 -
60 - def check(self, num, line):
61 - """Run the check on line and return error if there is one"""
62 - if self.re.match(line):
63 - return self.error
64 -
65 - def end(self):
66 - pass
67 -
68 -
69 -class PhaseCheck(LineCheck):
70 - """ basic class for function detection """
71 -
72 - func_end_re = re.compile(r'^\}$')
73 - phases_re = re.compile('(%s)' % '|'.join((
74 - 'pkg_pretend', 'pkg_setup', 'src_unpack', 'src_prepare',
75 - 'src_configure', 'src_compile', 'src_test', 'src_install',
76 - 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm',
77 - 'pkg_config')))
78 - in_phase = ''
79 -
80 - def check(self, num, line):
81 - m = self.phases_re.match(line)
82 - if m is not None:
83 - self.in_phase = m.group(1)
84 - if self.in_phase != '' and self.func_end_re.match(line) is not None:
85 - self.in_phase = ''
86 -
87 - return self.phase_check(num, line)
88 -
89 - def phase_check(self, num, line):
90 - """ override this function for your checks """
91 - pass
92 -
93 -
94 -class EbuildHeader(LineCheck):
95 - """Ensure ebuilds have proper headers
96 - Copyright header errors
97 - CVS header errors
98 - License header errors
99 -
100 - Args:
101 - modification_year - Year the ebuild was last modified
102 - """
103 -
104 - repoman_check_name = 'ebuild.badheader'
105 -
106 - gentoo_copyright = r'^# Copyright ((1999|2\d\d\d)-)?%s Gentoo Foundation$'
107 - gentoo_license = (
108 - '# Distributed under the terms'
109 - ' of the GNU General Public License v2')
110 - id_header_re = re.compile(r'.*\$(Id|Header)(:.*)?\$.*')
111 - blank_line_re = re.compile(r'^$')
112 - ignore_comment = False
113 -
114 - def new(self, pkg):
115 - if pkg.mtime is None:
116 - self.modification_year = r'2\d\d\d'
117 - else:
118 - self.modification_year = str(time.gmtime(pkg.mtime)[0])
119 - self.gentoo_copyright_re = re.compile(
120 - self.gentoo_copyright % self.modification_year)
121 -
122 - def check(self, num, line):
123 - if num > 2:
124 - return
125 - elif num == 0:
126 - if not self.gentoo_copyright_re.match(line):
127 - return errors.COPYRIGHT_ERROR
128 - elif num == 1 and line.rstrip('\n') != self.gentoo_license:
129 - return errors.LICENSE_ERROR
130 - elif num == 2 and self.id_header_re.match(line):
131 - return errors.ID_HEADER_ERROR
132 - elif num == 2 and not self.blank_line_re.match(line):
133 - return errors.NO_BLANK_LINE_ERROR
134 -
135 -
136 -class EbuildWhitespace(LineCheck):
137 - """Ensure ebuilds have proper whitespacing"""
138 -
139 - repoman_check_name = 'ebuild.minorsyn'
140 -
141 - ignore_line = re.compile(r'(^$)|(^(\t)*#)')
142 - ignore_comment = False
143 - leading_spaces = re.compile(r'^[\S\t]')
144 - trailing_whitespace = re.compile(r'.*([\S]$)')
145 -
146 - def check(self, num, line):
147 - if self.leading_spaces.match(line) is None:
148 - return errors.LEADING_SPACES_ERROR
149 - if self.trailing_whitespace.match(line) is None:
150 - return errors.TRAILING_WHITESPACE_ERROR
151 -
152 -
153 -class EbuildBlankLine(LineCheck):
154 - repoman_check_name = 'ebuild.minorsyn'
155 - ignore_comment = False
156 - blank_line = re.compile(r'^$')
157 -
158 - def new(self, pkg):
159 - self.line_is_blank = False
160 -
161 - def check(self, num, line):
162 - if self.line_is_blank and self.blank_line.match(line):
163 - return 'Useless blank line on line: %d'
164 - if self.blank_line.match(line):
165 - self.line_is_blank = True
166 - else:
167 - self.line_is_blank = False
168 -
169 - def end(self):
170 - if self.line_is_blank:
171 - yield 'Useless blank line on last line'
172 -
173 -
174 -class EbuildQuote(LineCheck):
175 - """Ensure ebuilds have valid quoting around things like D,FILESDIR, etc..."""
176 -
177 - repoman_check_name = 'ebuild.minorsyn'
178 - _message_commands = [
179 - "die", "echo", "eerror", "einfo", "elog", "eqawarn", "ewarn"]
180 - _message_re = re.compile(
181 - r'\s(' + "|".join(_message_commands) + r')\s+"[^"]*"\s*$')
182 - _ignored_commands = ["local", "export"] + _message_commands
183 - ignore_line = re.compile(
184 - r'(^$)|(^\s*#.*)|(^\s*\w+=.*)' +
185 - r'|(^\s*(' + "|".join(_ignored_commands) + r')\s+)')
186 - ignore_comment = False
187 - var_names = ["D", "DISTDIR", "FILESDIR", "S", "T", "ROOT", "WORKDIR"]
188 -
189 - # EAPI=3/Prefix vars
190 - var_names += ["ED", "EPREFIX", "EROOT"]
191 -
192 - # variables for games.eclass
193 - var_names += [
194 - "Ddir", "GAMES_PREFIX_OPT", "GAMES_DATADIR",
195 - "GAMES_DATADIR_BASE", "GAMES_SYSCONFDIR", "GAMES_STATEDIR",
196 - "GAMES_LOGDIR", "GAMES_BINDIR"]
197 -
198 - # variables for multibuild.eclass
199 - var_names += ["BUILD_DIR"]
200 -
201 - var_names = "(%s)" % "|".join(var_names)
202 - var_reference = re.compile(
203 - r'\$(\{%s\}|%s\W)' % (var_names, var_names))
204 - missing_quotes = re.compile(
205 - r'(\s|^)[^"\'\s]*\$\{?%s\}?[^"\'\s]*(\s|$)' % var_names)
206 - cond_begin = re.compile(r'(^|\s+)\[\[($|\\$|\s+)')
207 - cond_end = re.compile(r'(^|\s+)\]\]($|\\$|\s+)')
208 -
209 - def check(self, num, line):
210 - if self.var_reference.search(line) is None:
211 - return
212 - # There can be multiple matches / violations on a single line. We
213 - # have to make sure none of the matches are violators. Once we've
214 - # found one violator, any remaining matches on the same line can
215 - # be ignored.
216 - pos = 0
217 - while pos <= len(line) - 1:
218 - missing_quotes = self.missing_quotes.search(line, pos)
219 - if not missing_quotes:
220 - break
221 - # If the last character of the previous match is a whitespace
222 - # character, that character may be needed for the next
223 - # missing_quotes match, so search overlaps by 1 character.
224 - group = missing_quotes.group()
225 - pos = missing_quotes.end() - 1
226 -
227 - # Filter out some false positives that can
228 - # get through the missing_quotes regex.
229 - if self.var_reference.search(group) is None:
230 - continue
231 -
232 - # Filter matches that appear to be an
233 - # argument to a message command.
234 - # For example: false || ewarn "foo $WORKDIR/bar baz"
235 - message_match = self._message_re.search(line)
236 - if message_match is not None and \
237 - message_match.start() < pos and \
238 - message_match.end() > pos:
239 - break
240 -
241 - # This is an attempt to avoid false positives without getting
242 - # too complex, while possibly allowing some (hopefully
243 - # unlikely) violations to slip through. We just assume
244 - # everything is correct if the there is a ' [[ ' or a ' ]] '
245 - # anywhere in the whole line (possibly continued over one
246 - # line).
247 - if self.cond_begin.search(line) is not None:
248 - continue
249 - if self.cond_end.search(line) is not None:
250 - continue
251 -
252 - # Any remaining matches on the same line can be ignored.
253 - return errors.MISSING_QUOTES_ERROR
254 -
255 -
256 -class EbuildAssignment(LineCheck):
257 - """Ensure ebuilds don't assign to readonly variables."""
258 -
259 - repoman_check_name = 'variable.readonly'
260 - read_only_vars = 'A|CATEGORY|P|P[VNRF]|PVR|D|WORKDIR|FILESDIR|FEATURES|USE'
261 - readonly_assignment = re.compile(r'^\s*(export\s+)?(%s)=' % read_only_vars)
262 -
263 - def check(self, num, line):
264 - match = self.readonly_assignment.match(line)
265 - e = None
266 - if match is not None:
267 - e = errors.READONLY_ASSIGNMENT_ERROR
268 - return e
269 -
270 -
271 -class Eapi3EbuildAssignment(EbuildAssignment):
272 - """Ensure ebuilds don't assign to readonly EAPI 3-introduced variables."""
273 -
274 - readonly_assignment = re.compile(r'\s*(export\s+)?(ED|EPREFIX|EROOT)=')
275 -
276 - def check_eapi(self, eapi):
277 - return eapi_supports_prefix(eapi)
278 -
279 -
280 -class EbuildNestedDie(LineCheck):
281 - """Check ebuild for nested die statements (die statements in subshells)"""
282 -
283 - repoman_check_name = 'ebuild.nesteddie'
284 - nesteddie_re = re.compile(r'^[^#]*\s\(\s[^)]*\bdie\b')
285 -
286 - def check(self, num, line):
287 - if self.nesteddie_re.match(line):
288 - return errors.NESTED_DIE_ERROR
289 -
290 -
291 -class EbuildUselessDodoc(LineCheck):
292 - """Check ebuild for useless files in dodoc arguments."""
293 - repoman_check_name = 'ebuild.minorsyn'
294 - uselessdodoc_re = re.compile(
295 - r'^\s*dodoc(\s+|\s+.*\s+)(ABOUT-NLS|COPYING|LICENCE|LICENSE)($|\s)')
296 -
297 - def check(self, num, line):
298 - match = self.uselessdodoc_re.match(line)
299 - if match:
300 - return "Useless dodoc '%s'" % (match.group(2), ) + " on line: %d"
301 -
302 -
303 -class EbuildUselessCdS(LineCheck):
304 - """Check for redundant cd ${S} statements"""
305 - repoman_check_name = 'ebuild.minorsyn'
306 - _src_phases = r'^\s*src_(prepare|configure|compile|install|test)\s*\(\)'
307 - method_re = re.compile(_src_phases)
308 - cds_re = re.compile(r'^\s*cd\s+("\$(\{S\}|S)"|\$(\{S\}|S))\s')
309 -
310 - def __init__(self):
311 - self.check_next_line = False
312 -
313 - def check(self, num, line):
314 - if self.check_next_line:
315 - self.check_next_line = False
316 - if self.cds_re.match(line):
317 - return errors.REDUNDANT_CD_S_ERROR
318 - elif self.method_re.match(line):
319 - self.check_next_line = True
320 -
321 -
322 -class EapiDefinition(LineCheck):
323 - """
324 - Check that EAPI assignment conforms to PMS section 7.3.1
325 - (first non-comment, non-blank line).
326 - """
327 - repoman_check_name = 'EAPI.definition'
328 - ignore_comment = True
329 - _eapi_re = portage._pms_eapi_re
330 -
331 - def new(self, pkg):
332 - self._cached_eapi = pkg.eapi
333 - self._parsed_eapi = None
334 - self._eapi_line_num = None
335 -
336 - def check(self, num, line):
337 - if self._eapi_line_num is None and line.strip():
338 - self._eapi_line_num = num + 1
339 - m = self._eapi_re.match(line)
340 - if m is not None:
341 - self._parsed_eapi = m.group(2)
342 -
343 - def end(self):
344 - if self._parsed_eapi is None:
345 - if self._cached_eapi != "0":
346 - yield "valid EAPI assignment must occur on or before line: %s" % \
347 - self._eapi_line_num
348 - elif self._parsed_eapi != self._cached_eapi:
349 - yield (
350 - "bash returned EAPI '%s' which does not match "
351 - "assignment on line: %s" %
352 - (self._cached_eapi, self._eapi_line_num))
353 -
354 -
355 -class EbuildPatches(LineCheck):
356 - """Ensure ebuilds use bash arrays for PATCHES to ensure white space safety"""
357 - repoman_check_name = 'ebuild.patches'
358 - re = re.compile(r'^\s*PATCHES=[^\(]')
359 - error = errors.PATCHES_ERROR
360 -
361 - def check_eapi(self, eapi):
362 - return eapi in ("0", "1", "2", "3", "4", "4-python",
363 - "4-slot-abi", "5", "5-hdepend", "5-progress")
364 -
365 -
366 -class EbuildQuotedA(LineCheck):
367 - """Ensure ebuilds have no quoting around ${A}"""
368 -
369 - repoman_check_name = 'ebuild.minorsyn'
370 - a_quoted = re.compile(r'.*\"\$(\{A\}|A)\"')
371 -
372 - def check(self, num, line):
373 - match = self.a_quoted.match(line)
374 - if match:
375 - return "Quoted \"${A}\" on line: %d"
376 -
377 -
378 -class NoOffsetWithHelpers(LineCheck):
379 - """ Check that the image location, the alternate root offset, and the
380 - offset prefix (D, ROOT, ED, EROOT and EPREFIX) are not used with
381 - helpers """
382 -
383 - repoman_check_name = 'variable.usedwithhelpers'
384 - # Ignore matches in quoted strings like this:
385 - # elog "installed into ${ROOT}usr/share/php5/apc/."
386 - _install_funcs = (
387 - 'docinto|do(compress|dir|hard)'
388 - '|exeinto|fowners|fperms|insinto|into')
389 - _quoted_vars = 'D|ROOT|ED|EROOT|EPREFIX'
390 - re = re.compile(
391 - r'^[^#"\']*\b(%s)\s+"?\$\{?(%s)\b.*' %
392 - (_install_funcs, _quoted_vars))
393 - error = errors.NO_OFFSET_WITH_HELPERS
394 -
395 -
396 -class ImplicitRuntimeDeps(LineCheck):
397 - """
398 - Detect the case where DEPEND is set and RDEPEND is unset in the ebuild,
399 - since this triggers implicit RDEPEND=$DEPEND assignment (prior to EAPI 4).
400 - """
401 -
402 - repoman_check_name = 'RDEPEND.implicit'
403 - _assignment_re = re.compile(r'^\s*(R?DEPEND)\+?=')
404 -
405 - def new(self, pkg):
406 - self._rdepend = False
407 - self._depend = False
408 -
409 - def check_eapi(self, eapi):
410 - # Beginning with EAPI 4, there is no
411 - # implicit RDEPEND=$DEPEND assignment
412 - # to be concerned with.
413 - return eapi_has_implicit_rdepend(eapi)
414 -
415 - def check(self, num, line):
416 - if not self._rdepend:
417 - m = self._assignment_re.match(line)
418 - if m is None:
419 - pass
420 - elif m.group(1) == "RDEPEND":
421 - self._rdepend = True
422 - elif m.group(1) == "DEPEND":
423 - self._depend = True
424 -
425 - def end(self):
426 - if self._depend and not self._rdepend:
427 - yield 'RDEPEND is not explicitly assigned'
428 -
429 -
430 -class InheritDeprecated(LineCheck):
431 - """Check if ebuild directly or indirectly inherits a deprecated eclass."""
432 -
433 - repoman_check_name = 'inherit.deprecated'
434 -
435 - # deprecated eclass : new eclass (False if no new eclass)
436 - deprecated_eclasses = {
437 - "autotools-multilib": "multilib-minimal",
438 - "autotools-utils": False,
439 - "base": False,
440 - "bash-completion": "bash-completion-r1",
441 - "boost-utils": False,
442 - "clutter": "gnome2",
443 - "confutils": False,
444 - "distutils": "distutils-r1",
445 - "fdo-mime": "xdg-utils",
446 - "games": False,
447 - "gems": "ruby-fakegem",
448 - "git-2": "git-r3",
449 - "gpe": False,
450 - "gst-plugins-bad": "gstreamer",
451 - "gst-plugins-base": "gstreamer",
452 - "gst-plugins-good": "gstreamer",
453 - "gst-plugins-ugly": "gstreamer",
454 - "gst-plugins10": "gstreamer",
455 - "mono": "mono-env",
456 - "python": "python-r1 / python-single-r1 / python-any-r1",
457 - "ruby": "ruby-ng",
458 - "x-modular": "xorg-2",
459 - "xfconf": False,
460 - }
461 -
462 - _inherit_re = re.compile(r'^\s*inherit\s(.*)$')
463 -
464 - def new(self, pkg):
465 - self._errors = []
466 -
467 - def check(self, num, line):
468 - direct_inherits = None
469 - m = self._inherit_re.match(line)
470 - if m is not None:
471 - direct_inherits = m.group(1)
472 - if direct_inherits:
473 - direct_inherits = direct_inherits.split()
474 -
475 - if not direct_inherits:
476 - return
477 -
478 - for eclass in direct_inherits:
479 - replacement = self.deprecated_eclasses.get(eclass)
480 - if replacement is None:
481 - pass
482 - elif replacement is False:
483 - self._errors.append(
484 - "please migrate from "
485 - "'%s' (no replacement) on line: %d" % (eclass, num + 1))
486 - else:
487 - self._errors.append(
488 - "please migrate from "
489 - "'%s' to '%s' on line: %d" % (eclass, replacement, num + 1))
490 -
491 - def end(self):
492 - for error in self._errors:
493 - yield error
494 - del self._errors
495 -
496 -
497 -
498 -class InheritEclass(LineCheck):
499 - """
500 - Base class for checking for missing inherits, as well as excess inherits.
501 -
502 - Args:
503 - eclass: Set to the name of your eclass.
504 - funcs: A tuple of functions that this eclass provides.
505 - comprehensive: Is the list of functions complete?
506 - exempt_eclasses: If these eclasses are inherited, disable the missing
507 - inherit check.
508 - """
509 -
510 - def __init__(
511 - self, eclass, funcs=None, comprehensive=False,
512 - exempt_eclasses=None, ignore_missing=False, **kwargs):
513 - self._eclass = eclass
514 - self._comprehensive = comprehensive
515 - self._exempt_eclasses = exempt_eclasses
516 - self._ignore_missing = ignore_missing
517 - inherit_re = eclass
518 - self._inherit_re = re.compile(
519 - r'^(\s*|.*[|&]\s*)\binherit\s(.*\s)?%s(\s|$)' % inherit_re)
520 - # Match when the function is preceded only by leading whitespace, a
521 - # shell operator such as (, {, |, ||, or &&, or optional variable
522 - # setting(s). This prevents false positives in things like elog
523 - # messages, as reported in bug #413285.
524 - self._func_re = re.compile(
525 - r'(^|[|&{(])\s*(\w+=.*)?\b(' + '|'.join(funcs) + r')\b')
526 -
527 - def new(self, pkg):
528 - self.repoman_check_name = 'inherit.missing'
529 - # We can't use pkg.inherited because that tells us all the eclasses that
530 - # have been inherited and not just the ones we inherit directly.
531 - self._inherit = False
532 - self._func_call = False
533 - if self._exempt_eclasses is not None:
534 - inherited = pkg.inherited
535 - self._disabled = any(x in inherited for x in self._exempt_eclasses)
536 - else:
537 - self._disabled = False
538 - self._eapi = pkg.eapi
539 -
540 - def check(self, num, line):
541 - if not self._inherit:
542 - self._inherit = self._inherit_re.match(line)
543 - if not self._inherit:
544 - if self._disabled or self._ignore_missing:
545 - return
546 - s = self._func_re.search(line)
547 - if s is not None:
548 - func_name = s.group(3)
549 - eapi_func = _eclass_eapi_functions.get(func_name)
550 - if eapi_func is None or not eapi_func(self._eapi):
551 - self._func_call = True
552 - return (
553 - '%s.eclass is not inherited, '
554 - 'but "%s" found at line: %s' %
555 - (self._eclass, func_name, '%d'))
556 - elif not self._func_call:
557 - self._func_call = self._func_re.search(line)
558 -
559 - def end(self):
560 - if not self._disabled and self._comprehensive and self._inherit \
561 - and not self._func_call:
562 - self.repoman_check_name = 'inherit.unused'
563 - yield 'no function called from %s.eclass; please drop' % self._eclass
564 -
565 -_usex_supported_eapis = ("0", "1", "2", "3", "4", "4-python", "4-slot-abi")
566 -_in_iuse_supported_eapis = ("0", "1", "2", "3", "4", "4-python", "4-slot-abi",
567 - "5", "5-hdepend", "5-progress")
568 -_get_libdir_supported_eapis = _in_iuse_supported_eapis
569 -_eclass_eapi_functions = {
570 - "usex": lambda eapi: eapi not in _usex_supported_eapis,
571 - "in_iuse": lambda eapi: eapi not in _in_iuse_supported_eapis,
572 - "get_libdir": lambda eapi: eapi not in _get_libdir_supported_eapis,
573 -}
574 -
575 -# eclasses that export ${ECLASS}_src_(compile|configure|install)
576 -_eclass_export_functions = (
577 - 'ant-tasks', 'apache-2', 'apache-module', 'aspell-dict',
578 - 'autotools-utils', 'base', 'bsdmk', 'cannadic',
579 - 'clutter', 'cmake-utils', 'db', 'distutils', 'elisp',
580 - 'embassy', 'emboss', 'emul-linux-x86', 'enlightenment',
581 - 'font-ebdftopcf', 'font', 'fox', 'freebsd', 'freedict',
582 - 'games', 'games-ggz', 'games-mods', 'gdesklets',
583 - 'gems', 'gkrellm-plugin', 'gnatbuild', 'gnat', 'gnome2',
584 - 'gnome-python-common', 'gnustep-base', 'go-mono', 'gpe',
585 - 'gst-plugins-bad', 'gst-plugins-base', 'gst-plugins-good',
586 - 'gst-plugins-ugly', 'gtk-sharp-module', 'haskell-cabal',
587 - 'horde', 'java-ant-2', 'java-pkg-2', 'java-pkg-simple',
588 - 'java-virtuals-2', 'kde4-base', 'kde4-meta', 'kernel-2',
589 - 'latex-package', 'linux-mod', 'mozlinguas', 'myspell',
590 - 'myspell-r2', 'mysql', 'mysql-v2', 'mythtv-plugins',
591 - 'oasis', 'obs-service', 'office-ext', 'perl-app',
592 - 'perl-module', 'php-ext-base-r1', 'php-ext-pecl-r2',
593 - 'php-ext-source-r2', 'php-lib-r1', 'php-pear-lib-r1',
594 - 'php-pear-r1', 'python-distutils-ng', 'python',
595 - 'qt4-build', 'qt4-r2', 'rox-0install', 'rox', 'ruby',
596 - 'ruby-ng', 'scsh', 'selinux-policy-2', 'sgml-catalog',
597 - 'stardict', 'sword-module', 'tetex-3', 'tetex',
598 - 'texlive-module', 'toolchain-binutils', 'toolchain',
599 - 'twisted', 'vdr-plugin-2', 'vdr-plugin', 'vim',
600 - 'vim-plugin', 'vim-spell', 'virtuoso', 'vmware',
601 - 'vmware-mod', 'waf-utils', 'webapp', 'xemacs-elisp',
602 - 'xemacs-packages', 'xfconf', 'x-modular', 'xorg-2',
603 - 'zproduct'
604 -)
605 -
606 -_eclass_info = {
607 - 'autotools': {
608 - 'funcs': (
609 - 'eaclocal', 'eautoconf', 'eautoheader',
610 - 'eautomake', 'eautoreconf', '_elibtoolize',
611 - 'eautopoint'
612 - ),
613 - 'comprehensive': True,
614 -
615 - # Exempt eclasses:
616 - # git - An EGIT_BOOTSTRAP variable may be used to call one of
617 - # the autotools functions.
618 - # subversion - An ESVN_BOOTSTRAP variable may be used to call one of
619 - # the autotools functions.
620 - 'exempt_eclasses': ('git', 'git-2', 'subversion', 'autotools-utils')
621 - },
622 -
623 - 'eutils': {
624 - 'funcs': (
625 - 'estack_push', 'estack_pop', 'eshopts_push', 'eshopts_pop',
626 - 'eumask_push', 'eumask_pop', 'epatch', 'epatch_user',
627 - 'emktemp', 'edos2unix', 'in_iuse', 'use_if_iuse', 'usex'
628 - ),
629 - 'comprehensive': False,
630 -
631 - # These are "eclasses are the whole ebuild" type thing.
632 - 'exempt_eclasses': _eclass_export_functions,
633 - },
634 -
635 - 'flag-o-matic': {
636 - 'funcs': (
637 - 'filter-(ld)?flags', 'strip-flags', 'strip-unsupported-flags',
638 - 'append-((ld|c(pp|xx)?))?flags', 'append-libs',
639 - ),
640 - 'comprehensive': False
641 - },
642 -
643 - 'libtool': {
644 - 'funcs': (
645 - 'elibtoolize',
646 - ),
647 - 'comprehensive': True,
648 - 'exempt_eclasses': ('autotools',)
649 - },
650 -
651 - 'multilib': {
652 - 'funcs': (
653 - 'get_libdir',
654 - ),
655 -
656 - # These are "eclasses are the whole ebuild" type thing.
657 - 'exempt_eclasses': _eclass_export_functions + (
658 - 'autotools', 'libtool', 'multilib-minimal'),
659 -
660 - 'comprehensive': False
661 - },
662 -
663 - 'multiprocessing': {
664 - 'funcs': (
665 - 'makeopts_jobs',
666 - ),
667 - 'comprehensive': False
668 - },
669 -
670 - 'prefix': {
671 - 'funcs': (
672 - 'eprefixify',
673 - ),
674 - 'comprehensive': True
675 - },
676 -
677 - 'toolchain-funcs': {
678 - 'funcs': (
679 - 'gen_usr_ldscript',
680 - ),
681 - 'comprehensive': False
682 - },
683 -
684 - 'user': {
685 - 'funcs': (
686 - 'enewuser', 'enewgroup',
687 - 'egetent', 'egethome', 'egetshell', 'esethome'
688 - ),
689 - 'comprehensive': True
690 - }
691 -}
692 -
693 -
694 -class EMakeParallelDisabled(PhaseCheck):
695 - """Check for emake -j1 calls which disable parallelization."""
696 - repoman_check_name = 'upstream.workaround'
697 - re = re.compile(r'^\s*emake\s+.*-j\s*1\b')
698 - error = errors.EMAKE_PARALLEL_DISABLED
699 -
700 - def phase_check(self, num, line):
701 - if self.in_phase == 'src_compile' or self.in_phase == 'src_install':
702 - if self.re.match(line):
703 - return self.error
704 -
705 -
706 -class EMakeParallelDisabledViaMAKEOPTS(LineCheck):
707 - """Check for MAKEOPTS=-j1 that disables parallelization."""
708 - repoman_check_name = 'upstream.workaround'
709 - re = re.compile(r'^\s*MAKEOPTS=(\'|")?.*-j\s*1\b')
710 - error = errors.EMAKE_PARALLEL_DISABLED_VIA_MAKEOPTS
711 -
712 -
713 -class UriUseHttps(LineCheck):
714 - """Check that we use https:// for known good sites."""
715 - repoman_check_name = 'uri.https'
716 - _SITES = (
717 - '([-._a-zA-Z0-9]*\.)?apache\.org',
718 - '((alioth|packages(\.qa)?|people|www)\.)?debian\.org',
719 - # Most FDO sites support https, but not all (like tango).
720 - # List the most common ones here for now.
721 - '((anongit|bugs|cgit|dri|patchwork|people|specifications|www|xcb|xorg)\.)?freedesktop\.org',
722 - '((bugs|dev|wiki|www)\.)?gentoo\.org',
723 - '((wiki)\.)?github\.(io|com)',
724 - 'savannah\.(non)?gnu\.org',
725 - '((gcc|www)\.)?gnu\.org',
726 - 'curl\.haxx\.se',
727 - '((bugzilla|git|mirrors|patchwork|planet|www(\.wiki)?)\.)?kernel\.org',
728 - '((bugs|wiki|www)\.)?linuxfoundation\.org',
729 - '((docs|pypi|www)\.)?python\.org',
730 - '(sf|sourceforge)\.net',
731 - '(www\.)?(enlightenment|sourceware|x)\.org',
732 - )
733 - # Try to anchor the end of the URL so we don't get false positives
734 - # with http://github.com.foo.bar.com/. Unlikely, but possible.
735 - re = re.compile(r'.*\bhttp://(%s)(\s|["\'/]|$)' % r'|'.join(_SITES))
736 - error = errors.URI_HTTPS
737 -
738 -
739 -class NoAsNeeded(LineCheck):
740 - """Check for calls to the no-as-needed function."""
741 - repoman_check_name = 'upstream.workaround'
742 - re = re.compile(r'.*\$\(no-as-needed\)')
743 - error = errors.NO_AS_NEEDED
744 -
745 -
746 -class PreserveOldLib(LineCheck):
747 - """Check for calls to the deprecated preserve_old_lib function."""
748 - repoman_check_name = 'ebuild.minorsyn'
749 - re = re.compile(r'.*preserve_old_lib')
750 - error = errors.PRESERVE_OLD_LIB
751 -
752 -
753 -class SandboxAddpredict(LineCheck):
754 - """Check for calls to the addpredict function."""
755 - repoman_check_name = 'upstream.workaround'
756 - re = re.compile(r'(^|\s)addpredict\b')
757 - error = errors.SANDBOX_ADDPREDICT
758 -
759 -
760 -class DeprecatedBindnowFlags(LineCheck):
761 - """Check for calls to the deprecated bindnow-flags function."""
762 - repoman_check_name = 'ebuild.minorsyn'
763 - re = re.compile(r'.*\$\(bindnow-flags\)')
764 - error = errors.DEPRECATED_BINDNOW_FLAGS
765 -
766 -
767 -class WantAutoDefaultValue(LineCheck):
768 - """Check setting WANT_AUTO* to latest (default value)."""
769 - repoman_check_name = 'ebuild.minorsyn'
770 - _re = re.compile(r'^WANT_AUTO(CONF|MAKE)=(\'|")?latest')
771 -
772 - def check(self, num, line):
773 - m = self._re.match(line)
774 - if m is not None:
775 - return 'WANT_AUTO' + m.group(1) + \
776 - ' redundantly set to default value "latest" on line: %d'
777 -
778 -
779 -class SrcCompileEconf(PhaseCheck):
780 - repoman_check_name = 'ebuild.minorsyn'
781 - configure_re = re.compile(r'\s(econf|./configure)')
782 -
783 - def check_eapi(self, eapi):
784 - return eapi_has_src_prepare_and_src_configure(eapi)
785 -
786 - def phase_check(self, num, line):
787 - if self.in_phase == 'src_compile':
788 - m = self.configure_re.match(line)
789 - if m is not None:
790 - return ("'%s'" % m.group(1)) + \
791 - " call should be moved to src_configure from line: %d"
792 -
793 -
794 -class SrcUnpackPatches(PhaseCheck):
795 - repoman_check_name = 'ebuild.minorsyn'
796 - src_prepare_tools_re = re.compile(r'\s(e?patch|sed)\s')
797 -
798 - def check_eapi(self, eapi):
799 - return eapi_has_src_prepare_and_src_configure(eapi)
800 -
801 - def phase_check(self, num, line):
802 - if self.in_phase == 'src_unpack':
803 - m = self.src_prepare_tools_re.search(line)
804 - if m is not None:
805 - return ("'%s'" % m.group(1)) + \
806 - " call should be moved to src_prepare from line: %d"
807 -
808 -
809 -class BuiltWithUse(LineCheck):
810 - repoman_check_name = 'ebuild.minorsyn'
811 - re = re.compile(r'(^|.*\b)built_with_use\b')
812 - error = errors.BUILT_WITH_USE
813 -
814 -
815 -class DeprecatedUseq(LineCheck):
816 - """Checks for use of the deprecated useq function"""
817 - repoman_check_name = 'ebuild.minorsyn'
818 - re = re.compile(r'(^|.*\b)useq\b')
819 - error = errors.USEQ_ERROR
820 -
821 -
822 -class DeprecatedHasq(LineCheck):
823 - """Checks for use of the deprecated hasq function"""
824 - repoman_check_name = 'ebuild.minorsyn'
825 - re = re.compile(r'(^|.*\b)hasq\b')
826 - error = errors.HASQ_ERROR
827 -
828 -
829 -# EAPI <2 checks
830 -class UndefinedSrcPrepareSrcConfigurePhases(LineCheck):
831 - repoman_check_name = 'EAPI.incompatible'
832 - src_configprepare_re = re.compile(r'\s*(src_configure|src_prepare)\s*\(\)')
833 -
834 - def check_eapi(self, eapi):
835 - return not eapi_has_src_prepare_and_src_configure(eapi)
836 -
837 - def check(self, num, line):
838 - m = self.src_configprepare_re.match(line)
839 - if m is not None:
840 - return ("'%s'" % m.group(1)) + \
841 - " phase is not defined in EAPI < 2 on line: %d"
842 -
843 -
844 -# EAPI-3 checks
845 -class Eapi3DeprecatedFuncs(LineCheck):
846 - repoman_check_name = 'EAPI.deprecated'
847 - deprecated_commands_re = re.compile(r'^\s*(check_license)\b')
848 -
849 - def check_eapi(self, eapi):
850 - return eapi not in ('0', '1', '2')
851 -
852 - def check(self, num, line):
853 - m = self.deprecated_commands_re.match(line)
854 - if m is not None:
855 - return ("'%s'" % m.group(1)) + \
856 - " has been deprecated in EAPI=3 on line: %d"
857 -
858 -
859 -# EAPI <4 checks
860 -class UndefinedPkgPretendPhase(LineCheck):
861 - repoman_check_name = 'EAPI.incompatible'
862 - pkg_pretend_re = re.compile(r'\s*(pkg_pretend)\s*\(\)')
863 -
864 - def check_eapi(self, eapi):
865 - return not eapi_has_pkg_pretend(eapi)
866 -
867 - def check(self, num, line):
868 - m = self.pkg_pretend_re.match(line)
869 - if m is not None:
870 - return ("'%s'" % m.group(1)) + \
871 - " phase is not defined in EAPI < 4 on line: %d"
872 -
873 -
874 -# EAPI-4 checks
875 -class Eapi4IncompatibleFuncs(LineCheck):
876 - repoman_check_name = 'EAPI.incompatible'
877 - banned_commands_re = re.compile(r'^\s*(dosed|dohard)')
878 -
879 - def check_eapi(self, eapi):
880 - return not eapi_has_dosed_dohard(eapi)
881 -
882 - def check(self, num, line):
883 - m = self.banned_commands_re.match(line)
884 - if m is not None:
885 - return ("'%s'" % m.group(1)) + \
886 - " has been banned in EAPI=4 on line: %d"
887 -
888 -
889 -class Eapi4GoneVars(LineCheck):
890 - repoman_check_name = 'EAPI.incompatible'
891 - undefined_vars_re = re.compile(
892 - r'.*\$(\{(AA|KV|EMERGE_FROM)\}|(AA|KV|EMERGE_FROM))')
893 -
894 - def check_eapi(self, eapi):
895 - # AA, KV, and EMERGE_FROM should not be referenced in EAPI 4 or later.
896 - return not eapi_exports_AA(eapi)
897 -
898 - def check(self, num, line):
899 - m = self.undefined_vars_re.match(line)
900 - if m is not None:
901 - return ("variable '$%s'" % m.group(1)) + \
902 - " is gone in EAPI=4 on line: %d"
903 -
904 -
905 -class PortageInternal(LineCheck):
906 - repoman_check_name = 'portage.internal'
907 - ignore_comment = True
908 - # Match when the command is preceded only by leading whitespace or a shell
909 - # operator such as (, {, |, ||, or &&. This prevents false positives in
910 - # things like elog messages, as reported in bug #413285.
911 -
912 - internal_portage_func_or_var = (
913 - 'ecompress|ecompressdir|env-update|prepall|prepalldocs|preplib')
914 - re = re.compile(
915 - r'^(\s*|.*[|&{(]+\s*)\b(%s)\b' % internal_portage_func_or_var)
916 -
917 - def check(self, num, line):
918 - """Run the check on line and return error if there is one"""
919 - m = self.re.match(line)
920 - if m is not None:
921 - return ("'%s'" % m.group(2)) + " called on line: %d"
922 -
923 -
924 -class PortageInternalVariableAssignment(LineCheck):
925 - repoman_check_name = 'portage.internal'
926 - internal_assignment = re.compile(
927 - r'\s*(export\s+)?(EXTRA_ECONF|EXTRA_EMAKE)\+?=')
928 -
929 - def check(self, num, line):
930 - match = self.internal_assignment.match(line)
931 - e = None
932 - if match is not None:
933 - e = 'Assignment to variable %s' % match.group(2)
934 - e += ' on line: %d'
935 - return e
936 -
937 -
938 -class EbuildNonRelativeDosym(LineCheck):
939 - """Check ebuild for dosym using absolute paths instead of relative."""
940 - repoman_check_name = 'ebuild.absdosym'
941 - regex = re.compile(
942 - r'^\s*dosym\s+["\']?(/(bin|etc|lib|opt|sbin|srv|usr|var)\S*)')
943 -
944 - def check(self, num, line):
945 - match = self.regex.match(line)
946 - if match:
947 - return "dosym '%s'... could use relative path" % (match.group(1), ) + " on line: %d"
948 -
949 -
950 -_base_check_classes = (InheritEclass, LineCheck, PhaseCheck)
951 -_constant_checks = None
952 -
953 -
954 -def checks_init(experimental_inherit=False):
955 -
956 - global _constant_checks, _eclass_info
957 -
958 - if not experimental_inherit:
959 - # Emulate the old eprefixify.defined and inherit.autotools checks.
960 - _eclass_info = {
961 - 'autotools': {
962 - 'funcs': (
963 - 'eaclocal', 'eautoconf', 'eautoheader',
964 - 'eautomake', 'eautoreconf', '_elibtoolize',
965 - 'eautopoint'
966 - ),
967 - 'comprehensive': True,
968 - 'ignore_missing': True,
969 - 'exempt_eclasses': ('git', 'git-2', 'subversion', 'autotools-utils')
970 - },
971 -
972 - 'prefix': {
973 - 'funcs': (
974 - 'eprefixify',
975 - ),
976 - 'comprehensive': False
977 - }
978 - }
979 -
980 - _constant_checks = tuple(
981 - chain((
982 - v() for k, v in globals().items()
983 - if (
984 - isinstance(v, type)
985 - and issubclass(v, LineCheck)
986 - and v not in _base_check_classes)), (
987 - InheritEclass(k, **kwargs)
988 - for k, kwargs in _eclass_info.items())))
989 -
990 -
991 -_here_doc_re = re.compile(r'.*<<[-]?(\w+)\s*(>\s*\S+\s*)?$')
992 -_ignore_comment_re = re.compile(r'^\s*#')
993 -_continuation_re = re.compile(r'(\\)*$')
994 -
995 -
996 -def run_checks(contents, pkg):
997 - if _constant_checks is None:
998 - checks_init()
999 - checks = _constant_checks
1000 - here_doc_delim = None
1001 - multiline = None
1002 -
1003 - for lc in checks:
1004 - lc.new(pkg)
1005 -
1006 - multinum = 0
1007 - for num, line in enumerate(contents):
1008 -
1009 - # Check if we're inside a here-document.
1010 - if here_doc_delim is not None:
1011 - if here_doc_delim.match(line):
1012 - here_doc_delim = None
1013 - if here_doc_delim is None:
1014 - here_doc = _here_doc_re.match(line)
1015 - if here_doc is not None:
1016 - here_doc_delim = re.compile(r'^\s*%s$' % here_doc.group(1))
1017 - if here_doc_delim is not None:
1018 - continue
1019 -
1020 - # Unroll multiline escaped strings so that we can check things:
1021 - # inherit foo bar \
1022 - # moo \
1023 - # cow
1024 - # This will merge these lines like so:
1025 - # inherit foo bar moo cow
1026 - # A line ending with an even number of backslashes does not count,
1027 - # because the last backslash is escaped. Therefore, search for an
1028 - # odd number of backslashes.
1029 - line_escaped = operator.sub(*_continuation_re.search(line).span()) % 2 == 1
1030 - if multiline:
1031 - # Chop off the \ and \n bytes from the previous line.
1032 - multiline = multiline[:-2] + line
1033 - if not line_escaped:
1034 - line = multiline
1035 - num = multinum
1036 - multiline = None
1037 - else:
1038 - continue
1039 - else:
1040 - if line_escaped:
1041 - multinum = num
1042 - multiline = line
1043 - continue
1044 -
1045 - if not line.endswith("#nowarn\n"):
1046 - # Finally we have a full line to parse.
1047 - is_comment = _ignore_comment_re.match(line) is not None
1048 - for lc in checks:
1049 - if is_comment and lc.ignore_comment:
1050 - continue
1051 - if lc.check_eapi(pkg.eapi):
1052 - ignore = lc.ignore_line
1053 - if not ignore or not ignore.match(line):
1054 - e = lc.check(num, line)
1055 - if e:
1056 - yield lc.repoman_check_name, e % (num + 1)
1057 -
1058 - for lc in checks:
1059 - i = lc.end()
1060 - if i is not None:
1061 - for e in i:
1062 - yield lc.repoman_check_name, e