aboutsummaryrefslogtreecommitdiff
blob: 4e3d6f0b4792d6cbcb38cc5cee51d45b05d01a97 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import logging
import re


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
	"""True if lines containing nothing more than comments with optional
	leading whitespace should be ignored"""
	ignore_comment = True

	def __init__(self, errors):
		self.errors = errors

	def new(self, pkg):
		pass

	def check_eapi(self, eapi):
		"""Returns if check should be run in the given EAPI (default: True)"""
		return True

	def check(self, num, line):
		"""Run the check on line and return error if there is one"""
		if self.re.match(line):
			return self.errors[self.error]

	def end(self):
		pass


class InheritEclass(LineCheck):
	"""
	Base class for checking for missing inherits, as well as excess inherits.

	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.
	"""

	def __init__(
		self, eclass, eclass_eapi_functions, errors, 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
		self.errors = errors
		inherit_re = eclass
		self._eclass_eapi_functions = eclass_eapi_functions
		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.
		logging.debug("InheritEclass, eclass: %s, funcs: %s", eclass, funcs)
		self._func_re = re.compile(
			r'(^|[|&{(])\s*(\w+=.*)?\b(' + r'|'.join(funcs) + r')\b')

	def new(self, pkg):
		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 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 = self._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 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