File indexing completed on 2025-08-03 08:10:04
0001
0002 """
0003 This script accepts a cmake lists file as an argument extracts all
0004 `option` and `set(... CACHE ...)` variables. It then writes a
0005 markdown table to stdout
0006 """
0007
0008 import argparse
0009 from pathlib import Path
0010 import re
0011 import textwrap
0012 import sys
0013 import difflib
0014
0015 p = argparse.ArgumentParser(description=__doc__)
0016
0017 p.add_argument("cmakefile", help="Input cmake lists file to parse")
0018 p.add_argument(
0019 "--prefix", default="ACTS_", help="Prefix to identify relevant variables to extract"
0020 )
0021 p.add_argument(
0022 "--width",
0023 type=int,
0024 default=40,
0025 help="Width of second column generated from cmake doc strings",
0026 )
0027 p.add_argument(
0028 "--write",
0029 "-w",
0030 help="Write table to this file, expects delimiters CMAKE_OPTS_{BEGIN,END}",
0031 type=Path,
0032 )
0033 p.add_argument(
0034 "--verify",
0035 "-v",
0036 help="Only verify the target file contains the right table, don't write",
0037 action="store_true",
0038 )
0039
0040
0041 args = p.parse_args()
0042
0043 cmakefile = Path(args.cmakefile)
0044
0045 with cmakefile.open() as fh:
0046 opts = {}
0047 rows = []
0048 for line in fh:
0049 if m := re.match(
0050 rf"option\( *({args.prefix}\w*) \"(.*)\" (ON|OFF|\${{\w+}})\ *\)", line
0051 ):
0052 name, doc, default = m.groups()
0053 type = "bool"
0054 if m := re.match(r"\${(\w+)}", default):
0055 lookup = m.group(1)
0056 if lookup in opts:
0057 default = f"{lookup} -> {opts[lookup]}"
0058 elif m := re.match(
0059 rf"set\( *({args.prefix}\w*) \"(.*)\" CACHE (\w+) \"(.*)\"( FORCE)? *\)",
0060 line,
0061 ):
0062 name, default, type, doc, _ = m.groups()
0063 type = type.lower()
0064 if default == "":
0065 default = '""'
0066 else:
0067 continue
0068
0069 opts[name] = default
0070 doc = "<br>".join(textwrap.wrap(doc, width=args.width))
0071 rows.append((name, f"{doc}<br> type: `{type}`, default: `{default}`"))
0072
0073 output = ""
0074
0075 headers = ("Option", "Description")
0076 column_lengths = [0] * len(rows[0])
0077
0078 for row in rows:
0079 for i, col in enumerate(row):
0080 column_lengths[i] = max(column_lengths[i], len(col))
0081
0082 output += "|"
0083 for i, header in enumerate(headers):
0084 output += " " + header.ljust(column_lengths[i]) + " |"
0085 output += "\n"
0086
0087 output += "|"
0088 for i in range(len(column_lengths)):
0089 output += "-" + ("-" * column_lengths[i]) + "-|"
0090 output += "\n"
0091
0092
0093 for row in rows:
0094 output += "|"
0095 for i, col in enumerate(row):
0096 output += " " + col.ljust(column_lengths[i]) + " |"
0097 output += "\n"
0098
0099 output = output.strip()
0100
0101 if args.write and args.write.exists():
0102 source = args.write.read_text().split("\n")
0103 try:
0104 begin = source.index("<!-- CMAKE_OPTS_BEGIN -->")
0105 end = source.index("<!-- CMAKE_OPTS_END -->")
0106 except ValueError:
0107 print("Markers not found in output file")
0108 sys.exit(1)
0109
0110 if args.verify:
0111 actual = "\n".join(source[begin + 1 : end])
0112 if output != actual:
0113 print("MISMATCH:\n" + "-" * 9 + "\n")
0114 print(
0115 "\n".join(
0116 difflib.unified_diff(
0117 actual.split("\n"),
0118 output.split("\n"),
0119 fromfile="actual",
0120 tofile="output",
0121 )
0122 )
0123 )
0124 sys.exit(1)
0125 elif args.write:
0126 out = source[: begin + 1] + output.split("\n") + source[end:]
0127 args.write.write_text("\n".join(out))
0128 else:
0129 print(output)