#!/usr/bin/env python """ sanitize a bitbake file following the OpenEmbedded style guidelines see http://openembedded.org/wiki/StyleGuide (C) 2006 Cyril Romain MIT license This script requires the odict module written by Nicola Larosa and Michael Foord. To get the odict module is available on: Debian: apt-get install rest2web Gentoo: emerge pythonutils or can be download here: http://www.voidspace.org.uk/python/odict.html 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 odict import OrderedDict import fileinput import string import re __author__ = "Cyril Romain " __version__ = "$Revision: 0.4 $" # The standard set of variables often found in .bb files in the preferred order OE_vars = OrderedDict([ ('DESCRIPTION', []), ('AUTHOR', []), ('HOMEPAGE', []), ('SECTION', []), ('PRIORITY', []), ('MAINTAINER', []), ('LICENSE', []), ('DEPENDS', []), ('RDEPENDS', []), ('RRECOMMENDS', []), ('RSUGGESTS', []), ('PROVIDES', []), ('RPROVIDES', []), ('RCONFLICTS', []), ('SRCDATE', []), ('PV', []), ('PR', []), ('SRC_URI', []), ('S', []), ('GPE_TARBALL_SUFFIX', []), ('inherit', []), ('EXTRA_', []), ('do_fetch', []), ('do_unpack', []), ('do_patch', []), ('do_configure', []), ('do_compile', []), ('do_install', []), ('do_package', []), ('do_stage', []), ('PACKAGE_ARCH', []), ('PACKAGES', []), ('FILES', []), ('WORKDIR', []), ('acpaths', []), ('addhandler', []), ('addtask', []), ('bindir', []), ('export', []), ('headers', []), ('include', []), ('includedir', []), ('python', []), ('qtopiadir', []), ('pkg_postins', []), ('pkg_postrm', []), ('require', []), ('sbindir', []), ('basesysconfdir', []), ('sysconfdir', []), ('ALLOW_EMPTY', []), ('ALTERNATIVE_LINK', []), ('ALTERNATIVE_NAME', []), ('ALTERNATIVE_PATH', []), ('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_ALL_DEPS', []), ('BUILD_CPPFLAGS', []), ('CFLAGS', []), ('CCFLAGS', []), ('CMDLINE', []), ('COLLIE_MEMORY_SIZE', []), ('COMPATIBLE_HOST', []), ('COMPATIBLE_MACHINE', []), ('COMPILE_HERMES', []), ('CONFFILES', []), ('CONFLICTS', []), ('CORE_EXTRA_D', []), ('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_NAME', []), ('INITSCRIPT_PACKAGES', []), ('INITSCRIPT_PARAMS', []), ('IPKG_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', []), ('MACHINE_ESSENTIAL_EXTRA_RDEPENDS', []), ('MACHINE_ESSENTIAL_EXTRA_RRECOMMENDS', []), ('MACHINE_EXTRA_RDEPENDS', []), ('MACHINE_EXTRA_RRECOMMENDS', []), ('MACHINE_FEATURES', []), ('MACHINE_TASKS', []), ('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', []), ('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', []), ('UNSLUNG_PACKAGES', []), ('VIRTUAL_NAME', []), ('XORG_PN', []), ('XSERVER', []), ('others', []) ]) varRegexp = r'^([A-Z_0-9]*)([ \t]*?)([+.:]?=[+.]?)([ \t]*?)("[^"]*["\\]?)' routineRegexp = r'^([a-zA-Z0-9_ -]+?)\(' # _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): return re.match(varRegexp, line) is not None def conformTo_rule4(line): return conformTo_rule5_(line) # _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:", line 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: # 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 OE_vars.has_key(var): for c in commentBloc: OE_vars[var].append(c) commentBloc = [] OE_vars[var].append(line) else: varexist = False for k in OE_vars: if line.startswith(k): line = follow_rule(0, line) varexist = True if re.match(routineRegexp, line) is not None: in_routine=True elif re.match(varRegexp, line) is not None: line = follow_rule(4, line) line = follow_rule(5, line) for c in commentBloc: OE_vars[k].append(c) commentBloc = [] OE_vars[k].append(line) var = (keep==True or in_routine==True) and k or "" break if not varexist: if not in_routine: print "## Warning: unknown variable/routine \"%s\"" % line OE_vars['others'].append(line) if not keep and not in_routine: var = "" # -- dump the sanitized .bb file -- #for k in OE_vars: print k, OE_vars[k] addEmptyLine = False for k in OE_vars: if k=='SRC_URI': addEmptyLine = True if OE_vars[k] != []: if addEmptyLine: olines.append("") for l in OE_vars[k]: olines.append(l) for line in olines: print line