summaryrefslogtreecommitdiff
path: root/zephyr/zmake/zmake/generate_readme.py
blob: f309a104ba034ec31f9e5677ece9cf19f7f59cae (plain)
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
# Copyright 2022 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Code for auto-generating README.md."""

import argparse
import io


class MarkdownHelpFormatter(argparse.HelpFormatter):
    """Callbacks to format help output as Markdown."""

    def __init__(self, prog):
        self._prog = prog
        self._section_title = None
        self._section_contents = []
        self._paragraphs = []
        super().__init__(prog=prog)

    def add_text(self, text):
        if text and text is not argparse.SUPPRESS:
            lst = self._paragraphs
            if self._section_title:
                lst = self._section_contents
            lst.append(text)

    def start_section(self, heading):
        self._section_title = heading.title()
        self._section_contents = []

    def end_section(self):
        if self._section_contents:
            self._paragraphs.append(f"#### {self._section_title}")
            self._paragraphs.extend(self._section_contents)
        self._section_title = None

    def add_usage(self, usage, actions, groups, prefix=None):
        if not usage:
            usage = self._prog
        self.add_text(
            f"**Usage:** `{usage} {self._format_actions_usage(actions, groups)}`"
        )

    def add_arguments(self, actions):
        def _get_metavar(action):
            return action.metavar or action.dest

        def _format_invocation(action):
            if action.option_strings:
                parts = []
                for option_string in action.option_strings:
                    if action.nargs == 0:
                        parts.append(option_string)
                    else:
                        parts.append(
                            f"{option_string} {_get_metavar(action).upper()}"
                        )
                return ", ".join(f"`{part}`" for part in parts)
            return f"`{_get_metavar(action)}`"

        def _get_table_line(action):
            return f"| {_format_invocation(action)} | {action.help} |"

        table_lines = [
            "|   |   |",
            "|---|---|",
            *(
                _get_table_line(action)
                for action in actions
                if action.help is not argparse.SUPPRESS
            ),
        ]

        # Don't want a table with no rows.
        if len(table_lines) > 2:
            self.add_text("\n".join(table_lines))

    def format_help(self):
        return "\n\n".join(self._paragraphs)


def generate_readme():
    """Generate the README.md file.

    Returns:
        A string with the README contents.
    """
    # Deferred import position to avoid circular dependency.
    # Normally, this would not be required, since we don't use from
    # imports.  But runpy's import machinery essentially does the
    # equivalent of a from import on __main__.py.
    import zmake.__main__  # pylint: disable=import-outside-toplevel

    output = io.StringIO()
    parser, sub_action = zmake.__main__.get_argparser()

    def _append(*args, **kwargs):
        kwargs.setdefault("file", output)
        print(*args, **kwargs)

    def _append_argparse_help(parser):
        parser.formatter_class = MarkdownHelpFormatter
        _append(parser.format_help())

    _append("# Zmake")
    _append()
    _append(
        '<!-- Auto-generated contents!  Run "zmake generate-readme" to update. -->'
    )
    _append()
    _append("[TOC]")
    _append()
    _append("## Usage")
    _append()
    _append_argparse_help(parser)
    _append()
    _append("## Subcommands")

    for sub_name, sub_parser in sub_action.choices.items():
        _append()
        _append(f"### zmake {sub_name}")
        _append()
        _append_argparse_help(sub_parser)

    return output.getvalue()