File indexing completed on 2025-01-18 09:11:34
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|\${{.+}})\ *\) ?(?:# (.*))?.*",
0051 line,
0052 ):
0053 name, doc, default, comment = m.groups()
0054
0055 if comment is not None and comment.strip().startswith("default:"):
0056 default = comment.strip().split("default:")[1].strip()
0057
0058 type = "bool"
0059 if m := re.match(r"\${(\w+)}", default):
0060 lookup = m.group(1)
0061 if lookup in opts:
0062 default = f"{lookup} -> {opts[lookup]}"
0063 elif m := re.match(
0064 rf"set\( *({args.prefix}\w*) \"(.*)\" CACHE (\w+) \"(.*)\"( FORCE)? *\)",
0065 line,
0066 ):
0067 name, default, type, doc, _ = m.groups()
0068 type = type.lower()
0069 if default == "":
0070 default = '""'
0071 else:
0072 continue
0073
0074 opts[name] = default
0075 doc = "<br>".join(textwrap.wrap(doc, width=args.width))
0076 rows.append((name, f"{doc}<br> type: `{type}`, default: `{default}`"))
0077
0078 output = ""
0079
0080 headers = ("Option", "Description")
0081 column_lengths = [0] * len(rows[0])
0082
0083 for row in rows:
0084 for i, col in enumerate(row):
0085 column_lengths[i] = max(column_lengths[i], len(col))
0086
0087 output += "|"
0088 for i, header in enumerate(headers):
0089 output += " " + header.ljust(column_lengths[i]) + " |"
0090 output += "\n"
0091
0092 output += "|"
0093 for i in range(len(column_lengths)):
0094 output += "-" + ("-" * column_lengths[i]) + "-|"
0095 output += "\n"
0096
0097
0098 for row in rows:
0099 output += "|"
0100 for i, col in enumerate(row):
0101 output += " " + col.ljust(column_lengths[i]) + " |"
0102 output += "\n"
0103
0104 output = output.strip()
0105
0106 if args.write and args.write.exists():
0107 source = args.write.read_text().split("\n")
0108 try:
0109 begin = source.index("<!-- CMAKE_OPTS_BEGIN -->")
0110 end = source.index("<!-- CMAKE_OPTS_END -->")
0111 except ValueError:
0112 print("Markers not found in output file")
0113 sys.exit(1)
0114
0115 if args.verify:
0116 actual = "\n".join(source[begin + 1 : end])
0117 if output != actual:
0118 print("MISMATCH:\n" + "-" * 9 + "\n")
0119 print(
0120 "\n".join(
0121 difflib.unified_diff(
0122 actual.split("\n"),
0123 output.split("\n"),
0124 fromfile="actual",
0125 tofile="output",
0126 )
0127 )
0128 )
0129 sys.exit(1)
0130 elif args.write:
0131 out = source[: begin + 1] + output.split("\n") + source[end:]
0132 args.write.write_text("\n".join(out))
0133 else:
0134 print(output)