bbvars.py 5.6 KB

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