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 |