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