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