| #!/usr/bin/env python |
| |
| """\ |
| Sanitize a bitbake file following the OpenEmbedded style guidelines, |
| see http://openembedded.org/wiki/StyleGuide |
| |
| (C) 2006 Cyril Romain <cyril.romain@gmail.com> |
| MIT license |
| |
| TODO: |
| - add the others OpenEmbedded variables commonly used: |
| - parse command arguments and print usage on misuse |
| . prevent giving more than one .bb file in arguments |
| - write result to a file |
| - backup the original .bb file |
| - make a diff and ask confirmation for patching ? |
| - do not use startswith only: |
| /!\ startswith('SOMETHING') is not taken into account due to the previous startswith('S'). |
| - count rule breaks and displays them in the order frequence |
| """ |
| |
| from __future__ import print_function |
| import fileinput |
| import string |
| import re |
| |
| __author__ = "Cyril Romain <cyril.romain@gmail.com>" |
| __version__ = "$Revision: 0.5 $" |
| |
| # The standard set of variables often found in .bb files in the preferred order |
| OE_vars = [ |
| 'SUMMARY', |
| 'DESCRIPTION', |
| 'AUTHOR', |
| 'HOMEPAGE', |
| 'SECTION', |
| 'LICENSE', |
| 'LIC_FILES_CHKSUM', |
| 'DEPENDS', |
| 'PROVIDES', |
| 'SRCREV', |
| 'SRCDATE', |
| 'PE', |
| 'PV', |
| 'PR', |
| 'INC_PR', |
| 'SRC_URI', |
| 'S', |
| 'GPE_TARBALL_SUFFIX', |
| 'inherit', |
| 'EXTRA_', |
| 'export', |
| 'do_fetch', |
| 'do_unpack', |
| 'do_patch', |
| 'WORKDIR', |
| 'acpaths', |
| 'do_configure', |
| 'do_compile', |
| 'do_install', |
| 'PACKAGES', |
| 'PACKAGE_ARCH', |
| 'RDEPENDS', |
| 'RRECOMMENDS', |
| 'RSUGGESTS', |
| 'RPROVIDES', |
| 'RCONFLICTS', |
| 'FILES', |
| 'do_package', |
| 'do_stage', |
| 'addhandler', |
| 'addtask', |
| 'bindir', |
| 'headers', |
| 'include', |
| 'includedir', |
| 'python', |
| 'qtopiadir', |
| 'pkg_preins', |
| 'pkg_prerm', |
| 'pkg_postins', |
| 'pkg_postrm', |
| 'require', |
| 'sbindir', |
| 'basesysconfdir', |
| 'sysconfdir', |
| 'ALLOW_EMPTY', |
| 'ALTERNATIVE_NAME', |
| 'ALTERNATIVE_PATH', |
| 'ALTERNATIVE_LINK', |
| 'ALTERNATIVE_PRIORITY', |
| 'ALTNAME', |
| 'AMD_DRIVER_LABEL', |
| 'AMD_DRIVER_VERSION', |
| 'ANGSTROM_EXTRA_INSTALL', |
| 'APPDESKTOP', |
| 'APPIMAGE', |
| 'APPNAME', |
| 'APPTYPE', |
| 'APPWEB_BUILD', |
| 'APPWEB_HOST', |
| 'AR', |
| 'ARCH', |
| 'ARM_INSTRUCTION_SET', |
| 'ARM_MUTEX', |
| 'ART_CONFIG', |
| 'B', |
| 'BJAM_OPTS', |
| 'BJAM_TOOLS', |
| 'BONOBO_HEADERS', |
| 'BOOTSCRIPTS', |
| 'BROKEN', |
| 'BUILD_CPPFLAGS', |
| 'CFLAGS', |
| 'CCFLAGS', |
| 'CMDLINE', |
| 'COLLIE_MEMORY_SIZE', |
| 'COMPATIBLE_HOST', |
| 'COMPATIBLE_MACHINE', |
| 'COMPILE_HERMES', |
| 'CONFFILES', |
| 'CONFLICTS', |
| 'CORE_EXTRA_D', |
| 'CORE_IMAGE_EXTRA_INSTALL', |
| 'CORE_PACKAGES_D', |
| 'CORE_PACKAGES_RD', |
| 'CPPFLAGS', |
| 'CVSDATE', |
| 'CXXFLAGS', |
| 'DEBIAN_NOAUTONAME', |
| 'DEBUG_APPS', |
| 'DEFAULT_PREFERENCE', |
| 'DB4_CONFIG', |
| 'EXCLUDE_FROM_SHLIBS', |
| 'EXCLUDE_FROM_WORLD', |
| 'FIXEDSRCDATE', |
| 'GLIBC_ADDONS', |
| 'GLIBC_EXTRA_OECONF', |
| 'GNOME_VFS_HEADERS', |
| 'HEADERS', |
| 'INHIBIT_DEFAULT_DEPS', |
| 'INITSCRIPT_PACKAGES', |
| 'INITSCRIPT_NAME', |
| 'INITSCRIPT_PARAMS', |
| 'INSANE_SKIP', |
| 'PACKAGE_INSTALL', |
| 'KERNEL_IMAGETYPE', |
| 'KERNEL_IMAGEDEST', |
| 'KERNEL_OUTPUT', |
| 'KERNEL_RELEASE', |
| 'KERNEL_PRIORITY', |
| 'KERNEL_SOURCE', |
| 'KERNEL_SUFFIX', |
| 'KERNEL_VERSION', |
| 'K_MAJOR', |
| 'K_MICRO', |
| 'K_MINOR', |
| 'HHV', |
| 'KV', |
| 'LDFLAGS', |
| 'LD', |
| 'LD_SO', |
| 'LDLIBS', |
| 'LEAD_SONAME', |
| 'LIBTOOL', |
| 'LIBBDB_EXTRA', |
| 'LIBV', |
| 'MACHINE_ESSENTIAL_EXTRA_RDEPENDS', |
| 'MACHINE_ESSENTIAL_EXTRA_RRECOMMENDS', |
| 'MACHINE_EXTRA_RDEPENDS', |
| 'MACHINE_EXTRA_RRECOMMENDS', |
| 'MACHINE_FEATURES', |
| 'MACHINE_TASKS', |
| 'MACHINE', |
| 'MACHTYPE', |
| 'MAKE_TARGETS', |
| 'MESSAGEUSER', |
| 'MESSAGEHOME', |
| 'MIRRORS', |
| 'MUTEX', |
| 'OE_QMAKE_INCDIR_QT', |
| 'OE_QMAKE_CXXFLAGS', |
| 'ORBIT_IDL_SRC', |
| 'PARALLEL_MAKE', |
| 'PAKCAGE_ARCH', |
| 'PCMCIA_MANAGER', |
| 'PKG_BASENAME', |
| 'PKG', |
| 'QEMU', |
| 'QMAKE_PROFILES', |
| 'QPEDIR', |
| 'QPF_DESCRIPTION', |
| 'QPF_PKGPATTERN', |
| 'QT_CONFIG_FLAGS', |
| 'QT_LIBRARY', |
| 'ROOTFS_POSTPROCESS_COMMAND', |
| 'RREPLACES', |
| 'TARGET_CFLAGS', |
| 'TARGET_CPPFLAGS', |
| 'TARGET_LDFLAGS', |
| 'UBOOT_MACHINE', |
| 'UCLIBC_BASE', |
| 'UCLIBC_PATCHES', |
| 'USERADD_PACKAGES', |
| 'USERADD_PARAM', |
| 'VIRTUAL_NAME', |
| 'XORG_PN', |
| 'XSERVER', |
| 'others' |
| ] |
| |
| varRegexp = r'^([a-zA-Z_0-9${}-]*)([ \t]*)([+.:]?=[+.]?)([ \t]*)([^\t]+)' |
| routineRegexp = r'^([a-zA-Z0-9_ ${}-]+?)\(' |
| |
| # Variables seen in the processed .bb |
| seen_vars = {} |
| for v in OE_vars: |
| seen_vars[v] = [] |
| |
| # _Format guideline #0_: |
| # No spaces are allowed at the beginning of lines that define a variable or |
| # a do_ routine |
| def respect_rule0(line): |
| return line.lstrip()==line |
| def conformTo_rule0(line): |
| return line.lstrip() |
| |
| # _Format guideline #1_: |
| # No spaces are allowed behind the line continuation symbol '\' |
| def respect_rule1(line): |
| if line.rstrip().endswith('\\'): |
| return line.endswith('\\') |
| else: |
| return True |
| def conformTo_rule1(line): |
| return line.rstrip() |
| |
| # _Format guideline #2_: |
| # Tabs should not be used (use spaces instead). |
| def respect_rule2(line): |
| return line.count('\t')==0 |
| def conformTo_rule2(line): |
| return line.expandtabs() |
| |
| # _Format guideline #3_: |
| # Comments inside bb files are allowed using the '#' character at the |
| # beginning of a line. |
| def respect_rule3(line): |
| if line.lstrip().startswith('#'): |
| return line.startswith('#') |
| else: |
| return True |
| def conformTo_rule3(line): |
| return line.lstrip() |
| |
| # _Format guideline #4_: |
| # Use quotes on the right hand side of assignments FOO = "BAR" |
| def respect_rule4(line): |
| r = re.search(varRegexp, line) |
| if r is not None: |
| r2 = re.search(r'("?)([^"\\]*)(["\\]?)', r.group(5)) |
| # do not test for None it because always match |
| return r2.group(1)=='"' and r2.group(3)!='' |
| return False |
| def conformTo_rule4(line): |
| r = re.search(varRegexp, line) |
| return ''.join([r.group(1), ' ', r.group(3), ' "', r.group(5), r.group(5).endswith('"') and '' or '"']) |
| |
| # _Format guideline #5_: |
| # The correct spacing for a variable is FOO = "BAR". |
| def respect_rule5(line): |
| r = re.search(varRegexp, line) |
| return r is not None and r.group(2)==" " and r.group(4)==" " |
| def conformTo_rule5(line): |
| r = re.search(varRegexp, line) |
| return ''.join([r.group(1), ' ', r.group(3), ' ', r.group(5)]) |
| |
| # _Format guideline #6_: |
| # Don't use spaces or tabs on empty lines |
| def respect_rule6(line): |
| return not line.isspace() or line=="\n" |
| def conformTo_rule6(line): |
| return "" |
| |
| # _Format guideline #7_: |
| # Indentation of multiline variables such as SRC_URI is desireable. |
| def respect_rule7(line): |
| return True |
| def conformTo_rule7(line): |
| return line |
| |
| rules = ( |
| (respect_rule0, conformTo_rule0, "No spaces are allowed at the beginning of lines that define a variable or a do_ routine"), |
| (respect_rule1, conformTo_rule1, "No spaces are allowed behind the line continuation symbol '\\'"), |
| (respect_rule2, conformTo_rule2, "Tabs should not be used (use spaces instead)"), |
| (respect_rule3, conformTo_rule3, "Comments inside bb files are allowed using the '#' character at the beginning of a line"), |
| (respect_rule4, conformTo_rule4, "Use quotes on the right hand side of assignments FOO = \"BAR\""), |
| (respect_rule5, conformTo_rule5, "The correct spacing for a variable is FOO = \"BAR\""), |
| (respect_rule6, conformTo_rule6, "Don't use spaces or tabs on empty lines"), |
| (respect_rule7, conformTo_rule7, "Indentation of multiline variables such as SRC_URI is desireable"), |
| ) |
| |
| # Function to check that a line respects a rule. If not, it tries to conform |
| # the line to the rule. Reminder or Disgression message are dump accordingly. |
| def follow_rule(i, line): |
| oldline = line |
| # if the line does not respect the rule |
| if not rules[i][0](line): |
| # try to conform it to the rule |
| line = rules[i][1](line) |
| # if the line still does not respect the rule |
| if not rules[i][0](line): |
| # this is a rule disgression |
| print ("## Disgression: ", rules[i][2], " in: '", oldline, "'") |
| else: |
| # just remind user about his/her errors |
| print ("## Reminder: ", rules[i][2], " in : '", oldline, "'") |
| return line |
| |
| |
| if __name__ == "__main__": |
| |
| # -- retrieves the lines of the .bb file -- |
| lines = [] |
| for line in fileinput.input(): |
| # use 'if True' to warn user about all the rule he/she breaks |
| # use 'if False' to conform to rules{2,1,6} without warnings |
| if True: |
| lines.append(line) |
| else: |
| # expandtabs on each line so that rule2 is always respected |
| # rstrip each line so that rule1 is always respected |
| line = line.expandtabs().rstrip() |
| # ignore empty lines (or line filled with spaces or tabs only) |
| # so that rule6 is always respected |
| if line is not '': |
| lines.append(line) |
| |
| # -- parse the file -- |
| var = "" |
| in_routine = False |
| commentBloc = [] |
| olines = [] |
| for line in lines: |
| originalLine = line |
| # rstrip line to remove line breaks characters |
| line = line.rstrip() |
| line = follow_rule(2, line) |
| line = follow_rule(1, line) |
| line = follow_rule(6, line) |
| |
| # ignore empty lines |
| if line.isspace() or line is '': |
| # flush comments into the olines |
| for c in commentBloc: olines.append(c) |
| commentBloc = [] |
| continue |
| |
| if line.startswith('}'): |
| in_routine=False |
| keep = line.endswith('\\') or in_routine |
| |
| # handles commented lines |
| if line.lstrip().startswith('#'): |
| # check and follow rule3 if not in a variables or routines |
| if not in_routine: |
| line = follow_rule(3, line) |
| commentBloc.append(line) |
| continue |
| |
| if var in seen_vars: |
| for c in commentBloc: seen_vars[var].append(c) |
| commentBloc = [] |
| seen_vars[var].append(line) |
| else: |
| for k in OE_vars: |
| if line.startswith(k): |
| var = k |
| break |
| if re.match(routineRegexp, line) is not None: |
| in_routine=True |
| line = follow_rule(0, line) |
| elif re.match(varRegexp, line) is not None: |
| line = follow_rule(0, line) |
| line = follow_rule(4, line) |
| line = follow_rule(5, line) |
| if var == "": |
| if not in_routine: |
| print ("## Warning: unknown variable/routine \"%s\"" % originalLine.rstrip('\n')) |
| var = 'others' |
| for c in commentBloc: seen_vars[var].append(c) |
| commentBloc = [] |
| seen_vars[var].append(line) |
| if not keep and not in_routine: var = "" |
| |
| # -- dump the sanitized .bb file -- |
| addEmptyLine = False |
| # write comments that are not related to variables nor routines |
| for l in commentBloc: olines.append(l) |
| # write variables and routines |
| previourVarPrefix = "unknown" |
| for k in OE_vars: |
| if k=='SRC_URI': addEmptyLine = True |
| if seen_vars[k] != []: |
| if addEmptyLine and not k.startswith(previourVarPrefix): |
| olines.append("") |
| for l in seen_vars[k]: |
| olines.append(l) |
| previourVarPrefix = k.split('_')[0]=='' and "unknown" or k.split('_')[0] |
| for line in olines: print(line) |
| |