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