blob: db8a3632d93e430f3f3b7f790fde0e760283ace9 [file] [log] [blame]
Patrick Williamsb48b7b42016-08-17 15:04:38 -05001#!/usr/bin/env python
2
3"""\
4Sanitize a bitbake file following the OpenEmbedded style guidelines,
5see http://openembedded.org/wiki/StyleGuide
6
7(C) 2006 Cyril Romain <cyril.romain@gmail.com>
8MIT license
9
10TODO:
11 - add the others OpenEmbedded variables commonly used:
12 - parse command arguments and print usage on misuse
13 . prevent giving more than one .bb file in arguments
14 - write result to a file
15 - backup the original .bb file
16 - make a diff and ask confirmation for patching ?
17 - do not use startswith only:
18 /!\ startswith('SOMETHING') is not taken into account due to the previous startswith('S').
19 - count rule breaks and displays them in the order frequence
20"""
21
22from __future__ import print_function
23import fileinput
24import string
25import re
26
27__author__ = "Cyril Romain <cyril.romain@gmail.com>"
28__version__ = "$Revision: 0.5 $"
29
30# The standard set of variables often found in .bb files in the preferred order
31OE_vars = [
32 'SUMMARY',
33 'DESCRIPTION',
34 'AUTHOR',
35 'HOMEPAGE',
36 'SECTION',
37 'LICENSE',
38 'LIC_FILES_CHKSUM',
39 'DEPENDS',
40 'PROVIDES',
41 'SRCREV',
42 'SRCDATE',
43 'PE',
44 'PV',
45 'PR',
46 'INC_PR',
47 'SRC_URI',
48 'S',
49 'GPE_TARBALL_SUFFIX',
50 'inherit',
51 'EXTRA_',
52 'export',
53 'do_fetch',
54 'do_unpack',
55 'do_patch',
56 'WORKDIR',
57 'acpaths',
58 'do_configure',
59 'do_compile',
60 'do_install',
61 'PACKAGES',
62 'PACKAGE_ARCH',
63 'RDEPENDS',
64 'RRECOMMENDS',
65 'RSUGGESTS',
66 'RPROVIDES',
67 'RCONFLICTS',
68 'FILES',
69 'do_package',
70 'do_stage',
71 'addhandler',
72 'addtask',
73 'bindir',
74 'headers',
75 'include',
76 'includedir',
77 'python',
78 'qtopiadir',
79 'pkg_preins',
80 'pkg_prerm',
81 'pkg_postins',
82 'pkg_postrm',
83 'require',
84 'sbindir',
85 'basesysconfdir',
86 'sysconfdir',
87 'ALLOW_EMPTY',
88 'ALTERNATIVE_NAME',
89 'ALTERNATIVE_PATH',
90 'ALTERNATIVE_LINK',
91 'ALTERNATIVE_PRIORITY',
92 'ALTNAME',
93 'AMD_DRIVER_LABEL',
94 'AMD_DRIVER_VERSION',
95 'ANGSTROM_EXTRA_INSTALL',
96 'APPDESKTOP',
97 'APPIMAGE',
98 'APPNAME',
99 'APPTYPE',
100 'APPWEB_BUILD',
101 'APPWEB_HOST',
102 'AR',
103 'ARCH',
104 'ARM_INSTRUCTION_SET',
105 'ARM_MUTEX',
106 'ART_CONFIG',
107 'B',
108 'BJAM_OPTS',
109 'BJAM_TOOLS',
110 'BONOBO_HEADERS',
111 'BOOTSCRIPTS',
112 'BROKEN',
113 'BUILD_CPPFLAGS',
114 'CFLAGS',
115 'CCFLAGS',
116 'CMDLINE',
117 'COLLIE_MEMORY_SIZE',
118 'COMPATIBLE_HOST',
119 'COMPATIBLE_MACHINE',
120 'COMPILE_HERMES',
121 'CONFFILES',
122 'CONFLICTS',
123 'CORE_EXTRA_D',
124 'CORE_IMAGE_EXTRA_INSTALL',
125 'CORE_PACKAGES_D',
126 'CORE_PACKAGES_RD',
127 'CPPFLAGS',
128 'CVSDATE',
129 'CXXFLAGS',
130 'DEBIAN_NOAUTONAME',
131 'DEBUG_APPS',
132 'DEFAULT_PREFERENCE',
133 'DB4_CONFIG',
134 'EXCLUDE_FROM_SHLIBS',
135 'EXCLUDE_FROM_WORLD',
136 'FIXEDSRCDATE',
137 'GLIBC_ADDONS',
138 'GLIBC_EXTRA_OECONF',
139 'GNOME_VFS_HEADERS',
140 'HEADERS',
141 'INHIBIT_DEFAULT_DEPS',
142 'INITSCRIPT_PACKAGES',
143 'INITSCRIPT_NAME',
144 'INITSCRIPT_PARAMS',
145 'INSANE_SKIP',
146 'PACKAGE_INSTALL',
147 'KERNEL_IMAGETYPE',
148 'KERNEL_IMAGEDEST',
149 'KERNEL_OUTPUT',
150 'KERNEL_RELEASE',
151 'KERNEL_PRIORITY',
152 'KERNEL_SOURCE',
153 'KERNEL_SUFFIX',
154 'KERNEL_VERSION',
155 'K_MAJOR',
156 'K_MICRO',
157 'K_MINOR',
158 'HHV',
159 'KV',
160 'LDFLAGS',
161 'LD',
162 'LD_SO',
163 'LDLIBS',
164 'LEAD_SONAME',
165 'LIBTOOL',
166 'LIBBDB_EXTRA',
167 'LIBV',
168 'MACHINE_ESSENTIAL_EXTRA_RDEPENDS',
169 'MACHINE_ESSENTIAL_EXTRA_RRECOMMENDS',
170 'MACHINE_EXTRA_RDEPENDS',
171 'MACHINE_EXTRA_RRECOMMENDS',
172 'MACHINE_FEATURES',
173 'MACHINE_TASKS',
174 'MACHINE',
175 'MACHTYPE',
176 'MAKE_TARGETS',
177 'MESSAGEUSER',
178 'MESSAGEHOME',
179 'MIRRORS',
180 'MUTEX',
181 'OE_QMAKE_INCDIR_QT',
182 'OE_QMAKE_CXXFLAGS',
183 'ORBIT_IDL_SRC',
184 'PARALLEL_MAKE',
185 'PAKCAGE_ARCH',
186 'PCMCIA_MANAGER',
187 'PKG_BASENAME',
188 'PKG',
189 'QEMU',
190 'QMAKE_PROFILES',
191 'QPEDIR',
192 'QPF_DESCRIPTION',
193 'QPF_PKGPATTERN',
194 'QT_CONFIG_FLAGS',
195 'QT_LIBRARY',
196 'ROOTFS_POSTPROCESS_COMMAND',
197 'RREPLACES',
198 'TARGET_CFLAGS',
199 'TARGET_CPPFLAGS',
200 'TARGET_LDFLAGS',
201 'UBOOT_MACHINE',
202 'UCLIBC_BASE',
203 'UCLIBC_PATCHES',
204 'USERADD_PACKAGES',
205 'USERADD_PARAM',
206 'VIRTUAL_NAME',
207 'XORG_PN',
208 'XSERVER',
209 'others'
210]
211
212varRegexp = r'^([a-zA-Z_0-9${}-]*)([ \t]*)([+.:]?=[+.]?)([ \t]*)([^\t]+)'
213routineRegexp = r'^([a-zA-Z0-9_ ${}-]+?)\('
214
215# Variables seen in the processed .bb
216seen_vars = {}
217for v in OE_vars:
218 seen_vars[v] = []
219
220# _Format guideline #0_:
221# No spaces are allowed at the beginning of lines that define a variable or
222# a do_ routine
223def respect_rule0(line):
224 return line.lstrip()==line
225def conformTo_rule0(line):
226 return line.lstrip()
227
228# _Format guideline #1_:
229# No spaces are allowed behind the line continuation symbol '\'
230def respect_rule1(line):
231 if line.rstrip().endswith('\\'):
232 return line.endswith('\\')
233 else:
234 return True
235def conformTo_rule1(line):
236 return line.rstrip()
237
238# _Format guideline #2_:
239# Tabs should not be used (use spaces instead).
240def respect_rule2(line):
241 return line.count('\t')==0
242def conformTo_rule2(line):
243 return line.expandtabs()
244
245# _Format guideline #3_:
246# Comments inside bb files are allowed using the '#' character at the
247# beginning of a line.
248def respect_rule3(line):
249 if line.lstrip().startswith('#'):
250 return line.startswith('#')
251 else:
252 return True
253def conformTo_rule3(line):
254 return line.lstrip()
255
256# _Format guideline #4_:
257# Use quotes on the right hand side of assignments FOO = "BAR"
258def respect_rule4(line):
259 r = re.search(varRegexp, line)
260 if r is not None:
261 r2 = re.search(r'("?)([^"\\]*)(["\\]?)', r.group(5))
262 # do not test for None it because always match
263 return r2.group(1)=='"' and r2.group(3)!=''
264 return False
265def conformTo_rule4(line):
266 r = re.search(varRegexp, line)
267 return ''.join([r.group(1), ' ', r.group(3), ' "', r.group(5), r.group(5).endswith('"') and '' or '"'])
268
269# _Format guideline #5_:
270# The correct spacing for a variable is FOO = "BAR".
271def respect_rule5(line):
272 r = re.search(varRegexp, line)
273 return r is not None and r.group(2)==" " and r.group(4)==" "
274def conformTo_rule5(line):
275 r = re.search(varRegexp, line)
276 return ''.join([r.group(1), ' ', r.group(3), ' ', r.group(5)])
277
278# _Format guideline #6_:
279# Don't use spaces or tabs on empty lines
280def respect_rule6(line):
281 return not line.isspace() or line=="\n"
282def conformTo_rule6(line):
283 return ""
284
285# _Format guideline #7_:
286# Indentation of multiline variables such as SRC_URI is desireable.
287def respect_rule7(line):
288 return True
289def conformTo_rule7(line):
290 return line
291
292rules = (
293 (respect_rule0, conformTo_rule0, "No spaces are allowed at the beginning of lines that define a variable or a do_ routine"),
294 (respect_rule1, conformTo_rule1, "No spaces are allowed behind the line continuation symbol '\\'"),
295 (respect_rule2, conformTo_rule2, "Tabs should not be used (use spaces instead)"),
296 (respect_rule3, conformTo_rule3, "Comments inside bb files are allowed using the '#' character at the beginning of a line"),
297 (respect_rule4, conformTo_rule4, "Use quotes on the right hand side of assignments FOO = \"BAR\""),
298 (respect_rule5, conformTo_rule5, "The correct spacing for a variable is FOO = \"BAR\""),
299 (respect_rule6, conformTo_rule6, "Don't use spaces or tabs on empty lines"),
300 (respect_rule7, conformTo_rule7, "Indentation of multiline variables such as SRC_URI is desireable"),
301)
302
303# Function to check that a line respects a rule. If not, it tries to conform
304# the line to the rule. Reminder or Disgression message are dump accordingly.
305def follow_rule(i, line):
306 oldline = line
307 # if the line does not respect the rule
308 if not rules[i][0](line):
309 # try to conform it to the rule
310 line = rules[i][1](line)
311 # if the line still does not respect the rule
312 if not rules[i][0](line):
313 # this is a rule disgression
314 print ("## Disgression: ", rules[i][2], " in: '", oldline, "'")
315 else:
316 # just remind user about his/her errors
317 print ("## Reminder: ", rules[i][2], " in : '", oldline, "'")
318 return line
319
320
321if __name__ == "__main__":
322
323 # -- retrieves the lines of the .bb file --
324 lines = []
325 for line in fileinput.input():
326 # use 'if True' to warn user about all the rule he/she breaks
327 # use 'if False' to conform to rules{2,1,6} without warnings
328 if True:
329 lines.append(line)
330 else:
331 # expandtabs on each line so that rule2 is always respected
332 # rstrip each line so that rule1 is always respected
333 line = line.expandtabs().rstrip()
334 # ignore empty lines (or line filled with spaces or tabs only)
335 # so that rule6 is always respected
336 if line is not '':
337 lines.append(line)
338
339 # -- parse the file --
340 var = ""
341 in_routine = False
342 commentBloc = []
343 olines = []
344 for line in lines:
345 originalLine = line
346 # rstrip line to remove line breaks characters
347 line = line.rstrip()
348 line = follow_rule(2, line)
349 line = follow_rule(1, line)
350 line = follow_rule(6, line)
351
352 # ignore empty lines
353 if line.isspace() or line is '':
354 # flush comments into the olines
355 for c in commentBloc: olines.append(c)
356 commentBloc = []
357 continue
358
359 if line.startswith('}'):
360 in_routine=False
361 keep = line.endswith('\\') or in_routine
362
363 # handles commented lines
364 if line.lstrip().startswith('#'):
365 # check and follow rule3 if not in a variables or routines
366 if not in_routine:
367 line = follow_rule(3, line)
368 commentBloc.append(line)
369 continue
370
371 if var in seen_vars:
372 for c in commentBloc: seen_vars[var].append(c)
373 commentBloc = []
374 seen_vars[var].append(line)
375 else:
376 for k in OE_vars:
377 if line.startswith(k):
378 var = k
379 break
380 if re.match(routineRegexp, line) is not None:
381 in_routine=True
382 line = follow_rule(0, line)
383 elif re.match(varRegexp, line) is not None:
384 line = follow_rule(0, line)
385 line = follow_rule(4, line)
386 line = follow_rule(5, line)
387 if var == "":
388 if not in_routine:
389 print ("## Warning: unknown variable/routine \"%s\"" % originalLine.rstrip('\n'))
390 var = 'others'
391 for c in commentBloc: seen_vars[var].append(c)
392 commentBloc = []
393 seen_vars[var].append(line)
394 if not keep and not in_routine: var = ""
395
396 # -- dump the sanitized .bb file --
397 addEmptyLine = False
398 # write comments that are not related to variables nor routines
399 for l in commentBloc: olines.append(l)
400 # write variables and routines
401 previourVarPrefix = "unknown"
402 for k in OE_vars:
403 if k=='SRC_URI': addEmptyLine = True
404 if seen_vars[k] != []:
405 if addEmptyLine and not k.startswith(previourVarPrefix):
406 olines.append("")
407 for l in seen_vars[k]:
408 olines.append(l)
409 previourVarPrefix = k.split('_')[0]=='' and "unknown" or k.split('_')[0]
410 for line in olines: print(line)
411