angstrom feed sorter: add igep2 support
[openembedded.git] / contrib / oe-stylize.py
1 #!/usr/bin/env python
2
3 """\
4 Sanitize a bitbake file following the OpenEmbedded style guidelines,
5 see http://openembedded.org/wiki/StyleGuide 
6
7 (C) 2006 Cyril Romain <cyril.romain@gmail.com>
8 MIT license
9
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 """
21
22 import fileinput
23 import string
24 import re
25
26 __author__ = "Cyril Romain <cyril.romain@gmail.com>"
27 __version__ = "$Revision: 0.5 $"
28
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'
202 ]
203
204 varRegexp = r'^([a-zA-Z_0-9${}-]*)([ \t]*)([+.:]?=[+.]?)([ \t]*)([^\t]+)'
205 routineRegexp = r'^([a-zA-Z0-9_ ${}-]+?)\('
206
207 # Variables seen in the processed .bb
208 seen_vars = {}
209 for v in OE_vars: 
210     seen_vars[v] = []
211
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()
219
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()
229
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()
236
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()
247
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 '"'])
260
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)])
269
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 ""
276
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
283
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"),
293 )
294
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
311
312
313 if __name__ == "__main__":
314
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)
330
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)
343
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
350
351         if line.startswith('}'): 
352             in_routine=False
353         keep = line.endswith('\\') or in_routine
354
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
362
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 = ""
387
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
403