blob: ba4b87a74cb4238ca96252067e36efcae0e47add [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',
Patrick Williamsddad1a12017-02-23 20:36:32 -0600105 'MIPS_INSTRUCTION_SET',
Patrick Williamsb48b7b42016-08-17 15:04:38 -0500106 'ARM_MUTEX',
107 'ART_CONFIG',
108 'B',
109 'BJAM_OPTS',
110 'BJAM_TOOLS',
111 'BONOBO_HEADERS',
112 'BOOTSCRIPTS',
113 'BROKEN',
114 'BUILD_CPPFLAGS',
115 'CFLAGS',
116 'CCFLAGS',
117 'CMDLINE',
118 'COLLIE_MEMORY_SIZE',
119 'COMPATIBLE_HOST',
120 'COMPATIBLE_MACHINE',
121 'COMPILE_HERMES',
122 'CONFFILES',
123 'CONFLICTS',
124 'CORE_EXTRA_D',
125 'CORE_IMAGE_EXTRA_INSTALL',
126 'CORE_PACKAGES_D',
127 'CORE_PACKAGES_RD',
128 'CPPFLAGS',
129 'CVSDATE',
130 'CXXFLAGS',
131 'DEBIAN_NOAUTONAME',
132 'DEBUG_APPS',
133 'DEFAULT_PREFERENCE',
134 'DB4_CONFIG',
135 'EXCLUDE_FROM_SHLIBS',
136 'EXCLUDE_FROM_WORLD',
137 'FIXEDSRCDATE',
138 'GLIBC_ADDONS',
139 'GLIBC_EXTRA_OECONF',
140 'GNOME_VFS_HEADERS',
141 'HEADERS',
142 'INHIBIT_DEFAULT_DEPS',
143 'INITSCRIPT_PACKAGES',
144 'INITSCRIPT_NAME',
145 'INITSCRIPT_PARAMS',
146 'INSANE_SKIP',
147 'PACKAGE_INSTALL',
148 'KERNEL_IMAGETYPE',
149 'KERNEL_IMAGEDEST',
150 'KERNEL_OUTPUT',
151 'KERNEL_RELEASE',
152 'KERNEL_PRIORITY',
153 'KERNEL_SOURCE',
154 'KERNEL_SUFFIX',
155 'KERNEL_VERSION',
156 'K_MAJOR',
157 'K_MICRO',
158 'K_MINOR',
159 'HHV',
160 'KV',
161 'LDFLAGS',
162 'LD',
163 'LD_SO',
164 'LDLIBS',
165 'LEAD_SONAME',
166 'LIBTOOL',
167 'LIBBDB_EXTRA',
168 'LIBV',
169 'MACHINE_ESSENTIAL_EXTRA_RDEPENDS',
170 'MACHINE_ESSENTIAL_EXTRA_RRECOMMENDS',
171 'MACHINE_EXTRA_RDEPENDS',
172 'MACHINE_EXTRA_RRECOMMENDS',
173 'MACHINE_FEATURES',
174 'MACHINE_TASKS',
175 'MACHINE',
176 'MACHTYPE',
177 'MAKE_TARGETS',
178 'MESSAGEUSER',
179 'MESSAGEHOME',
180 'MIRRORS',
181 'MUTEX',
182 'OE_QMAKE_INCDIR_QT',
183 'OE_QMAKE_CXXFLAGS',
184 'ORBIT_IDL_SRC',
185 'PARALLEL_MAKE',
186 'PAKCAGE_ARCH',
187 'PCMCIA_MANAGER',
188 'PKG_BASENAME',
189 'PKG',
190 'QEMU',
191 'QMAKE_PROFILES',
192 'QPEDIR',
193 'QPF_DESCRIPTION',
194 'QPF_PKGPATTERN',
195 'QT_CONFIG_FLAGS',
196 'QT_LIBRARY',
197 'ROOTFS_POSTPROCESS_COMMAND',
198 'RREPLACES',
199 'TARGET_CFLAGS',
200 'TARGET_CPPFLAGS',
201 'TARGET_LDFLAGS',
202 'UBOOT_MACHINE',
203 'UCLIBC_BASE',
204 'UCLIBC_PATCHES',
205 'USERADD_PACKAGES',
206 'USERADD_PARAM',
207 'VIRTUAL_NAME',
208 'XORG_PN',
209 'XSERVER',
210 'others'
211]
212
213varRegexp = r'^([a-zA-Z_0-9${}-]*)([ \t]*)([+.:]?=[+.]?)([ \t]*)([^\t]+)'
214routineRegexp = r'^([a-zA-Z0-9_ ${}-]+?)\('
215
216# Variables seen in the processed .bb
217seen_vars = {}
218for v in OE_vars:
219 seen_vars[v] = []
220
221# _Format guideline #0_:
222# No spaces are allowed at the beginning of lines that define a variable or
223# a do_ routine
224def respect_rule0(line):
225 return line.lstrip()==line
226def conformTo_rule0(line):
227 return line.lstrip()
228
229# _Format guideline #1_:
230# No spaces are allowed behind the line continuation symbol '\'
231def respect_rule1(line):
232 if line.rstrip().endswith('\\'):
233 return line.endswith('\\')
234 else:
235 return True
236def conformTo_rule1(line):
237 return line.rstrip()
238
239# _Format guideline #2_:
240# Tabs should not be used (use spaces instead).
241def respect_rule2(line):
242 return line.count('\t')==0
243def conformTo_rule2(line):
244 return line.expandtabs()
245
246# _Format guideline #3_:
247# Comments inside bb files are allowed using the '#' character at the
248# beginning of a line.
249def respect_rule3(line):
250 if line.lstrip().startswith('#'):
251 return line.startswith('#')
252 else:
253 return True
254def conformTo_rule3(line):
255 return line.lstrip()
256
257# _Format guideline #4_:
258# Use quotes on the right hand side of assignments FOO = "BAR"
259def respect_rule4(line):
260 r = re.search(varRegexp, line)
261 if r is not None:
262 r2 = re.search(r'("?)([^"\\]*)(["\\]?)', r.group(5))
263 # do not test for None it because always match
264 return r2.group(1)=='"' and r2.group(3)!=''
265 return False
266def conformTo_rule4(line):
267 r = re.search(varRegexp, line)
268 return ''.join([r.group(1), ' ', r.group(3), ' "', r.group(5), r.group(5).endswith('"') and '' or '"'])
269
270# _Format guideline #5_:
271# The correct spacing for a variable is FOO = "BAR".
272def respect_rule5(line):
273 r = re.search(varRegexp, line)
274 return r is not None and r.group(2)==" " and r.group(4)==" "
275def conformTo_rule5(line):
276 r = re.search(varRegexp, line)
277 return ''.join([r.group(1), ' ', r.group(3), ' ', r.group(5)])
278
279# _Format guideline #6_:
280# Don't use spaces or tabs on empty lines
281def respect_rule6(line):
282 return not line.isspace() or line=="\n"
283def conformTo_rule6(line):
284 return ""
285
286# _Format guideline #7_:
287# Indentation of multiline variables such as SRC_URI is desireable.
288def respect_rule7(line):
289 return True
290def conformTo_rule7(line):
291 return line
292
293rules = (
294 (respect_rule0, conformTo_rule0, "No spaces are allowed at the beginning of lines that define a variable or a do_ routine"),
295 (respect_rule1, conformTo_rule1, "No spaces are allowed behind the line continuation symbol '\\'"),
296 (respect_rule2, conformTo_rule2, "Tabs should not be used (use spaces instead)"),
297 (respect_rule3, conformTo_rule3, "Comments inside bb files are allowed using the '#' character at the beginning of a line"),
298 (respect_rule4, conformTo_rule4, "Use quotes on the right hand side of assignments FOO = \"BAR\""),
299 (respect_rule5, conformTo_rule5, "The correct spacing for a variable is FOO = \"BAR\""),
300 (respect_rule6, conformTo_rule6, "Don't use spaces or tabs on empty lines"),
301 (respect_rule7, conformTo_rule7, "Indentation of multiline variables such as SRC_URI is desireable"),
302)
303
304# Function to check that a line respects a rule. If not, it tries to conform
305# the line to the rule. Reminder or Disgression message are dump accordingly.
306def follow_rule(i, line):
307 oldline = line
308 # if the line does not respect the rule
309 if not rules[i][0](line):
310 # try to conform it to the rule
311 line = rules[i][1](line)
312 # if the line still does not respect the rule
313 if not rules[i][0](line):
314 # this is a rule disgression
315 print ("## Disgression: ", rules[i][2], " in: '", oldline, "'")
316 else:
317 # just remind user about his/her errors
318 print ("## Reminder: ", rules[i][2], " in : '", oldline, "'")
319 return line
320
321
322if __name__ == "__main__":
323
324 # -- retrieves the lines of the .bb file --
325 lines = []
326 for line in fileinput.input():
327 # use 'if True' to warn user about all the rule he/she breaks
328 # use 'if False' to conform to rules{2,1,6} without warnings
329 if True:
330 lines.append(line)
331 else:
332 # expandtabs on each line so that rule2 is always respected
333 # rstrip each line so that rule1 is always respected
334 line = line.expandtabs().rstrip()
335 # ignore empty lines (or line filled with spaces or tabs only)
336 # so that rule6 is always respected
337 if line is not '':
338 lines.append(line)
339
340 # -- parse the file --
341 var = ""
342 in_routine = False
343 commentBloc = []
344 olines = []
345 for line in lines:
346 originalLine = line
347 # rstrip line to remove line breaks characters
348 line = line.rstrip()
349 line = follow_rule(2, line)
350 line = follow_rule(1, line)
351 line = follow_rule(6, line)
352
353 # ignore empty lines
354 if line.isspace() or line is '':
355 # flush comments into the olines
356 for c in commentBloc: olines.append(c)
357 commentBloc = []
358 continue
359
360 if line.startswith('}'):
361 in_routine=False
362 keep = line.endswith('\\') or in_routine
363
364 # handles commented lines
365 if line.lstrip().startswith('#'):
366 # check and follow rule3 if not in a variables or routines
367 if not in_routine:
368 line = follow_rule(3, line)
369 commentBloc.append(line)
370 continue
371
372 if var in seen_vars:
373 for c in commentBloc: seen_vars[var].append(c)
374 commentBloc = []
375 seen_vars[var].append(line)
376 else:
377 for k in OE_vars:
378 if line.startswith(k):
379 var = k
380 break
381 if re.match(routineRegexp, line) is not None:
382 in_routine=True
383 line = follow_rule(0, line)
384 elif re.match(varRegexp, line) is not None:
385 line = follow_rule(0, line)
386 line = follow_rule(4, line)
387 line = follow_rule(5, line)
388 if var == "":
389 if not in_routine:
390 print ("## Warning: unknown variable/routine \"%s\"" % originalLine.rstrip('\n'))
391 var = 'others'
392 for c in commentBloc: seen_vars[var].append(c)
393 commentBloc = []
394 seen_vars[var].append(line)
395 if not keep and not in_routine: var = ""
396
397 # -- dump the sanitized .bb file --
398 addEmptyLine = False
399 # write comments that are not related to variables nor routines
400 for l in commentBloc: olines.append(l)
401 # write variables and routines
402 previourVarPrefix = "unknown"
403 for k in OE_vars:
404 if k=='SRC_URI': addEmptyLine = True
405 if seen_vars[k] != []:
406 if addEmptyLine and not k.startswith(previourVarPrefix):
407 olines.append("")
408 for l in seen_vars[k]:
409 olines.append(l)
410 previourVarPrefix = k.split('_')[0]=='' and "unknown" or k.split('_')[0]
411 for line in olines: print(line)
412