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