]> rtime.felk.cvut.cz Git - coffee/buildroot.git/blob - utils/checkpackagelib/lib_mk.py
check-package: fix code style
[coffee/buildroot.git] / utils / checkpackagelib / lib_mk.py
1 # See utils/checkpackagelib/readme.txt before editing this file.
2 # There are already dependency checks during the build, so below check
3 # functions don't need to check for things already checked by exploring the
4 # menu options using "make menuconfig" and by running "make" with appropriate
5 # packages enabled.
6
7 import re
8
9 from base import _CheckFunction
10 from lib import ConsecutiveEmptyLines  # noqa: F401
11 from lib import EmptyLastLine          # noqa: F401
12 from lib import NewlineAtEof           # noqa: F401
13 from lib import TrailingSpace          # noqa: F401
14
15
16 class Indent(_CheckFunction):
17     COMMENT = re.compile("^\s*#")
18     CONDITIONAL = re.compile("^\s*(ifeq|ifneq|endif)\s")
19     ENDS_WITH_BACKSLASH = re.compile(r"^[^#].*\\$")
20     END_DEFINE = re.compile("^\s*endef\s")
21     MAKEFILE_TARGET = re.compile("^[^# \t]+:\s")
22     START_DEFINE = re.compile("^\s*define\s")
23
24     def before(self):
25         self.define = False
26         self.backslash = False
27         self.makefile_target = False
28
29     def check_line(self, lineno, text):
30         if self.START_DEFINE.search(text):
31             self.define = True
32             return
33         if self.END_DEFINE.search(text):
34             self.define = False
35             return
36
37         expect_tabs = False
38         if self.define or self.backslash or self.makefile_target:
39             expect_tabs = True
40         if self.CONDITIONAL.search(text):
41             expect_tabs = False
42
43         # calculate for next line
44         if self.ENDS_WITH_BACKSLASH.search(text):
45             self.backslash = True
46         else:
47             self.backslash = False
48
49         if self.MAKEFILE_TARGET.search(text):
50             self.makefile_target = True
51             return
52         if text.strip() == "":
53             self.makefile_target = False
54             return
55
56         # comment can be indented or not inside define ... endef, so ignore it
57         if self.define and self.COMMENT.search(text):
58             return
59
60         if expect_tabs:
61             if not text.startswith("\t"):
62                 return ["{}:{}: expected indent with tabs"
63                         .format(self.filename, lineno),
64                         text]
65         else:
66             if text.startswith("\t"):
67                 return ["{}:{}: unexpected indent with tabs"
68                         .format(self.filename, lineno),
69                         text]
70
71
72 class PackageHeader(_CheckFunction):
73     def before(self):
74         self.skip = False
75
76     def check_line(self, lineno, text):
77         if self.skip or lineno > 6:
78             return
79
80         if lineno in [1, 5]:
81             if lineno == 1 and text.startswith("include "):
82                 self.skip = True
83                 return
84             if text.rstrip() != "#" * 80:
85                 return ["{}:{}: should be 80 hashes ({}#writing-rules-mk)"
86                         .format(self.filename, lineno, self.url_to_manual),
87                         text,
88                         "#" * 80]
89         elif lineno in [2, 4]:
90             if text.rstrip() != "#":
91                 return ["{}:{}: should be 1 hash ({}#writing-rules-mk)"
92                         .format(self.filename, lineno, self.url_to_manual),
93                         text]
94         elif lineno == 6:
95             if text.rstrip() != "":
96                 return ["{}:{}: should be a blank line ({}#writing-rules-mk)"
97                         .format(self.filename, lineno, self.url_to_manual),
98                         text]
99
100
101 class RemoveDefaultPackageSourceVariable(_CheckFunction):
102     packages_that_may_contain_default_source = ["binutils", "gcc", "gdb"]
103     PACKAGE_NAME = re.compile("/([^/]+)\.mk")
104
105     def before(self):
106         package = self.PACKAGE_NAME.search(self.filename).group(1)
107         package_upper = package.replace("-", "_").upper()
108         self.package = package
109         self.FIND_SOURCE = re.compile(
110             "^{}_SOURCE\s*=\s*{}-\$\({}_VERSION\)\.tar\.gz"
111             .format(package_upper, package, package_upper))
112
113     def check_line(self, lineno, text):
114         if self.FIND_SOURCE.search(text):
115
116             if self.package in self.packages_that_may_contain_default_source:
117                 return
118
119             return ["{}:{}: remove default value of _SOURCE variable "
120                     "({}#generic-package-reference)"
121                     .format(self.filename, lineno, self.url_to_manual),
122                     text]
123
124
125 class SpaceBeforeBackslash(_CheckFunction):
126     TAB_OR_MULTIPLE_SPACES_BEFORE_BACKSLASH = re.compile(r"^.*(  |\t)\\$")
127
128     def check_line(self, lineno, text):
129         if self.TAB_OR_MULTIPLE_SPACES_BEFORE_BACKSLASH.match(text.rstrip()):
130             return ["{}:{}: use only one space before backslash"
131                     .format(self.filename, lineno),
132                     text]
133
134
135 class TrailingBackslash(_CheckFunction):
136     ENDS_WITH_BACKSLASH = re.compile(r"^[^#].*\\$")
137
138     def before(self):
139         self.backslash = False
140
141     def check_line(self, lineno, text):
142         last_line_ends_in_backslash = self.backslash
143
144         # calculate for next line
145         if self.ENDS_WITH_BACKSLASH.search(text):
146             self.backslash = True
147             self.lastline = text
148             return
149         self.backslash = False
150
151         if last_line_ends_in_backslash and text.strip() == "":
152             return ["{}:{}: remove trailing backslash"
153                     .format(self.filename, lineno - 1),
154                     self.lastline]
155
156
157 class TypoInPackageVariable(_CheckFunction):
158     ALLOWED = re.compile("|".join([
159         "ACLOCAL_DIR",
160         "ACLOCAL_HOST_DIR",
161         "BR_CCACHE_INITIAL_SETUP",
162         "BR_NO_CHECK_HASH_FOR",
163         "LINUX_POST_PATCH_HOOKS",
164         "LINUX_TOOLS",
165         "LUA_RUN",
166         "MKFS_JFFS2",
167         "MKIMAGE_ARCH",
168         "PKG_CONFIG_HOST_BINARY",
169         "TARGET_FINALIZE_HOOKS",
170         "XTENSA_CORE_NAME"]))
171     PACKAGE_NAME = re.compile("/([^/]+)\.mk")
172     VARIABLE = re.compile("^([A-Z0-9_]+_[A-Z0-9_]+)\s*(\+|)=")
173
174     def before(self):
175         package = self.PACKAGE_NAME.search(self.filename).group(1)
176         package = package.replace("-", "_").upper()
177         # linux tools do not use LINUX_TOOL_ prefix for variables
178         package = package.replace("LINUX_TOOL_", "")
179         self.package = package
180         self.REGEX = re.compile("^(HOST_)?({}_[A-Z0-9_]+)".format(package))
181         self.FIND_VIRTUAL = re.compile(
182             "^{}_PROVIDES\s*(\+|)=\s*(.*)".format(package))
183         self.virtual = []
184
185     def check_line(self, lineno, text):
186         m = self.VARIABLE.search(text)
187         if m is None:
188             return
189
190         variable = m.group(1)
191
192         # allow to set variables for virtual package this package provides
193         v = self.FIND_VIRTUAL.search(text)
194         if v:
195             self.virtual += v.group(2).upper().split()
196             return
197         for virtual in self.virtual:
198             if variable.startswith("{}_".format(virtual)):
199                 return
200
201         if self.ALLOWED.match(variable):
202             return
203         if self.REGEX.search(text) is None:
204             return ["{}:{}: possible typo: {} -> *{}*"
205                     .format(self.filename, lineno, variable, self.package),
206                     text]
207
208
209 class UselessFlag(_CheckFunction):
210     DEFAULT_AUTOTOOLS_FLAG = re.compile("^.*{}".format("|".join([
211         "_AUTORECONF\s*=\s*NO",
212         "_LIBTOOL_PATCH\s*=\s*YES"])))
213     DEFAULT_GENERIC_FLAG = re.compile("^.*{}".format("|".join([
214         "_INSTALL_IMAGES\s*=\s*NO",
215         "_INSTALL_REDISTRIBUTE\s*=\s*YES",
216         "_INSTALL_STAGING\s*=\s*NO",
217         "_INSTALL_TARGET\s*=\s*YES"])))
218     END_CONDITIONAL = re.compile("^\s*(endif)")
219     START_CONDITIONAL = re.compile("^\s*(ifeq|ifneq)")
220
221     def before(self):
222         self.conditional = 0
223
224     def check_line(self, lineno, text):
225         if self.START_CONDITIONAL.search(text):
226             self.conditional += 1
227             return
228         if self.END_CONDITIONAL.search(text):
229             self.conditional -= 1
230             return
231
232         # allow non-default conditionally overridden by default
233         if self.conditional > 0:
234             return
235
236         if self.DEFAULT_GENERIC_FLAG.search(text):
237             return ["{}:{}: useless default value ({}#"
238                     "_infrastructure_for_packages_with_specific_build_systems)"
239                     .format(self.filename, lineno, self.url_to_manual),
240                     text]
241
242         if self.DEFAULT_AUTOTOOLS_FLAG.search(text) and not text.lstrip().startswith("HOST_"):
243             return ["{}:{}: useless default value "
244                     "({}#_infrastructure_for_autotools_based_packages)"
245                     .format(self.filename, lineno, self.url_to_manual),
246                     text]