oe-stylize.py 11 KB

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