bbvars.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. #!/usr/bin/env python3
  2. #
  3. # SPDX-License-Identifier: GPL-2.0-or-later
  4. #
  5. # Copyright (C) Darren Hart <dvhart@linux.intel.com>, 2010
  6. import sys
  7. import getopt
  8. import os
  9. import os.path
  10. import re
  11. # Set up sys.path to let us import tinfoil
  12. scripts_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
  13. lib_path = scripts_path + '/lib'
  14. sys.path.insert(0, lib_path)
  15. import scriptpath
  16. scriptpath.add_bitbake_lib_path()
  17. import bb.tinfoil
  18. def usage():
  19. print('Usage: %s -d FILENAME [-d FILENAME]*' % os.path.basename(sys.argv[0]))
  20. print(' -d FILENAME documentation file to search')
  21. print(' -h, --help display this help and exit')
  22. print(' -t FILENAME documentation config file (for doc tags)')
  23. print(' -T Only display variables with doc tags (requires -t)')
  24. def bbvar_is_documented(var, documented_vars):
  25. ''' Check if variable (var) is in the list of documented variables(documented_vars) '''
  26. if var in documented_vars:
  27. return True
  28. else:
  29. return False
  30. def collect_documented_vars(docfiles):
  31. ''' Walk the docfiles and collect the documented variables '''
  32. documented_vars = []
  33. prog = re.compile(".*($|[^A-Z_])<glossentry id=\'var-")
  34. var_prog = re.compile('<glossentry id=\'var-(.*)\'>')
  35. for d in docfiles:
  36. with open(d) as f:
  37. documented_vars += var_prog.findall(f.read())
  38. return documented_vars
  39. def bbvar_doctag(var, docconf):
  40. prog = re.compile('^%s\[doc\] *= *"(.*)"' % (var))
  41. if docconf == "":
  42. return "?"
  43. try:
  44. f = open(docconf)
  45. except IOError as err:
  46. return err.args[1]
  47. for line in f:
  48. m = prog.search(line)
  49. if m:
  50. return m.group(1)
  51. f.close()
  52. return ""
  53. def main():
  54. docfiles = []
  55. bbvars = set()
  56. undocumented = []
  57. docconf = ""
  58. onlydoctags = False
  59. # Collect and validate input
  60. try:
  61. opts, args = getopt.getopt(sys.argv[1:], "d:hm:t:T", ["help"])
  62. except getopt.GetoptError as err:
  63. print('%s' % str(err))
  64. usage()
  65. sys.exit(2)
  66. for o, a in opts:
  67. if o in ('-h', '--help'):
  68. usage()
  69. sys.exit(0)
  70. elif o == '-d':
  71. if os.path.isfile(a):
  72. docfiles.append(a)
  73. else:
  74. print('ERROR: documentation file %s is not a regular file' % a)
  75. sys.exit(3)
  76. elif o == "-t":
  77. if os.path.isfile(a):
  78. docconf = a
  79. elif o == "-T":
  80. onlydoctags = True
  81. else:
  82. assert False, "unhandled option"
  83. if len(docfiles) == 0:
  84. print('ERROR: no docfile specified')
  85. usage()
  86. sys.exit(5)
  87. if onlydoctags and docconf == "":
  88. print('ERROR: no docconf specified')
  89. usage()
  90. sys.exit(7)
  91. prog = re.compile("^[^a-z]*$")
  92. with bb.tinfoil.Tinfoil() as tinfoil:
  93. tinfoil.prepare(config_only=False)
  94. parser = bb.codeparser.PythonParser('parser', None)
  95. datastore = tinfoil.config_data
  96. def bbvars_update(data):
  97. if prog.match(data):
  98. bbvars.add(data)
  99. if tinfoil.config_data.getVarFlag(data, 'python'):
  100. try:
  101. parser.parse_python(tinfoil.config_data.getVar(data))
  102. except bb.data_smart.ExpansionError:
  103. pass
  104. for var in parser.references:
  105. if prog.match(var):
  106. bbvars.add(var)
  107. else:
  108. try:
  109. expandedVar = datastore.expandWithRefs(datastore.getVar(data, False), data)
  110. for var in expandedVar.references:
  111. if prog.match(var):
  112. bbvars.add(var)
  113. except bb.data_smart.ExpansionError:
  114. pass
  115. # Use tinfoil to collect all the variable names globally
  116. for data in datastore:
  117. bbvars_update(data)
  118. # Collect variables from all recipes
  119. for recipe in tinfoil.all_recipe_files(variants=False):
  120. print("Checking %s" % recipe)
  121. for data in tinfoil.parse_recipe_file(recipe):
  122. bbvars_update(data)
  123. documented_vars = collect_documented_vars(docfiles)
  124. # Check each var for documentation
  125. varlen = 0
  126. for v in bbvars:
  127. if len(v) > varlen:
  128. varlen = len(v)
  129. if not bbvar_is_documented(v, documented_vars):
  130. undocumented.append(v)
  131. undocumented.sort()
  132. varlen = varlen + 1
  133. # Report all undocumented variables
  134. print('Found %d undocumented bb variables (out of %d):' % (len(undocumented), len(bbvars)))
  135. header = '%s%s' % (str("VARIABLE").ljust(varlen), str("DOCTAG").ljust(7))
  136. print(header)
  137. print(str("").ljust(len(header), '='))
  138. for v in undocumented:
  139. doctag = bbvar_doctag(v, docconf)
  140. if not onlydoctags or not doctag == "":
  141. print('%s%s' % (v.ljust(varlen), doctag))
  142. if __name__ == "__main__":
  143. main()