1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
#!/usr/bin/env python3
# Copyright (C) 2019-2025 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http:#mozilla.org/MPL/2.0/.
# Produce System Messages Manual
#
# This tool reads all the message files given on the command line.
# It pulls all the messages and description out, sorts them by
# message ID, and writes them out as a single (formatted) file.
#
# Invocation:
# The code is invoked using the command line:
#
# mes2doc.py [-o <output-file>] <files>
#
# If no output file is specified, output is written to stdout.
# The produced format is ReStructuredText.
import argparse
import os
import pathlib
import re
import sys
def parse_args():
parser = argparse.ArgumentParser(description='Convert set of *.mes files to .rst documentation format')
parser.add_argument('-o', '--output', help='Output file name (default to stdout).')
parser.add_argument('files', help='Input .mes files.', nargs='?')
args = parser.parse_args()
return args
def read_input_files(files):
messages = {}
for f in files:
if '/premium/' in f and not pathlib.Path(f).is_file():
# Premium can be missing which is fine for daily development, and CI tasks.
print(f'Ignoring non-existing file {f}')
continue
with open(f, encoding='utf-8') as fp:
print(f'Processing {f}')
msg_descr = None
msg_id = None
msg_text = None
for line in fp.readlines():
line = line.strip()
if not line or line.startswith('#'):
pass
elif line.startswith('//'):
pass
elif line.startswith('$'):
pass
elif line.startswith('%'):
# end previous message
if msg_id is not None:
section = msg_id.split('_')[0]
messages[msg_id] = (section, msg_id, msg_text, msg_descr)
# start next message
m = re.search(r'^%\s?([A-Z0-9_]+)\s+(.*)', line)
msg_id, msg_text = m.groups()
msg_descr = []
else:
msg_descr.append(line)
if msg_id is not None:
section = msg_id.split('_')[0]
messages[msg_id] = (section, msg_id, msg_text, msg_descr)
return messages
def generate_rst(messages):
rst = '..\n'
rst += ' File generated by "doc/sphinx/mes2doc.py" or by "make -C doc/sphinx". Do not edit by hand.\n\n'
rst += '''.. _kea-messages:
###################
Kea Messages Manual
###################
Kea is an open source implementation of the Dynamic Host Configuration
Protocol (DHCP) servers, developed and maintained by Internet Systems
Consortium (ISC).
This is the reference guide for Kea version |release|.
Links to the most up-to-date version of this document (in PDF, HTML,
and plain text formats), along with other useful information about
Kea, can be found in ISC's `Knowledgebase <https://kea.readthedocs.io>`_.
Please note that in the messages below, the percent sign (``%``) followed by a number is
used to indicate a placeholder for data that is provided by the Kea code during its operation.
.. toctree::
:numbered:
:maxdepth: 5
'''
prev_section = None
for _, msg in sorted(messages.items()):
section, msg_id, msg_text, msg_descr = msg
if section != prev_section:
prev_section = section
rst += '*' * len(section) + '\n'
rst += section + '\n'
rst += '*' * len(section) + '\n'
rst += '\n'
rst += msg_id + '\n'
rst += '=' * len(msg_id) + '\n'
rst += '\n'
rst += '.. code-block:: text\n'
rst += '\n'
rst += ' ' + msg_text + '\n'
rst += '\n'
rst += ''.join([line + '\n' for line in msg_descr])
rst += '\n'
rst += '''.. _kea-debug-messages:
*******************************
Kea Debug Messages By Log Level
*******************************
'''
rst += '.. include:: debug-messages.rst'
rst += '\n'
return rst
def generate(in_files, out_file):
messages = read_input_files(in_files)
rst = generate_rst(messages)
if out_file:
with open(out_file, 'w', encoding='utf-8') as f:
f.write(rst)
print('Wrote generated RST content to: %s' % out_file)
else:
print(rst)
def main():
args = parse_args()
if args.files is None:
parent_dir = os.path.dirname(os.path.realpath(os.path.abspath(sys.argv[0])))
mes_files = sorted(pathlib.Path(f'{parent_dir}/../..').glob('**/*.mes'))
# Convert from Path to str.
mes_files = [str(i) for i in mes_files]
else:
mes_files = args.files
generate(mes_files, args.output)
if __name__ == '__main__':
main()
|