aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'portage_with_autodep/pym/repoman/checks.py')
-rw-r--r--portage_with_autodep/pym/repoman/checks.py391
1 files changed, 290 insertions, 101 deletions
diff --git a/portage_with_autodep/pym/repoman/checks.py b/portage_with_autodep/pym/repoman/checks.py
index 77df603..85aa065 100644
--- a/portage_with_autodep/pym/repoman/checks.py
+++ b/portage_with_autodep/pym/repoman/checks.py
@@ -1,17 +1,21 @@
# repoman: Checks
-# Copyright 2007-2012 Gentoo Foundation
+# Copyright 2007-2013 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
"""This module contains functions used in Repoman to ascertain the quality
and correctness of an ebuild."""
+from __future__ import unicode_literals
+
+import codecs
+from itertools import chain
import re
import time
import repoman.errors as errors
import portage
from portage.eapi import eapi_supports_prefix, eapi_has_implicit_rdepend, \
eapi_has_src_prepare_and_src_configure, eapi_has_dosed_dohard, \
- eapi_exports_AA, eapi_exports_KV
+ eapi_exports_AA
class LineCheck(object):
"""Run a check on a line of an ebuild."""
@@ -66,7 +70,7 @@ class EbuildHeader(LineCheck):
Copyright header errors
CVS header errors
License header errors
-
+
Args:
modification_year - Year the ebuild was last modified
"""
@@ -109,7 +113,7 @@ class EbuildWhitespace(LineCheck):
ignore_line = re.compile(r'(^$)|(^(\t)*#)')
ignore_comment = False
leading_spaces = re.compile(r'^[\S\t]')
- trailing_whitespace = re.compile(r'.*([\S]$)')
+ trailing_whitespace = re.compile(r'.*([\S]$)')
def check(self, num, line):
if self.leading_spaces.match(line) is None:
@@ -159,6 +163,9 @@ class EbuildQuote(LineCheck):
"GAMES_DATADIR_BASE", "GAMES_SYSCONFDIR", "GAMES_STATEDIR",
"GAMES_LOGDIR", "GAMES_BINDIR"]
+ # variables for multibuild.eclass
+ var_names += ["BUILD_DIR"]
+
var_names = "(%s)" % "|".join(var_names)
var_reference = re.compile(r'\$(\{'+var_names+'\}|' + \
var_names + '\W)')
@@ -166,7 +173,7 @@ class EbuildQuote(LineCheck):
r'\}?[^"\'\s]*(\s|$)')
cond_begin = re.compile(r'(^|\s+)\[\[($|\\$|\s+)')
cond_end = re.compile(r'(^|\s+)\]\]($|\\$|\s+)')
-
+
def check(self, num, line):
if self.var_reference.search(line) is None:
return
@@ -218,21 +225,13 @@ class EbuildAssignment(LineCheck):
"""Ensure ebuilds don't assign to readonly variables."""
repoman_check_name = 'variable.readonly'
-
readonly_assignment = re.compile(r'^\s*(export\s+)?(A|CATEGORY|P|PV|PN|PR|PVR|PF|D|WORKDIR|FILESDIR|FEATURES|USE)=')
- line_continuation = re.compile(r'([^#]*\S)(\s+|\t)\\$')
- ignore_line = re.compile(r'(^$)|(^(\t)*#)')
- ignore_comment = False
-
- def __init__(self):
- self.previous_line = None
def check(self, num, line):
match = self.readonly_assignment.match(line)
e = None
- if match and (not self.previous_line or not self.line_continuation.match(self.previous_line)):
+ if match is not None:
e = errors.READONLY_ASSIGNMENT_ERROR
- self.previous_line = line
return e
class Eapi3EbuildAssignment(EbuildAssignment):
@@ -244,11 +243,11 @@ class Eapi3EbuildAssignment(EbuildAssignment):
return eapi_supports_prefix(eapi)
class EbuildNestedDie(LineCheck):
- """Check ebuild for nested die statements (die statements in subshells"""
-
+ """Check ebuild for nested die statements (die statements in subshells)"""
+
repoman_check_name = 'ebuild.nesteddie'
nesteddie_re = re.compile(r'^[^#]*\s\(\s[^)]*\bdie\b')
-
+
def check(self, num, line):
if self.nesteddie_re.match(line):
return errors.NESTED_DIE_ERROR
@@ -293,7 +292,7 @@ class EapiDefinition(LineCheck):
_eapi_re = portage._pms_eapi_re
def new(self, pkg):
- self._cached_eapi = pkg.metadata['EAPI']
+ self._cached_eapi = pkg.eapi
self._parsed_eapi = None
self._eapi_line_num = None
@@ -331,24 +330,6 @@ class EbuildQuotedA(LineCheck):
if match:
return "Quoted \"${A}\" on line: %d"
-class EprefixifyDefined(LineCheck):
- """ Check that prefix.eclass is inherited if needed"""
-
- repoman_check_name = 'eprefixify.defined'
-
- _eprefixify_re = re.compile(r'\beprefixify\b')
- _inherit_prefix_re = re.compile(r'^\s*inherit\s(.*\s)?prefix\b')
-
- def new(self, pkg):
- self._prefix_inherited = False
-
- def check(self, num, line):
- if self._eprefixify_re.search(line) is not None:
- if not self._prefix_inherited:
- return errors.EPREFIXIFY_MISSING_INHERIT
- elif self._inherit_prefix_re.search(line) is not None:
- self._prefix_inherited = True
-
class NoOffsetWithHelpers(LineCheck):
""" Check that the image location, the alternate root offset, and the
offset prefix (D, ROOT, ED, EROOT and EPREFIX) are not used with
@@ -401,13 +382,18 @@ class InheritDeprecated(LineCheck):
# deprecated eclass : new eclass (False if no new eclass)
deprecated_classes = {
"bash-completion": "bash-completion-r1",
+ "boost-utils": False,
+ "distutils": "distutils-r1",
"gems": "ruby-fakegem",
"git": "git-2",
+ "mono": "mono-env",
"mozconfig-2": "mozconfig-3",
"mozcoreconf": "mozcoreconf-2",
"php-ext-pecl-r1": "php-ext-pecl-r2",
"php-ext-source-r1": "php-ext-source-r2",
"php-pear": "php-pear-r1",
+ "python": "python-r1 / python-single-r1 / python-any-r1",
+ "python-distutils-ng": "python-r1 + distutils-r1",
"qt3": False,
"qt4": "qt4-r2",
"ruby": "ruby-ng",
@@ -464,63 +450,189 @@ class InheritDeprecated(LineCheck):
(eclass, replacement)
del self._indirect_deprecated
-class InheritAutotools(LineCheck):
+class InheritEclass(LineCheck):
"""
- Make sure appropriate functions are called in
- ebuilds that inherit autotools.eclass.
- """
-
- repoman_check_name = 'inherit.autotools'
- _inherit_autotools_re = re.compile(r'^\s*inherit\s(.*\s)?autotools(\s|$)')
- _autotools_funcs = (
- "eaclocal", "eautoconf", "eautoheader",
- "eautomake", "eautoreconf", "_elibtoolize")
- _autotools_func_re = re.compile(r'\b(' + \
- "|".join(_autotools_funcs) + r')\b')
- # Exempt eclasses:
- # git - An EGIT_BOOTSTRAP variable may be used to call one of
- # the autotools functions.
- # subversion - An ESVN_BOOTSTRAP variable may be used to call one of
- # the autotools functions.
- _exempt_eclasses = frozenset(["git", "subversion"])
-
- def new(self, pkg):
- self._inherit_autotools = None
- self._autotools_func_call = None
- self._disabled = self._exempt_eclasses.intersection(pkg.inherited)
-
- def check(self, num, line):
- if self._disabled:
- return
- if self._inherit_autotools is None:
- self._inherit_autotools = self._inherit_autotools_re.match(line)
- if self._inherit_autotools is not None and \
- self._autotools_func_call is None:
- self._autotools_func_call = self._autotools_func_re.search(line)
-
- def end(self):
- if self._inherit_autotools and self._autotools_func_call is None:
- yield 'no eauto* function called'
+ Base class for checking for missing inherits, as well as excess inherits.
-class IUseUndefined(LineCheck):
- """
- Make sure the ebuild defines IUSE (style guideline
- says to define IUSE even when empty).
+ Args:
+ eclass: Set to the name of your eclass.
+ funcs: A tuple of functions that this eclass provides.
+ comprehensive: Is the list of functions complete?
+ exempt_eclasses: If these eclasses are inherited, disable the missing
+ inherit check.
"""
- repoman_check_name = 'IUSE.undefined'
- _iuse_def_re = re.compile(r'^IUSE=.*')
+ def __init__(self, eclass, funcs=None, comprehensive=False,
+ exempt_eclasses=None, ignore_missing=False, **kwargs):
+ self._eclass = eclass
+ self._comprehensive = comprehensive
+ self._exempt_eclasses = exempt_eclasses
+ self._ignore_missing = ignore_missing
+ inherit_re = eclass
+ self._inherit_re = re.compile(r'^(\s*|.*[|&]\s*)\binherit\s(.*\s)?%s(\s|$)' % inherit_re)
+ # Match when the function is preceded only by leading whitespace, a
+ # shell operator such as (, {, |, ||, or &&, or optional variable
+ # setting(s). This prevents false positives in things like elog
+ # messages, as reported in bug #413285.
+ self._func_re = re.compile(r'(^|[|&{(])\s*(\w+=.*)?\b(' + '|'.join(funcs) + r')\b')
def new(self, pkg):
- self._iuse_def = None
+ self.repoman_check_name = 'inherit.missing'
+ # We can't use pkg.inherited because that tells us all the eclasses that
+ # have been inherited and not just the ones we inherit directly.
+ self._inherit = False
+ self._func_call = False
+ if self._exempt_eclasses is not None:
+ inherited = pkg.inherited
+ self._disabled = any(x in inherited for x in self._exempt_eclasses)
+ else:
+ self._disabled = False
+ self._eapi = pkg.eapi
def check(self, num, line):
- if self._iuse_def is None:
- self._iuse_def = self._iuse_def_re.match(line)
+ if not self._inherit:
+ self._inherit = self._inherit_re.match(line)
+ if not self._inherit:
+ if self._disabled or self._ignore_missing:
+ return
+ s = self._func_re.search(line)
+ if s is not None:
+ func_name = s.group(3)
+ eapi_func = _eclass_eapi_functions.get(func_name)
+ if eapi_func is None or not eapi_func(self._eapi):
+ self._func_call = True
+ return ('%s.eclass is not inherited, '
+ 'but "%s" found at line: %s') % \
+ (self._eclass, func_name, '%d')
+ elif not self._func_call:
+ self._func_call = self._func_re.search(line)
def end(self):
- if self._iuse_def is None:
- yield 'IUSE is not defined'
+ if not self._disabled and self._comprehensive and self._inherit and not self._func_call:
+ self.repoman_check_name = 'inherit.unused'
+ yield 'no function called from %s.eclass; please drop' % self._eclass
+
+_eclass_eapi_functions = {
+ "usex" : lambda eapi: eapi not in ("0", "1", "2", "3", "4", "4-python", "4-slot-abi")
+}
+
+# eclasses that export ${ECLASS}_src_(compile|configure|install)
+_eclass_export_functions = (
+ 'ant-tasks', 'apache-2', 'apache-module', 'aspell-dict',
+ 'autotools-utils', 'base', 'bsdmk', 'cannadic',
+ 'clutter', 'cmake-utils', 'db', 'distutils', 'elisp',
+ 'embassy', 'emboss', 'emul-linux-x86', 'enlightenment',
+ 'font-ebdftopcf', 'font', 'fox', 'freebsd', 'freedict',
+ 'games', 'games-ggz', 'games-mods', 'gdesklets',
+ 'gems', 'gkrellm-plugin', 'gnatbuild', 'gnat', 'gnome2',
+ 'gnome-python-common', 'gnustep-base', 'go-mono', 'gpe',
+ 'gst-plugins-bad', 'gst-plugins-base', 'gst-plugins-good',
+ 'gst-plugins-ugly', 'gtk-sharp-module', 'haskell-cabal',
+ 'horde', 'java-ant-2', 'java-pkg-2', 'java-pkg-simple',
+ 'java-virtuals-2', 'kde4-base', 'kde4-meta', 'kernel-2',
+ 'latex-package', 'linux-mod', 'mozlinguas', 'myspell',
+ 'myspell-r2', 'mysql', 'mysql-v2', 'mythtv-plugins',
+ 'oasis', 'obs-service', 'office-ext', 'perl-app',
+ 'perl-module', 'php-ext-base-r1', 'php-ext-pecl-r2',
+ 'php-ext-source-r2', 'php-lib-r1', 'php-pear-lib-r1',
+ 'php-pear-r1', 'python-distutils-ng', 'python',
+ 'qt4-build', 'qt4-r2', 'rox-0install', 'rox', 'ruby',
+ 'ruby-ng', 'scsh', 'selinux-policy-2', 'sgml-catalog',
+ 'stardict', 'sword-module', 'tetex-3', 'tetex',
+ 'texlive-module', 'toolchain-binutils', 'toolchain',
+ 'twisted', 'vdr-plugin-2', 'vdr-plugin', 'vim',
+ 'vim-plugin', 'vim-spell', 'virtuoso', 'vmware',
+ 'vmware-mod', 'waf-utils', 'webapp', 'xemacs-elisp',
+ 'xemacs-packages', 'xfconf', 'x-modular', 'xorg-2',
+ 'zproduct'
+)
+
+_eclass_info = {
+ 'autotools': {
+ 'funcs': (
+ 'eaclocal', 'eautoconf', 'eautoheader',
+ 'eautomake', 'eautoreconf', '_elibtoolize',
+ 'eautopoint'
+ ),
+ 'comprehensive': True,
+
+ # Exempt eclasses:
+ # git - An EGIT_BOOTSTRAP variable may be used to call one of
+ # the autotools functions.
+ # subversion - An ESVN_BOOTSTRAP variable may be used to call one of
+ # the autotools functions.
+ 'exempt_eclasses': ('git', 'git-2', 'subversion', 'autotools-utils')
+ },
+
+ 'eutils': {
+ 'funcs': (
+ 'estack_push', 'estack_pop', 'eshopts_push', 'eshopts_pop',
+ 'eumask_push', 'eumask_pop', 'epatch', 'epatch_user',
+ 'emktemp', 'edos2unix', 'in_iuse', 'use_if_iuse', 'usex'
+ ),
+ 'comprehensive': False,
+
+ # These are "eclasses are the whole ebuild" type thing.
+ 'exempt_eclasses': _eclass_export_functions,
+ },
+
+ 'flag-o-matic': {
+ 'funcs': (
+ 'filter-(ld)?flags', 'strip-flags', 'strip-unsupported-flags',
+ 'append-((ld|c(pp|xx)?))?flags', 'append-libs',
+ ),
+ 'comprehensive': False
+ },
+
+ 'libtool': {
+ 'funcs': (
+ 'elibtoolize',
+ ),
+ 'comprehensive': True,
+ 'exempt_eclasses': ('autotools',)
+ },
+
+ 'multilib': {
+ 'funcs': (
+ 'get_libdir',
+ ),
+
+ # These are "eclasses are the whole ebuild" type thing.
+ 'exempt_eclasses': _eclass_export_functions + ('autotools', 'libtool',
+ 'multilib-minimal'),
+
+ 'comprehensive': False
+ },
+
+ 'multiprocessing': {
+ 'funcs': (
+ 'makeopts_jobs',
+ ),
+ 'comprehensive': False
+ },
+
+ 'prefix': {
+ 'funcs': (
+ 'eprefixify',
+ ),
+ 'comprehensive': True
+ },
+
+ 'toolchain-funcs': {
+ 'funcs': (
+ 'gen_usr_ldscript',
+ ),
+ 'comprehensive': False
+ },
+
+ 'user': {
+ 'funcs': (
+ 'enewuser', 'enewgroup',
+ 'egetent', 'egethome', 'egetshell', 'esethome'
+ ),
+ 'comprehensive': True
+ }
+}
class EMakeParallelDisabled(PhaseCheck):
"""Check for emake -j1 calls which disable parallelization."""
@@ -546,8 +658,8 @@ class NoAsNeeded(LineCheck):
error = errors.NO_AS_NEEDED
class PreserveOldLib(LineCheck):
- """Check for calls to the preserve_old_lib function."""
- repoman_check_name = 'upstream.workaround'
+ """Check for calls to the deprecated preserve_old_lib function."""
+ repoman_check_name = 'ebuild.minorsyn'
re = re.compile(r'.*preserve_old_lib')
error = errors.PRESERVE_OLD_LIB
@@ -665,7 +777,7 @@ class PortageInternal(LineCheck):
repoman_check_name = 'portage.internal'
ignore_comment = True
# Match when the command is preceded only by leading whitespace or a shell
- # operator such as (, {, |, ||, or &&. This prevents false postives in
+ # operator such as (, {, |, ||, or &&. This prevents false positives in
# things like elog messages, as reported in bug #413285.
re = re.compile(r'^(\s*|.*[|&{(]+\s*)\b(ecompress|ecompressdir|env-update|prepall|prepalldocs|preplib)\b')
@@ -675,25 +787,64 @@ class PortageInternal(LineCheck):
if m is not None:
return ("'%s'" % m.group(2)) + " called on line: %d"
-_constant_checks = tuple((c() for c in (
- EbuildHeader, EbuildWhitespace, EbuildBlankLine, EbuildQuote,
- EbuildAssignment, Eapi3EbuildAssignment, EbuildUselessDodoc,
- EbuildUselessCdS, EbuildNestedDie,
- EbuildPatches, EbuildQuotedA, EapiDefinition, EprefixifyDefined,
- ImplicitRuntimeDeps, InheritAutotools, InheritDeprecated, IUseUndefined,
- EMakeParallelDisabled, EMakeParallelDisabledViaMAKEOPTS, NoAsNeeded,
- DeprecatedBindnowFlags, SrcUnpackPatches, WantAutoDefaultValue,
- SrcCompileEconf, Eapi3DeprecatedFuncs, NoOffsetWithHelpers,
- Eapi4IncompatibleFuncs, Eapi4GoneVars, BuiltWithUse,
- PreserveOldLib, SandboxAddpredict, PortageInternal,
- DeprecatedUseq, DeprecatedHasq)))
+class PortageInternalVariableAssignment(LineCheck):
+ repoman_check_name = 'portage.internal'
+ internal_assignment = re.compile(r'\s*(export\s+)?(EXTRA_ECONF|EXTRA_EMAKE)\+?=')
+
+ def check(self, num, line):
+ match = self.internal_assignment.match(line)
+ e = None
+ if match is not None:
+ e = 'Assignment to variable %s' % match.group(2)
+ e += ' on line: %d'
+ return e
+
+_base_check_classes = (InheritEclass, LineCheck, PhaseCheck)
+_constant_checks = None
+
+def _init(experimental_inherit=False):
+
+ global _constant_checks, _eclass_info
+
+ if not experimental_inherit:
+ # Emulate the old eprefixify.defined and inherit.autotools checks.
+ _eclass_info = {
+ 'autotools': {
+ 'funcs': (
+ 'eaclocal', 'eautoconf', 'eautoheader',
+ 'eautomake', 'eautoreconf', '_elibtoolize',
+ 'eautopoint'
+ ),
+ 'comprehensive': True,
+ 'ignore_missing': True,
+ 'exempt_eclasses': ('git', 'git-2', 'subversion', 'autotools-utils')
+ },
+
+ 'prefix': {
+ 'funcs': (
+ 'eprefixify',
+ ),
+ 'comprehensive': False
+ }
+ }
+
+ _constant_checks = tuple(chain((v() for k, v in globals().items()
+ if isinstance(v, type) and issubclass(v, LineCheck) and
+ v not in _base_check_classes),
+ (InheritEclass(k, **portage._native_kwargs(kwargs))
+ for k, kwargs in _eclass_info.items())))
_here_doc_re = re.compile(r'.*\s<<[-]?(\w+)$')
_ignore_comment_re = re.compile(r'^\s*#')
def run_checks(contents, pkg):
+ unicode_escape_codec = codecs.lookup('unicode_escape')
+ unicode_escape = lambda x: unicode_escape_codec.decode(x)[0]
+ if _constant_checks is None:
+ _init()
checks = _constant_checks
here_doc_delim = None
+ multiline = None
for lc in checks:
lc.new(pkg)
@@ -707,14 +858,52 @@ def run_checks(contents, pkg):
here_doc = _here_doc_re.match(line)
if here_doc is not None:
here_doc_delim = re.compile(r'^\s*%s$' % here_doc.group(1))
+ if here_doc_delim is not None:
+ continue
+
+ # Unroll multiline escaped strings so that we can check things:
+ # inherit foo bar \
+ # moo \
+ # cow
+ # This will merge these lines like so:
+ # inherit foo bar moo cow
+ try:
+ # A normal line will end in the two bytes: <\> <\n>. So decoding
+ # that will result in python thinking the <\n> is being escaped
+ # and eat the single <\> which makes it hard for us to detect.
+ # Instead, strip the newline (which we know all lines have), and
+ # append a <0>. Then when python escapes it, if the line ended
+ # in a <\>, we'll end up with a <\0> marker to key off of. This
+ # shouldn't be a problem with any valid ebuild ...
+ line_escaped = unicode_escape(line.rstrip('\n') + '0')
+ except SystemExit:
+ raise
+ except:
+ # Who knows what kind of crazy crap an ebuild will have
+ # in it -- don't allow it to kill us.
+ line_escaped = line
+ if multiline:
+ # Chop off the \ and \n bytes from the previous line.
+ multiline = multiline[:-2] + line
+ if not line_escaped.endswith('\0'):
+ line = multiline
+ num = multinum
+ multiline = None
+ else:
+ continue
+ else:
+ if line_escaped.endswith('\0'):
+ multinum = num
+ multiline = line
+ continue
- if here_doc_delim is None:
- # We're not in a here-document.
+ if not line.endswith("#nowarn\n"):
+ # Finally we have a full line to parse.
is_comment = _ignore_comment_re.match(line) is not None
for lc in checks:
if is_comment and lc.ignore_comment:
continue
- if lc.check_eapi(pkg.metadata['EAPI']):
+ if lc.check_eapi(pkg.eapi):
ignore = lc.ignore_line
if not ignore or not ignore.match(line):
e = lc.check(num, line)