bbvars.py: add a script to look for undocumented variables

bbvars.py will compare recipes in meta directories with documentation files
and report on variables that don't appear to be documented. It reports the
number of times a variable is used as well as any doctags present in the
documentation config file.

The output of this is intended to aid in determining where documentation may
be lacking, but it is not perfect, and does generate some false positives. An
experienced eye and careful attention to count and doctag should be applied to
the results.

$ ./bbvars.py -d ../../documentation/poky-ref-manual/poky-ref-manual.html -m ../../meta -t ../../meta/conf/documentation.conf -T  | head -n 10
Found 1413 undocumented bb variables (out of 1578):
VARIABLE                              COUNT DOCTAG
===================================================
BUILD_ARCH                            4     The name of the building architecture. E.g. i686.
BUILD_CC_ARCH                         2     FIXME
BUILD_PREFIX                          4     FIXME
BUILD_SYS                             13    FIXME
BUILD_VENDOR                          2     FIXME
CACHE                                 1     The directory holding the cache of the metadata.
COMPATIBLE_HOST                       19    A regular expression which matches the HOST_SYS names supported by the package/file. Failure to match will cause the file to be skipped by the parser.

Signed-off-by: Darren Hart <dvhart@linux.intel.com>
CC: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
Darren Hart 2011-01-11 15:29:58 -08:00 committed by Saul Wold
parent 514e59c780
commit e5e3fb23a2
1 changed files with 186 additions and 0 deletions

186
scripts/contrib/bbvars.py Executable file
View File

@ -0,0 +1,186 @@
#!/usr/bin/env python
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Copyright (C) Darren Hart <dvhart@linux.intel.com>, 2010
import sys
import getopt
import os
import os.path
import re
def usage():
print 'Usage: %s -d FILENAME [-d FILENAME]* -m METADIR [-m MATADIR]*' % os.path.basename(sys.argv[0])
print ' -d FILENAME documentation file to search'
print ' -h, --help display this help and exit'
print ' -m METADIR meta directory to search for recipes'
print ' -t FILENAME documentation config file (for doc tags)'
print ' -T Only display variables with doc tags (requires -t)'
def recipe_bbvars(recipe):
''' Return a unique set of every bbvar encountered in the recipe '''
prog = re.compile("[A-Z_]+")
vset = set()
try:
r = open(recipe)
except IOError as (errno, strerror):
print 'WARNING: Failed to open recipe ', recipe
print strerror
for line in r:
# Strip any comments from the line
line = line.rsplit('#')[0]
vset = vset.union(set(prog.findall(line)))
r.close()
bbvars = {}
for v in vset:
bbvars[v] = 1
return bbvars
def collect_bbvars(metadir):
''' Walk the metadir and collect the bbvars from each recipe found '''
bbvars = {}
for root,dirs,files in os.walk(metadir):
for name in files:
if name.find(".bb") >= 0:
for key in recipe_bbvars(os.path.join(root,name)).iterkeys():
if bbvars.has_key(key):
bbvars[key] = bbvars[key] + 1
else:
bbvars[key] = 1
return bbvars
def bbvar_is_documented(var, docfiles):
prog = re.compile(".*($|[^A-Z_])%s([^A-Z_]|$)" % (var))
for doc in docfiles:
try:
f = open(doc)
except IOError as (errno, strerror):
print 'WARNING: Failed to open doc ', doc
print strerror
for line in f:
if prog.match(line):
return True
f.close()
return False
def bbvar_doctag(var, docconf):
prog = re.compile('^%s\[doc\] *= *"(.*)"' % (var))
if docconf == "":
return "?"
try:
f = open(docconf)
except IOError as (errno, strerror):
return strerror
for line in f:
m = prog.search(line)
if m:
return m.group(1)
f.close()
return ""
def main():
docfiles = []
metadirs = []
bbvars = {}
undocumented = []
docconf = ""
onlydoctags = False
# Collect and validate input
try:
opts, args = getopt.getopt(sys.argv[1:], "d:hm:t:T", ["help"])
except getopt.GetoptError, err:
print '%s' % str(err)
usage()
sys.exit(2)
for o, a in opts:
if o in ('-h', '--help'):
usage()
sys.exit(0)
elif o == '-d':
if os.path.isfile(a):
docfiles.append(a)
else:
print 'ERROR: documentation file %s is not a regular file' % (a)
sys.exit(3)
elif o == '-m':
if os.path.isdir(a):
metadirs.append(a)
else:
print 'ERROR: meta directory %s is not a directory' % (a)
sys.exit(4)
elif o == "-t":
if os.path.isfile(a):
docconf = a
elif o == "-T":
onlydoctags = True
else:
assert False, "unhandled option"
if len(docfiles) == 0:
print 'ERROR: no docfile specified'
usage()
sys.exit(5)
if len(metadirs) == 0:
print 'ERROR: no metadir specified'
usage()
sys.exit(6)
if onlydoctags and docconf == "":
print 'ERROR: no docconf specified'
usage()
sys.exit(7)
# Collect all the variable names from the recipes in the metadirs
for m in metadirs:
for key,cnt in collect_bbvars(m).iteritems():
if bbvars.has_key(key):
bbvars[key] = bbvars[key] + cnt
else:
bbvars[key] = cnt
# Check each var for documentation
varlen = 0
for v in bbvars.iterkeys():
if len(v) > varlen:
varlen = len(v)
if not bbvar_is_documented(v, docfiles):
undocumented.append(v)
undocumented.sort()
varlen = varlen + 1
# Report all undocumented variables
print 'Found %d undocumented bb variables (out of %d):' % (len(undocumented), len(bbvars))
header = '%s%s%s' % (str("VARIABLE").ljust(varlen), str("COUNT").ljust(6), str("DOCTAG").ljust(7))
print header
print str("").ljust(len(header), '=')
for v in undocumented:
doctag = bbvar_doctag(v, docconf)
if not onlydoctags or not doctag == "":
print '%s%s%s' % (v.ljust(varlen), str(bbvars[v]).ljust(6), doctag)
if __name__ == "__main__":
main()