def emaint_main(myargv):
+ # Similar to emerge, emaint needs a default umask so that created
+ # files (such as the world file) have sane permissions.
+ os.umask(022)
+
# TODO: Create a system that allows external modules to be added without
# the need for hard coding.
modules = {
diefunc() {
local funcname="$1" lineno="$2" exitcode="$3"
shift 3
-
+ if [ -n "${QA_INTERCEPTORS}" ] ; then
+ # die was called from inside inherit. We need to clean up
+ # QA_INTERCEPTORS since sed is called below.
+ unset -f ${QA_INTERCEPTORS}
+ unset QA_INTERCEPTORS
+ fi
local n filespacing=0 linespacing=0
# setup spacing to make output easier to read
for ((n = ${#FUNCNAME[@]} - 1; n >= 0; --n)); do
from fileinput import input
from grp import getgrnam
from itertools import izip
-from stat import S_ISDIR, ST_CTIME, ST_GID, ST_MTIME
+from stat import S_ISDIR, ST_CTIME
try:
import cPickle as pickle
del os.environ["PORTAGE_LEGACY_GLOBALS"]
try:
- from repoman.checks import EbuildWhitespace, EbuildHeader, EbuildQuote, \
- EbuildAssignment, EbuildNestedDie, EbuildUselessDodoc, EbuildUselessCdS
+ from repoman.checks import run_checks
except ImportError:
from os import path as osp
sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), 'pym'))
- from repoman.checks import EbuildWhitespace, EbuildHeader, EbuildQuote, \
- EbuildAssignment, EbuildNestedDie, EbuildUselessDodoc, EbuildUselessCdS
+ from repoman.checks import run_checks
import portage.checksum
import portage.const
for mybad in mybadrestrict:
fails["RESTRICT.invalid"].append(x+"/"+y+".ebuild: %s" % mybad)
# Syntax Checks
- path = checkdir + '/' + y + '.ebuild'
- myear = time.gmtime(os.stat(path)[ST_MTIME])[0]
- f = open(path, 'rb')
+ relative_path = os.path.join(x, y + ".ebuild")
+ full_path = os.path.join(repodir, relative_path)
+ f = open(full_path, 'rb')
try:
- contents = f.readlines()
+ for check_name, e in run_checks(f, os.stat(full_path).st_mtime):
+ stats[check_name] += 1
+ fails[check_name].append(relative_path + ': %s' % e)
finally:
f.close()
del f
- for check in (EbuildWhitespace, EbuildQuote,
- EbuildAssignment, EbuildUselessDodoc, EbuildUselessCdS):
- c = check(contents)
- errors = c.Run()
- for e in errors:
- stats[c.repoman_check_name] += 1
- fails[c.repoman_check_name].append(x + '/' + y + '.ebuild: %s' % e[1] % e[0])
- del check
- check = EbuildHeader(contents, str(myear))
- errors = check.Run()
- for e in errors:
- stats[check.repoman_check_name] += 1
- fails[check.repoman_check_name].append(x + '/' + y + '.ebuild: %s' % e[1] % e[0])
- del check
- check = EbuildNestedDie(contents)
- errors = check.Run()
- for e in errors:
- stats[check.repoman_check_name] += 1
- fails[check.repoman_check_name].append(
- x + '/' + y + '.ebuild: %s' % e[1] % e[0])
- del check, errors, path, contents, myear
if options.force:
# The dep_check() calls are the most expensive QA test. If --force
class Package(object):
__slots__ = ("__weakref__", "built", "cpv", "depth",
"installed", "metadata", "root", "onlydeps", "type_name",
- "_digraph_node", "_slot_atom")
+ "slot_atom",
+ "digraph_node", "__eq__", "__hash__", "__str__",
+ "__len__", "__getitem__", "__iter__", "__contains__")
def __init__(self, **kwargs):
for myattr in self.__slots__:
if myattr == "__weakref__":
myvalue = kwargs.get(myattr, None)
setattr(self, myattr, myvalue)
- @property
- def slot_atom(self):
- if self._slot_atom is None:
- self._slot_atom = "%s:%s" % \
- (portage.cpv_getkey(self.cpv), self.metadata["SLOT"])
- return self._slot_atom
-
- @property
- def digraph_node(self):
- if self._digraph_node is None:
- status = "merge"
- if self.onlydeps or self.installed:
- status = "nomerge"
- self._digraph_node = (self.type_name, self.root, self.cpv, status)
- return self._digraph_node
+ self.slot_atom = "%s:%s" % \
+ (portage.cpv_getkey(self.cpv), self.metadata["SLOT"])
+
+ status = "merge"
+ if self.onlydeps or self.installed:
+ status = "nomerge"
+ node = (self.type_name, self.root, self.cpv, status)
+ self.digraph_node = node
+ self.__eq__ = node.__eq__
+ self.__hash__ = node.__hash__
+ self.__str__ = node.__str__
+ self.__len__ = node.__len__
+ self.__getitem__ = node.__getitem__
+ self.__iter__ = node.__iter__
+ self.__contains__ = node.__contains__
class DependencyArg(object):
def __init__(self, arg=None, root_config=None):
self.arg = arg
self.root_config = root_config
+ def __str__(self):
+ return self.arg
+
class AtomArg(DependencyArg):
def __init__(self, atom=None, **kwargs):
DependencyArg.__init__(self, **kwargs)
self.atom = atom
+ self.set = (self.atom, )
class PackageArg(DependencyArg):
def __init__(self, package=None, **kwargs):
DependencyArg.__init__(self, **kwargs)
self.package = package
self.atom = "=" + package.cpv
+ self.set = (self.atom, )
class SetArg(DependencyArg):
def __init__(self, set=None, **kwargs):
myvalue = kwargs.get(myattr, None)
setattr(self, myattr, myvalue)
+ if self.priority is None:
+ self.priority = DepPriority()
+ if self.depth is None:
+ self.depth = 0
+
class BlockerCache(DictMixin):
"""This caches blockers of installed packages so that dep_check does not
have to be done for every single installed package on every invocation of
# contains all atoms from all sets added to the graph, including
# atoms given as arguments
self._set_atoms = InternalPackageSet()
+ self._atom_arg_map = {}
# contains all nodes pulled in by self._set_atoms
self._set_nodes = set()
self.blocker_digraph = digraph()
myarg = None
if dep.root == self.target_root:
try:
- myarg = self._set_atoms.findAtomForPackage(
- dep_pkg.cpv, dep_pkg.metadata)
+ myarg = self._get_arg_for_pkg(dep_pkg)
except portage.exception.InvalidDependString:
if not dep_pkg.installed:
# This shouldn't happen since the package
return 0
return 1
- def create(self, *args, **kwargs):
- if not self._add_pkg(*args, **kwargs):
- return 0
- return self._create_graph()
-
def _add_pkg(self, pkg, myparent=None,
priority=None, arg=None, depth=0):
- if myparent is not None:
- myparent = myparent.digraph_node
"""
Fills the digraph with nodes comprised of packages to merge.
mybigkey is the package spec of the package to merge.
if not arg and myroot == self.target_root:
try:
- arg = self._set_atoms.findAtomForPackage(mykey, metadata)
+ arg = self._get_arg_for_pkg(pkg)
except portage.exception.InvalidDependString, e:
if mytype != "installed":
show_invalid_depstring_notice(tuple(mybigkey+["merge"]),
# dependencies.
self._filtered_trees[root]["atoms"].clear()
- def _get_parent_sets(self, root, atom):
- refs = []
- for set_name, atom_set in self._sets.iteritems():
- if set_name in refs:
- continue
- if atom in atom_set:
- refs.append(set_name)
- if len(refs) > 1 and "args" in refs:
- refs.remove("args")
- return refs
+ def _get_arg_for_pkg(self, pkg):
+ """
+ Return a matching DependencyArg instance for the given Package if
+ any exist, otherwise None. An attempt will be made to return the most
+ specific match (PackageArg type is the most specific).
+
+ This will raise an InvalidDependString exception if PROVIDE is invalid.
+ """
+ # TODO: add multiple $ROOT support
+ if pkg.root != self.target_root:
+ return None
+ atom_arg_map = self._atom_arg_map
+ any_arg = None
+ for atom in self._set_atoms.iterAtomsForPackage(pkg):
+ refs = atom_arg_map[(atom, pkg.root)]
+ for arg in refs:
+ if isinstance(arg, PackageArg):
+ # TODO: Implement a better comparison to ensure that
+ # these two packages really are identical.
+ if arg.package.type_name != pkg.type_name:
+ continue
+ return arg
+ any_arg = arg
+ return any_arg
def select_files(self, myfiles):
"""Given a list of .tbz2s, .ebuilds sets, and deps, create the
# Create the "args" package set from atoms and
# packages given as arguments.
args_set = self._sets["args"]
- expanded_args = []
for arg in args:
- if isinstance(arg, SetArg):
- for atom in arg.set:
- self._set_atoms.add(atom)
- expanded_args.append(AtomArg(arg=arg.arg, atom=atom,
- root_config=root_config))
+ if not isinstance(arg, (AtomArg, PackageArg)):
continue
- expanded_args.append(arg)
myatom = arg.atom
if myatom in args_set:
continue
args_set.add(myatom)
- self._set_atoms.add(myatom)
if not oneshot:
myfavorites.append(myatom)
- args = expanded_args
- del expanded_args
- pprovideddict = pkgsettings.pprovideddict
+ self._set_atoms.update(chain(*self._sets.itervalues()))
+ atom_arg_map = self._atom_arg_map
for arg in args:
- atom = arg.atom
+ for atom in arg.set:
+ atom_key = (atom, myroot)
+ refs = atom_arg_map.get(atom_key)
+ if refs is None:
+ refs = []
+ atom_arg_map[atom_key] = refs
+ if arg not in refs:
+ refs.append(arg)
+ pprovideddict = pkgsettings.pprovideddict
+ # Order needs to be preserved since a feature of --nodeps
+ # is to allow the user to force a specific merge order.
+ args.reverse()
+ while args:
+ arg = args.pop()
+ for atom in arg.set:
try:
pprovided = pprovideddict.get(portage.dep_getkey(atom))
if pprovided and portage.match_from_list(atom, pprovided):
# A provided package has been specified on the command line.
- self._pprovided_args.append((arg.arg, arg.atom))
+ self._pprovided_args.append((arg, atom))
continue
if isinstance(arg, PackageArg):
- if not self.create(arg.package, arg=arg.arg):
+ if not self._add_pkg(arg.package, arg=arg) or \
+ not self._create_graph():
+ sys.stderr.write(("\n\n!!! Problem resolving " + \
+ "dependencies for %s\n") % arg.arg)
return 0, myfavorites
continue
- self._populate_filtered_repo(myroot, atom)
pkg, existing_node = self._select_package(
myroot, atom, onlydeps=onlydeps)
if not pkg:
- refs = self._get_parent_sets(myroot, atom)
- if len(refs) == 1 and "args" in refs:
+ if not (isinstance(arg, SetArg) and \
+ arg.name in ("system", "world")):
self._show_unsatisfied_dep(myroot, atom)
return 0, myfavorites
- self._missing_args.append((arg.arg, arg.atom))
+ self._missing_args.append((arg, atom))
continue
- if not self.create(pkg):
- sys.stderr.write(("\n\n!!! Problem resolving " + \
- "dependencies for %s\n") % atom)
+ self._dep_stack.append(Dependency(atom=atom, root=myroot))
+ if not self._create_graph():
+ if isinstance(arg, SetArg):
+ sys.stderr.write(("\n\n!!! Problem resolving " + \
+ "dependencies for %s from %s\n") % \
+ (atom, arg.arg))
+ else:
+ sys.stderr.write(("\n\n!!! Problem resolving " + \
+ "dependencies for %s\n") % atom)
return 0, myfavorites
except portage.exception.MissingSignature, e:
portage.writemsg("\n\n!!! A missing gpg signature is preventing portage from calculating the\n")
myarg = None
if root == self.target_root:
try:
- myarg = self._set_atoms.findAtomForPackage(
- cpv, metadata)
+ myarg = self._get_arg_for_pkg(
+ Package(type_name=pkg_type, root=root,
+ cpv=cpv, metadata=metadata,
+ built=built, installed=installed,
+ onlydeps=onlydeps))
except portage.exception.InvalidDependString:
if not installed:
# masked by corruption
world_problems = False
if "world" in self._sets:
for arg, atom in self._missing_args:
- if "world" in self._get_parent_sets(
- self.target_root, atom):
+ if arg.name == "world":
world_problems = True
break
" Ebuilds for the following packages are either all\n")
sys.stderr.write(colorize("BAD", "!!!") + \
" masked or don't exist:\n")
- sys.stderr.write(" ".join(arg[1] for arg in \
+ sys.stderr.write(" ".join(atom for arg, atom in \
self._missing_args) + "\n")
if self._pprovided_args:
arg_refs = {}
- for arg_atom in self._pprovided_args:
- arg, atom = arg_atom
+ for arg, atom in self._pprovided_args:
+ if isinstance(arg, SetArg):
+ parent = arg.name
+ arg_atom = (atom, atom)
+ else:
+ parent = "args"
+ arg_atom = (arg.arg, atom)
refs = arg_refs.setdefault(arg_atom, [])
- refs.extend(self._get_parent_sets(self.target_root, atom))
+ if parent not in refs:
+ refs.append(parent)
msg = []
msg.append(bad("\nWARNING: "))
if len(self._pprovided_args) > 1:
def clone(self):
clone = digraph()
- clone.nodes = copy.deepcopy(self.nodes)
+ clone.nodes = {}
+ for k, v in self.nodes.iteritems():
+ clone.nodes[k] = (v[0].copy(), v[1].copy())
clone.order = self.order[:]
return clone
# Distributed under the terms of the GNU General Public License v2
# $Id$
-from portage import flatten
+from portage import cpv_getkey, flatten
from portage.dep import isvalidatom, match_from_list, \
best_match_to_list, dep_getkey, use_reduce, paren_reduce
from portage.exception import InvalidAtom
return atoms[transformed_atoms.index(best_match)]
return None
+ def iterAtomsForPackage(self, pkg):
+ """
+ Find all matching atoms for a given package. This matches virtual
+ arguments against the PROVIDE metadata. This will raise an
+ InvalidDependString exception if PROVIDE is invalid.
+ """
+ cpv_slot_list = ["%s:%s" % (pkg.cpv, pkg.metadata["SLOT"])]
+ cp = cpv_getkey(pkg.cpv)
+ self._load() # make sure the atoms are loaded
+ atoms = self._atommap.get(cp)
+ if atoms:
+ for atom in atoms:
+ if match_from_list(atom, cpv_slot_list):
+ yield atom
+ if not pkg.metadata["PROVIDE"]:
+ return
+ provides = flatten(use_reduce(paren_reduce(pkg.metadata["PROVIDE"]),
+ uselist=pkg.metadata["USE"].split()))
+ for provide in provides:
+ provided_cp = dep_getkey(provide)
+ atoms = self._atommap.get(provided_cp)
+ if atoms:
+ for atom in atoms:
+ if match_from_list(atom.replace(provided_cp, cp),
+ cpv_slot_list):
+ yield atom
+
class EditablePackageSet(PackageSet):
def update(self, atoms):
#this doesn't match the package we're unmerging; keep it.
newworldlist.append(x)
+ newworldlist.extend(self._nonatoms)
self.replace(newworldlist)
self.unlock()
LEADING_SPACES_ERROR, READONLY_ASSIGNMENT_ERROR, TRAILING_WHITESPACE_ERROR, \
MISSING_QUOTES_ERROR, NESTED_DIE_ERROR, REDUNDANT_CD_S_ERROR
+class LineCheck(object):
+ """Run a check on a line of an ebuild."""
+ """A regular expression to determine whether to ignore the line"""
+ ignore_line = False
-class ContentCheckException(Exception):
- """Parent class for exceptions relating to invalid content"""
-
- def __init__(self, str):
- Exception.__init__(self, str)
-
-
-class ContentCheck(object):
- """Given a file-like object, run checks over it's content
-
- Args:
- contents - A file-like object, preferably a StringIO instance
-
- Raises:
- ContentCheckException or a subclass
- """
- repoman_check_name = None
-
- def __init__(self, contents):
- self.contents = contents
- pass
-
- def Run(self):
- """Run the check against the contents, return a sequence of errors
- of the form ((line, error),...)
- """
+ def check(self, num, line):
+ """Run the check on line and return error if there is one"""
pass
-
-class EbuildHeader(ContentCheck):
+class EbuildHeader(LineCheck):
"""Ensure ebuilds have proper headers
+ Copyright header errors
+ CVS header errors
+ License header errors
Args:
modification_year - Year the ebuild was last modified
gentoo_license = r'# Distributed under the terms of the GNU General Public License v2'
cvs_header = re.compile(r'^#\s*\$Header.*\$$')
- def __init__(self, contents, modification_year):
- ContentCheck.__init__(self, contents)
- self.modification_year = modification_year
+ def __init__(self, st_mtime):
+ self.modification_year = str(time.gmtime(st_mtime)[0])
self.gentoo_copyright_re = re.compile(self.gentoo_copyright % self.modification_year)
- def Run(self):
- """Locate simple header mistakes in an ebuild
- Copyright header errors
- CVS header errors
- License header errors
- """
- errors = []
- for num, line in enumerate(self.contents):
- if num == 0:
- match = self.gentoo_copyright_re.match(line)
- if not match:
- errors.append((num + 1, COPYRIGHT_ERROR))
- if num == 1 and line.strip() != self.gentoo_license:
- errors.append((num + 1, LICENSE_ERROR))
- if num == 2:
- match = self.cvs_header.match(line)
- if not match:
- errors.append((num + 1, CVS_HEADER_ERROR))
- if num > 2:
- return errors
- return errors
+ def check(self, num, line):
+ if num > 2:
+ return
+ elif num == 0:
+ if not self.gentoo_copyright_re.match(line):
+ return COPYRIGHT_ERROR
+ elif num == 1 and line.strip() != self.gentoo_license:
+ return LICENSE_ERROR
+ elif num == 2:
+ if not self.cvs_header.match(line):
+ return CVS_HEADER_ERROR
-class EbuildWhitespace(ContentCheck):
+class EbuildWhitespace(LineCheck):
"""Ensure ebuilds have proper whitespacing"""
repoman_check_name = 'ebuild.minorsyn'
leading_spaces = re.compile(r'^[\S\t]')
trailing_whitespace = re.compile(r'.*([\S]$)')
- def __init__(self, contents):
- ContentCheck.__init__(self, contents)
-
- def Run(self):
- """Locate simple whitespace errors
- Lines with leading spaces or trailing whitespace
- """
- errors = []
- for num, line in enumerate(self.contents):
- match = self.ignore_line.match(line)
- if match:
- continue
- match = self.leading_spaces.match(line)
- if not match:
- errors.append((num + 1, LEADING_SPACES_ERROR))
- match = self.trailing_whitespace.match(line)
- if not match:
- errors.append((num + 1, TRAILING_WHITESPACE_ERROR))
- return errors
-
+ def check(self, num, line):
+ if not self.leading_spaces.match(line):
+ return LEADING_SPACES_ERROR
+ if not self.trailing_whitespace.match(line):
+ return TRAILING_WHITESPACE_ERROR
-class EbuildQuote(ContentCheck):
+class EbuildQuote(LineCheck):
"""Ensure ebuilds have valid quoting around things like D,FILESDIR, etc..."""
repoman_check_name = 'ebuild.minorsyn'
var_names = r'(D|S|T|ROOT|FILESDIR|WORKDIR)'
var_reference = re.compile(r'\$(\{'+var_names+'\}|' + \
var_names + '\W)')
- missing_quotes = re.compile(r'(\s|^)[^"\s]*\$\{?' + var_names + \
- r'\}?[^"\s]*(\s|$)')
+ missing_quotes = re.compile(r'(\s|^)[^"\'\s]*\$\{?' + var_names + \
+ r'\}?[^"\'\s]*(\s|$)')
cond_begin = re.compile(r'(^|\s+)\[\[($|\\$|\s+)')
cond_end = re.compile(r'(^|\s+)\]\]($|\\$|\s+)')
- def __init__(self, contents):
- ContentCheck.__init__(self, contents)
-
- def Run(self):
- """Locate simple errors in ebuilds:
- Missing quotes around variables that may contain spaces
- """
+ def check(self, num, line):
+ if self.var_reference.search(line) is None:
+ return
+ # There can be multiple matches / violations on a single line. We
+ # have to make sure none of the matches are violators. Once we've
+ # found one violator, any remaining matches on the same line can
+ # be ignored.
+ pos = 0
+ while pos <= len(line) - 1:
+ missing_quotes = self.missing_quotes.search(line, pos)
+ if not missing_quotes:
+ break
+ # If the last character of the previous match is a whitespace
+ # character, that character may be needed for the next
+ # missing_quotes match, so search overlaps by 1 character.
+ group = missing_quotes.group()
+ pos = missing_quotes.end() - 1
+
+ # Filter out some false positives that can
+ # get through the missing_quotes regex.
+ if self.var_reference.search(group) is None:
+ continue
- errors = []
- for num, line in enumerate(self.contents):
- if self.ignore_line.match(line) is not None:
+ # This is an attempt to avoid false positives without getting
+ # too complex, while possibly allowing some (hopefully
+ # unlikely) violations to slip through. We just assume
+ # everything is correct if the there is a ' [[ ' or a ' ]] '
+ # anywhere in the whole line (possibly continued over one
+ # line).
+ if self.cond_begin.search(line) is not None:
continue
- if self.var_reference.search(line) is None:
+ if self.cond_end.search(line) is not None:
continue
- # There can be multiple matches / violations on a single line. We
- # have to make sure none of the matches are violators. Once we've
- # found one violator, any remaining matches on the same line can
- # be ignored.
- pos = 0
- while pos <= len(line) - 1:
- missing_quotes = self.missing_quotes.search(line, pos)
- if not missing_quotes:
- break
- # If the last character of the previous match is a whitespace
- # character, that character may be needed for the next
- # missing_quotes match, so search overlaps by 1 character.
- group = missing_quotes.group()
- pos = missing_quotes.end() - 1
-
- # Filter out some false positives that can
- # get through the missing_quotes regex.
- if self.var_reference.search(group) is None:
- continue
- # This is an attempt to avoid false positives without getting
- # too complex, while possibly allowing some (hopefully
- # unlikely) violations to slip through. We just assume
- # everything is correct if the there is a ' [[ ' or a ' ]] '
- # anywhere in the whole line (possibly continued over one
- # line).
- if self.cond_begin.search(line) is not None:
- continue
- if self.cond_end.search(line) is not None:
- continue
+ # Any remaining matches on the same line can be ignored.
+ return MISSING_QUOTES_ERROR
- errors.append((num + 1, MISSING_QUOTES_ERROR))
- # Any remaining matches on the same line can be ignored.
- break
- return errors
-
-
-class EbuildAssignment(ContentCheck):
+class EbuildAssignment(LineCheck):
"""Ensure ebuilds don't assign to readonly variables."""
repoman_check_name = 'variable.readonly'
line_continuation = re.compile(r'([^#]*\S)(\s+|\t)\\$')
ignore_line = re.compile(r'(^$)|(^(\t)*#)')
- def __init__(self, contents):
- ContentCheck.__init__(self, contents)
-
- def Run(self):
- """Locate simple errors in ebuilds:
- Assigning to read-only variables.
- """
+ def __init__(self):
+ self.previous_line = None
- errors = []
- previous_line = None
- # enumerate is 0 indexed, so add one when necesseary
- for num, line in enumerate(self.contents):
- match = self.ignore_line.match(line)
- if match:
- continue
- match = self.readonly_assignment.match(line)
- if match and (not previous_line or not self.line_continuation.match(previous_line)):
- errors.append((num + 1, READONLY_ASSIGNMENT_ERROR))
- previous_line = line
- return errors
+ 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)):
+ e = READONLY_ASSIGNMENT_ERROR
+ self.previous_line = line
+ return e
-class EbuildNestedDie(ContentCheck):
+class EbuildNestedDie(LineCheck):
"""Check ebuild for nested die statements (die statements in subshells"""
repoman_check_name = 'ebuild.nesteddie'
nesteddie_re = re.compile(r'^[^#]*\([^)]*\bdie\b')
- def __init__(self, contents):
- ContentCheck.__init__(self, contents)
-
- def Run(self):
- errors = []
- for num, line in enumerate(self.contents):
- match = self.nesteddie_re.match(line)
- if match:
- errors.append((num + 1, NESTED_DIE_ERROR))
- return errors
+ def check(self, num, line):
+ if self.nesteddie_re.match(line):
+ return NESTED_DIE_ERROR
-class EbuildUselessDodoc(ContentCheck):
+class EbuildUselessDodoc(LineCheck):
"""Check ebuild for useless files in dodoc arguments."""
repoman_check_name = 'ebuild.minorsyn'
uselessdodoc_re = re.compile(
r'^\s*dodoc(\s+|\s+.*\s+)(ABOUT-NLS|COPYING|LICENSE)($|\s)')
- def __init__(self, contents):
- ContentCheck.__init__(self, contents)
+ def check(self, num, line):
+ match = self.uselessdodoc_re.match(line)
+ if match:
+ return "Useless dodoc '%s'" % (match.group(2), ) + " on line: %d"
- def Run(self):
- errors = []
- uselessdodoc_re = self.uselessdodoc_re
- for num, line in enumerate(self.contents):
- match = uselessdodoc_re.match(line)
- if match:
- errors.append((num + 1, "Useless dodoc '%s'" % \
- (match.group(2), ) + " on line: %d"))
- return errors
-
-class EbuildUselessCdS(ContentCheck):
+class EbuildUselessCdS(LineCheck):
"""Check for redundant cd ${S} statements"""
repoman_check_name = 'ebuild.minorsyn'
method_re = re.compile(r'^\s*src_(compile|install|test)\s*\(\)')
cds_re = re.compile(r'^\s*cd\s+("\$(\{S\}|S)"|\$(\{S\}|S))\s')
- def __init__(self, contents):
- ContentCheck.__init__(self, contents)
-
- def Run(self):
- errors = []
- check_next_line = False
- for num, line in enumerate(self.contents):
- if check_next_line:
- check_next_line = False
- if self.cds_re.match(line):
- errors.append((num + 1, REDUNDANT_CD_S_ERROR))
- elif self.method_re.match(line):
- check_next_line = True
- return errors
+ def __init__(self):
+ self.check_next_line = False
+
+ def check(self, num, line):
+ if self.check_next_line:
+ self.check_next_line = False
+ if self.cds_re.match(line):
+ return REDUNDANT_CD_S_ERROR
+ elif self.method_re.match(line):
+ self.check_next_line = True
+
+def run_checks(contents, st_mtime):
+ checks = []
+ for c in (EbuildWhitespace, EbuildQuote, EbuildAssignment,
+ EbuildUselessDodoc, EbuildUselessCdS, EbuildNestedDie):
+ checks.append(c())
+ checks.append(EbuildHeader(st_mtime))
+ for num, line in enumerate(contents):
+ for lc in checks:
+ ignore = lc.ignore_line
+ if not ignore or not ignore.match(line):
+ e = lc.check(num, line)
+ if e:
+ yield lc.repoman_check_name, e % (num + 1)