xo-umbrella2/xo-cmake/bin/xo-gen-ci.in

87 lines
3.2 KiB
Python
Executable file

#!/usr/bin/env python3
"""xo-gen-ci - generate a forgejo cmake pipeline from an xo subsystem-list.
The repetitive middle of a cmake CI workflow is one identical step per
subsystem, in build order. Maintaining that by hand drifts: as of 2026-09-07
.forgejo/workflows/ci-cmake.yaml named 41 subsystems where subsystem-list had
70, so 28 -- including every python binding module -- were not built by CI at
all, with nothing to say so.
Membership and order come from the same file xo-build reads, so a subsystem
added there is built by CI without a second edit. subsystem-list is a valid
topological order (verified against subsystem-edges: 226 edges, 0 violations),
which is what makes it safe to drive a sequential pipeline.
The generated step runs `xo-build --utest`, not a bare `ctest`: a subsystem
with no tests then reports 'no-tests' rather than exiting 0 and reading as a
pass, so the template needs no per-subsystem conditionals.
TEMPLATE. jinja2, like genfacet (xo-facet/codegen/*.j2), with the same
Environment settings. NB DELIMITERS ARE NOT THE DEFAULTS:
expression [[ sub ]] (jinja default {{ }} collides with
statement <% for ... %> Actions' own ${{ matrix.cc }} -- rendering
the workflow head raises
UndefinedError: 'matrix' is undefined)
Use:
xo-gen-ci <template.j2> <subsystem-list> [--exclude SUB]... [-o OUTPUT]
"""
import argparse
import sys
from pathlib import Path
from jinja2 import Environment, FileSystemLoader
def subsystems(path, exclude):
"""subsystem names from `path`, in file order, minus `exclude`"""
out = []
with open(path) as f:
for line in f:
name = line.strip()
if name and not name.startswith("#") and name not in exclude:
out.append(name)
return out
def main():
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
p.add_argument("template", help="jinja2 workflow template (.j2)")
p.add_argument("subsystem_list")
p.add_argument("--exclude", action="append", default=[], metavar="SUB",
help="omit this subsystem (e.g. the xo-cmake bootstrap)")
p.add_argument("-o", "--output", help="write here rather than to stdout")
args = p.parse_args()
template_path = Path(args.template)
env = Environment(loader=FileSystemLoader(template_path.parent),
variable_start_string="[[",
variable_end_string="]]",
block_start_string="<%",
block_end_string="%>",
trim_blocks=True,
lstrip_blocks=True,
keep_trailing_newline=True)
subs = subsystems(args.subsystem_list, set(args.exclude))
text = env.get_template(template_path.name).render(
subsystems=subs,
template_name=template_path.name,
subsystem_list=args.subsystem_list)
if args.output:
Path(args.output).write_text(text)
print(f"xo-gen-ci: wrote {args.output} ({len(subs)} subsystems)",
file=sys.stderr)
else:
sys.stdout.write(text)
return 0
if __name__ == "__main__":
sys.exit(main())