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