mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-24 14:03:55 +01:00
dist/tools/features_yaml2mx: feature management utility
Add a python script that allows having a single YAML file as source of
truth for what features are available, and also add documentation such
as help texts and grouping to them.
This utility converts such a YAML file to a Makefile with the contents
```
FEATURES_EXISTING := \
feature_a \
feature_b \
... \
features_z \
#
```
This allows the Makefile based build system to easily check if all
provided features and all requested features do actually exist.
In addition, this also converts the YAML file to a markdown file that
documents the features. This file is then intended to be used by
Doxygen to document the provided features.
Co-authored-by: mguetschow <mikolai.guetschow@tu-dresden.de>
Co-authored-by: chrysn <chrysn@fsfe.org>
This commit is contained in:
parent
001bc0e7eb
commit
ce5bae3edb
12
dist/tools/features_yaml2mx/README.md
vendored
Normal file
12
dist/tools/features_yaml2mx/README.md
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
Tool to export features described in a YAML to formats usable by our build system
|
||||
=================================================================================
|
||||
|
||||
The python script in this folder converts features specified in a YAML file to
|
||||
a Makefile for use to check provided and requested features against, as well
|
||||
as a markdown file for consumption by Doxygen to document the features.
|
||||
|
||||
The YAML file containing the features is `features.yaml` in the root of the
|
||||
repo. Its syntax is documented in a comment on top of said file.
|
||||
|
||||
This script should be invoked by running `make generate-features` in the root
|
||||
of the repository.
|
||||
161
dist/tools/features_yaml2mx/features_yaml2mx.py
vendored
Executable file
161
dist/tools/features_yaml2mx/features_yaml2mx.py
vendored
Executable file
@ -0,0 +1,161 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Command line utility generate trivial Makefile listing all existing features in
|
||||
RIOT and a matching documentation in Markdown format from single YAML file.
|
||||
"""
|
||||
import argparse
|
||||
import yaml
|
||||
|
||||
|
||||
def collect_features(parsed):
|
||||
"""
|
||||
Collect all features from a parsed YAML file
|
||||
|
||||
:param parsed: Parsed YAML file
|
||||
:type parsed: dict
|
||||
:return: list of features in no particular, possible unstable, order
|
||||
:rtype: list
|
||||
"""
|
||||
result = []
|
||||
for group in parsed.get("groups", []):
|
||||
result += collect_features(group)
|
||||
for feature in parsed.get("features", []):
|
||||
result.append(feature["name"])
|
||||
return result
|
||||
|
||||
|
||||
def write_makefile(outfile, yaml_path, parsed):
|
||||
"""
|
||||
Extract the list of features from the given parsed YAML file and writes
|
||||
them into file at the given path in Makefile syntax, e.g.
|
||||
|
||||
FEATURES_EXISTING := \
|
||||
feat_a \
|
||||
feat_b \
|
||||
feat_c \
|
||||
#
|
||||
|
||||
:param outfile: path to the Makefile to write the features to
|
||||
:type outfile: str
|
||||
:param yaml_path: Path to the source YAML file
|
||||
:type yaml_path: str
|
||||
:param parsed: the parsed YAML file
|
||||
:type parsed: dict
|
||||
"""
|
||||
outfile.write(f"""\
|
||||
# WARNING: This has been auto-generated from {yaml_path}.
|
||||
# Do not edit this by hand, but update {yaml_path} instead.
|
||||
# Finally, run `make generate-features` in the root of the RIOT repo.
|
||||
""")
|
||||
outfile.write("FEATURES_EXISTING := \\\n")
|
||||
for feature in sorted(collect_features(parsed)):
|
||||
outfile.write(f" {feature} \\\n")
|
||||
outfile.write(" #\n")
|
||||
outfile.flush()
|
||||
|
||||
|
||||
def write_md_section(outfile, group, level):
|
||||
"""
|
||||
Write a section documenting certain features to the given file in markdown
|
||||
format.
|
||||
|
||||
:param outfile: The file to write the section to
|
||||
:type outfile: file
|
||||
:param group: The group content (e.g. a subtree from the parsed YAML)
|
||||
:type group: dict
|
||||
:param level: The current section level (e.g. 1=section, 2=subsection)
|
||||
:type level: int
|
||||
"""
|
||||
title = group.get("title")
|
||||
outfile.write("#" * level + f" {title}" if title else "" + "\n")
|
||||
if "help" in group:
|
||||
outfile.write("\n")
|
||||
outfile.write(group["help"])
|
||||
outfile.write("\n")
|
||||
|
||||
if "features" in group:
|
||||
outfile.write("\n")
|
||||
outfile.write("""\
|
||||
| Feature | Description |
|
||||
|:--------------------------------- |:----------------------------------------------------------------------------- |
|
||||
""")
|
||||
|
||||
for feature in group["features"]:
|
||||
name = f"`{feature['name']}`"
|
||||
description = feature['help'].strip().replace("\n", " ")
|
||||
outfile.write(f"| {name:<33} | {description:<77} |\n")
|
||||
|
||||
for group in group.get('groups', []):
|
||||
outfile.write("\n")
|
||||
write_md_section(outfile, group, level + 1)
|
||||
|
||||
|
||||
def write_mdfile(outfile, yaml_path, parsed):
|
||||
"""
|
||||
Write the given contents from the parsed YAML file as markdown
|
||||
documentation to the given file
|
||||
|
||||
:param outfile: The file to write the documentation to
|
||||
:type outfile: file
|
||||
:param yaml_path: Path to the source YAML file
|
||||
:type yaml_path: str
|
||||
:param parsed: The parsed YAML file contents
|
||||
:type parsed: dict
|
||||
"""
|
||||
outfile.write(f"""\
|
||||
# List of Features (Features as Build System Enties)
|
||||
<!--
|
||||
WARNING: This has been auto-generated from {yaml_path}.
|
||||
Do not edit this by hand, but update {yaml_path} instead.
|
||||
Finally, run `make generate-features` in the root of the RIOT repo.
|
||||
-->
|
||||
|
||||
[TOC]
|
||||
|
||||
""")
|
||||
write_md_section(outfile, parsed, 0)
|
||||
|
||||
|
||||
def convert_features(yaml_file, mk_file, md_file):
|
||||
"""
|
||||
Convert the YAML file identified by the given path to a Makefile and
|
||||
to a markdown file, if their paths are given.
|
||||
|
||||
:param yaml_file: Path to the YAML file to read
|
||||
:type yaml_file: str
|
||||
:param mk_file: Path to the Makefile to write the features to or None
|
||||
for not writing the Makefile
|
||||
:type mk_file: str or None
|
||||
:param md_file: Path to the markdown file to write the doc to or None
|
||||
for not writing the doc
|
||||
:type md_file: str or None
|
||||
"""
|
||||
with open(yaml_file, 'rb') as file:
|
||||
parsed = yaml.safe_load(file)
|
||||
|
||||
if mk_file is not None:
|
||||
with open(mk_file, 'w', encoding="utf-8") as file:
|
||||
write_makefile(file, yaml_file, parsed)
|
||||
|
||||
if md_file is not None:
|
||||
with open(md_file, 'w', encoding="utf-8") as file:
|
||||
write_mdfile(file, yaml_file, parsed)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate documentation for features in markdown ' +
|
||||
'format and a Makefile listing existing features'
|
||||
)
|
||||
parser.add_argument('INPUT', type=str, default=None,
|
||||
help="Input file in YAML format")
|
||||
parser.add_argument('--output-md', type=str, default=None,
|
||||
help="Output file to write the markdown " +
|
||||
"documentation to (default: no documentation")
|
||||
parser.add_argument('--output-makefile', type=str, default=None,
|
||||
help="Output file to write the makefile to " +
|
||||
"(default: no makefile generated)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
convert_features(args.INPUT, args.output_makefile, args.output_md)
|
||||
Loading…
x
Reference in New Issue
Block a user