blob: 7082a5d02a1f67142707d879231b9c9ce8fadb5f [file] [log] [blame]
import logging
import operator
import os
import re
from repoman.modules.linechecks.base import InheritEclass
from repoman.modules.linechecks.config import LineChecksConfig
from repoman._portage import portage
# Avoid a circular import issue in py2.7
portage.proxy.lazyimport.lazyimport(globals(),
'portage.module:Modules',
)
MODULES_PATH = os.path.dirname(__file__)
# initial development debug info
logging.debug("LineChecks module path: %s", MODULES_PATH)
class LineCheckController(object):
'''Initializes and runs the LineCheck checks'''
def __init__(self, repo_settings, linechecks):
'''Class init
@param repo_settings: RepoSettings instance
'''
self.repo_settings = repo_settings
self.linechecks = linechecks
self.config = LineChecksConfig(repo_settings)
self.controller = Modules(path=MODULES_PATH, namepath="repoman.modules.linechecks")
logging.debug("LineCheckController; module_names: %s", self.controller.module_names)
self._constant_checks = None
self._here_doc_re = re.compile(r'.*<<[-]?(\w+)\s*(>\s*\S+\s*)?$')
self._ignore_comment_re = re.compile(r'^\s*#')
self._continuation_re = re.compile(r'(\\)*$')
def checks_init(self, experimental_inherit=False):
'''Initialize the main variables
@param experimental_inherit boolean
'''
if not experimental_inherit:
# Emulate the old eprefixify.defined and inherit.autotools checks.
self._eclass_info = self.config.eclass_info
else:
self._eclass_info = self.config.eclass_info_experimental_inherit
self._constant_checks = []
logging.debug("LineCheckController; modules: %s", self.linechecks)
# Add in the pluggable modules
for mod in self.linechecks:
mod_class = self.controller.get_class(mod)
logging.debug("LineCheckController; module_name: %s, class: %s", mod, mod_class.__name__)
self._constant_checks.append(mod_class(self.config.errors))
# Add in the InheritEclass checks
logging.debug("LineCheckController; eclass_info.items(): %s", list(self.config.eclass_info))
for k, kwargs in self.config.eclass_info.items():
logging.debug("LineCheckController; k: %s, kwargs: %s", k, kwargs)
self._constant_checks.append(
InheritEclass(
k,
self.config.eclass_eapi_functions,
self.config.errors,
**kwargs
)
)
def run_checks(self, contents, pkg):
'''Run the configured linechecks
@param contents: the ebjuild contents to check
@param pkg: the package being checked
'''
if self._constant_checks is None:
self.checks_init()
checks = self._constant_checks
here_doc_delim = None
multiline = None
for lc in checks:
lc.new(pkg)
multinum = 0
for num, line in enumerate(contents):
# Check if we're inside a here-document.
if here_doc_delim is not None:
if here_doc_delim.match(line):
here_doc_delim = None
if here_doc_delim is None:
here_doc = self._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
# A line ending with an even number of backslashes does not count,
# because the last backslash is escaped. Therefore, search for an
# odd number of backslashes.
line_escaped = operator.sub(*self._continuation_re.search(line).span()) % 2 == 1
if multiline:
# Chop off the \ and \n bytes from the previous line.
multiline = multiline[:-2] + line
if not line_escaped:
line = multiline
num = multinum
multiline = None
else:
continue
else:
if line_escaped:
multinum = num
multiline = line
continue
if not line.endswith("#nowarn\n"):
# Finally we have a full line to parse.
is_comment = self._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.eapi):
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)
for lc in checks:
i = lc.end()
if i is not None:
for e in i:
yield lc.repoman_check_name, e