blob: db8a3632d93e430f3f3b7f790fde0e760283ace9 [file] [log] [blame]
#!/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)