1*4882a593Smuzhiyun#!/usr/bin/env python 2*4882a593Smuzhiyun# 3*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun# documentation.conf update script 6*4882a593Smuzhiyun# 7*4882a593Smuzhiyun# Author: Paul Eggleton <paul.eggleton@linux.intel.com> 8*4882a593Smuzhiyun# 9*4882a593Smuzhiyun# Copyright (C) 2015 Intel Corporation 10*4882a593Smuzhiyun# 11*4882a593Smuzhiyun 12*4882a593Smuzhiyun 13*4882a593Smuzhiyunimport sys 14*4882a593Smuzhiyunimport os 15*4882a593Smuzhiyunimport argparse 16*4882a593Smuzhiyunimport re 17*4882a593Smuzhiyunfrom lxml import etree 18*4882a593Smuzhiyunimport logging 19*4882a593Smuzhiyun 20*4882a593Smuzhiyundef logger_create(name): 21*4882a593Smuzhiyun logger = logging.getLogger(name) 22*4882a593Smuzhiyun loggerhandler = logging.StreamHandler() 23*4882a593Smuzhiyun loggerhandler.setFormatter(logging.Formatter("%(levelname)s: %(message)s")) 24*4882a593Smuzhiyun logger.addHandler(loggerhandler) 25*4882a593Smuzhiyun logger.setLevel(logging.INFO) 26*4882a593Smuzhiyun return logger 27*4882a593Smuzhiyunlogger = logger_create('docconfupdater') 28*4882a593Smuzhiyun 29*4882a593Smuzhiyundef main(): 30*4882a593Smuzhiyun parser = argparse.ArgumentParser(description="documentation.conf updater") 31*4882a593Smuzhiyun parser.add_argument('basepath', help='Path to OE-Core base directory') 32*4882a593Smuzhiyun parser.add_argument('-q', '--quiet', help='Print only warnings/errors', action='store_true') 33*4882a593Smuzhiyun 34*4882a593Smuzhiyun args = parser.parse_args() 35*4882a593Smuzhiyun 36*4882a593Smuzhiyun if args.quiet: 37*4882a593Smuzhiyun logger.setLevel(logging.WARN) 38*4882a593Smuzhiyun 39*4882a593Smuzhiyun if not os.path.isdir(args.basepath): 40*4882a593Smuzhiyun logger.error('Specified base path %s not found') 41*4882a593Smuzhiyun return 1 42*4882a593Smuzhiyun 43*4882a593Smuzhiyun doc_conf = os.path.join(args.basepath, 'meta', 'conf', 'documentation.conf') 44*4882a593Smuzhiyun if not os.path.exists(doc_conf): 45*4882a593Smuzhiyun logger.error('Unable to find %s' % doc_conf) 46*4882a593Smuzhiyun return 1 47*4882a593Smuzhiyun 48*4882a593Smuzhiyun allowed_flags = ['doc'] 49*4882a593Smuzhiyun flag_re = re.compile(r'\[(.+?)\]') 50*4882a593Smuzhiyun 51*4882a593Smuzhiyun infos = {} 52*4882a593Smuzhiyun tree = etree.parse('ref-manual/ref-variables.xml') 53*4882a593Smuzhiyun root = tree.getroot() 54*4882a593Smuzhiyun for glossary in root.findall('glossary'): 55*4882a593Smuzhiyun for glossdiv in glossary.findall('glossdiv'): 56*4882a593Smuzhiyun for glossentry in glossdiv.findall('glossentry'): 57*4882a593Smuzhiyun info = glossentry.find('info') 58*4882a593Smuzhiyun if info is not None: 59*4882a593Smuzhiyun infoline = ' '.join(info.text.split()) 60*4882a593Smuzhiyun infolinesplit = infoline.split('=', 1) 61*4882a593Smuzhiyun if len(infoline) < 2: 62*4882a593Smuzhiyun logger.warn('Invalid info line (no = character), ignoring: %s' % infoline) 63*4882a593Smuzhiyun continue 64*4882a593Smuzhiyun flags = flag_re.findall(infolinesplit[0]) 65*4882a593Smuzhiyun if not flags: 66*4882a593Smuzhiyun logger.warn('Invalid info line (no varflag), ignoring: %s' % infoline) 67*4882a593Smuzhiyun continue 68*4882a593Smuzhiyun for flag in flags: 69*4882a593Smuzhiyun if flag not in allowed_flags: 70*4882a593Smuzhiyun logger.warn('Invalid info line (varflag %s not in allowed list), ignoring: %s' % (flag, infoline)) 71*4882a593Smuzhiyun continue 72*4882a593Smuzhiyun infos[infolinesplit[0].rstrip()] = infolinesplit[1].lstrip() 73*4882a593Smuzhiyun 74*4882a593Smuzhiyun if not infos: 75*4882a593Smuzhiyun logger.error('ERROR: Unable to find any info tags in the glossary') 76*4882a593Smuzhiyun return 1 77*4882a593Smuzhiyun 78*4882a593Smuzhiyun def sortkey(key): 79*4882a593Smuzhiyun # Underscores sort undesirably, so replace them 80*4882a593Smuzhiyun return key.split('[')[0].replace('_', '-') 81*4882a593Smuzhiyun 82*4882a593Smuzhiyun changed = False 83*4882a593Smuzhiyun lines = [] 84*4882a593Smuzhiyun invars = False 85*4882a593Smuzhiyun lastletter = None 86*4882a593Smuzhiyun added = [] 87*4882a593Smuzhiyun with open(doc_conf, 'r') as dcf: 88*4882a593Smuzhiyun for line in dcf: 89*4882a593Smuzhiyun if not invars: 90*4882a593Smuzhiyun if line.startswith('#') and 'DESCRIPTIONS FOR VARIABLES' in line: 91*4882a593Smuzhiyun invars = True 92*4882a593Smuzhiyun elif not line.startswith('#'): 93*4882a593Smuzhiyun linesplit = line.split('=', 1) 94*4882a593Smuzhiyun if len(linesplit) > 1: 95*4882a593Smuzhiyun key = linesplit[0].rstrip() 96*4882a593Smuzhiyun lastletter = key[0] 97*4882a593Smuzhiyun # Find anything in the dict that should come before the current key 98*4882a593Smuzhiyun for dkey in sorted(infos.keys()): 99*4882a593Smuzhiyun if sortkey(dkey) < sortkey(key): 100*4882a593Smuzhiyun lines.append('%s = %s\n' % (dkey, infos[dkey])) 101*4882a593Smuzhiyun added.append(dkey) 102*4882a593Smuzhiyun del infos[dkey] 103*4882a593Smuzhiyun changed = True 104*4882a593Smuzhiyun newvalue = infos.get(key, None) 105*4882a593Smuzhiyun if newvalue: 106*4882a593Smuzhiyun del infos[key] 107*4882a593Smuzhiyun if newvalue != linesplit[1].strip(): 108*4882a593Smuzhiyun lines.append('%s = %s\n' % (key, newvalue)) 109*4882a593Smuzhiyun changed = True 110*4882a593Smuzhiyun continue 111*4882a593Smuzhiyun elif key in added: 112*4882a593Smuzhiyun # We already added a new value for this key, so skip it 113*4882a593Smuzhiyun continue 114*4882a593Smuzhiyun elif lastletter: 115*4882a593Smuzhiyun # Ensure we write out anything anything left over for this letter 116*4882a593Smuzhiyun for dkey in sorted(infos.keys()): 117*4882a593Smuzhiyun if dkey[0] == lastletter: 118*4882a593Smuzhiyun lines.append('%s = %s\n' % (dkey, infos[dkey])) 119*4882a593Smuzhiyun del infos[dkey] 120*4882a593Smuzhiyun changed = True 121*4882a593Smuzhiyun elif dkey[0] > lastletter: 122*4882a593Smuzhiyun # List is sorted, so we're done 123*4882a593Smuzhiyun break 124*4882a593Smuzhiyun lastletter = None 125*4882a593Smuzhiyun lines.append(line) 126*4882a593Smuzhiyun 127*4882a593Smuzhiyun if not invars: 128*4882a593Smuzhiyun logger.error('ERROR: Unable to find variables section in documentation.conf') 129*4882a593Smuzhiyun return 1 130*4882a593Smuzhiyun 131*4882a593Smuzhiyun if infos: 132*4882a593Smuzhiyun changed = True 133*4882a593Smuzhiyun # Write out anything left over 134*4882a593Smuzhiyun lines.append('\n\n') 135*4882a593Smuzhiyun for key in sorted(infos.keys()): 136*4882a593Smuzhiyun lines.append('%s = %s\n' % (key, infos[key])) 137*4882a593Smuzhiyun 138*4882a593Smuzhiyun if changed: 139*4882a593Smuzhiyun logger.info('Updating %s' % doc_conf) 140*4882a593Smuzhiyun with open(doc_conf, 'w') as dcf: 141*4882a593Smuzhiyun for line in lines: 142*4882a593Smuzhiyun dcf.write(line) 143*4882a593Smuzhiyun else: 144*4882a593Smuzhiyun logger.info('No changes required') 145*4882a593Smuzhiyun 146*4882a593Smuzhiyun return 0 147*4882a593Smuzhiyun 148*4882a593Smuzhiyun 149*4882a593Smuzhiyunif __name__ == "__main__": 150*4882a593Smuzhiyun try: 151*4882a593Smuzhiyun ret = main() 152*4882a593Smuzhiyun except Exception: 153*4882a593Smuzhiyun ret = 1 154*4882a593Smuzhiyun import traceback 155*4882a593Smuzhiyun traceback.print_exc(5) 156*4882a593Smuzhiyun sys.exit(ret) 157*4882a593Smuzhiyun 158*4882a593Smuzhiyun 159