xref: /llvm-project/bolt/docs/generate_doc.py (revision 8bc02bf5c6e94489a79c8d924e5d9866fcc18417)
1765ce869SElvina Yakubova#!/usr/bin/env python3
2765ce869SElvina Yakubova# A tool to parse the output of `llvm-bolt --help-hidden` and update the
3765ce869SElvina Yakubova# documentation in CommandLineArgumentReference.md automatically.
4765ce869SElvina Yakubova# Run from the directory in which this file is located to update the docs.
5765ce869SElvina Yakubova
6765ce869SElvina Yakubovaimport subprocess
7765ce869SElvina Yakubovafrom textwrap import wrap
8765ce869SElvina Yakubova
9765ce869SElvina YakubovaLINE_LIMIT = 80
10765ce869SElvina Yakubova
11765ce869SElvina Yakubova
12765ce869SElvina Yakubovadef wrap_text(text, indent, limit=LINE_LIMIT):
13765ce869SElvina Yakubova    wrapped_lines = wrap(text, width=limit - len(indent))
14765ce869SElvina Yakubova    wrapped_text = ("\n" + indent).join(wrapped_lines)
15765ce869SElvina Yakubova    return wrapped_text
16765ce869SElvina Yakubova
17765ce869SElvina Yakubova
18765ce869SElvina Yakubovadef add_info(sections, section, option, description):
19765ce869SElvina Yakubova    indent = "  "
20765ce869SElvina Yakubova    wrapped_description = "\n".join(
21765ce869SElvina Yakubova        [
22765ce869SElvina Yakubova            wrap_text(line, indent) if len(line) > LINE_LIMIT else line
23765ce869SElvina Yakubova            for line in description
24765ce869SElvina Yakubova        ]
25765ce869SElvina Yakubova    )
26765ce869SElvina Yakubova    sections[section].append((option, indent + wrapped_description))
27765ce869SElvina Yakubova
28765ce869SElvina Yakubova
29765ce869SElvina Yakubovadef parse_bolt_options(output):
30765ce869SElvina Yakubova    section_headers = [
31765ce869SElvina Yakubova        "Generic options:",
32765ce869SElvina Yakubova        "Output options:",
33765ce869SElvina Yakubova        "BOLT generic options:",
34765ce869SElvina Yakubova        "BOLT optimization options:",
35765ce869SElvina Yakubova        "BOLT options in relocation mode:",
36765ce869SElvina Yakubova        "BOLT instrumentation options:",
37765ce869SElvina Yakubova        "BOLT printing options:",
38765ce869SElvina Yakubova    ]
39765ce869SElvina Yakubova
40765ce869SElvina Yakubova    sections = {key: [] for key in section_headers}
41765ce869SElvina Yakubova    current_section, prev_section = None, None
42765ce869SElvina Yakubova    option, description = None, []
43765ce869SElvina Yakubova
44765ce869SElvina Yakubova    for line in output.split("\n"):
45765ce869SElvina Yakubova        cleaned_line = line.strip()
46765ce869SElvina Yakubova
47765ce869SElvina Yakubova        if cleaned_line.casefold() in map(str.casefold, section_headers):
48*8bc02bf5SEisuke Kawashima            if prev_section is not None:  # Save last option from prev section
49765ce869SElvina Yakubova                add_info(sections, current_section, option, description)
50765ce869SElvina Yakubova                option, description = None, []
51765ce869SElvina Yakubova
52765ce869SElvina Yakubova            cleaned_line = cleaned_line.split()
53765ce869SElvina Yakubova            # Apply lowercase to all words except the first one
54765ce869SElvina Yakubova            cleaned_line = [cleaned_line[0]] + [
55765ce869SElvina Yakubova                word.lower() for word in cleaned_line[1:]
56765ce869SElvina Yakubova            ]
57765ce869SElvina Yakubova            # Join the words back together into a string
58765ce869SElvina Yakubova            cleaned_line = " ".join(cleaned_line)
59765ce869SElvina Yakubova
60765ce869SElvina Yakubova            current_section = cleaned_line
61765ce869SElvina Yakubova            prev_section = current_section
62765ce869SElvina Yakubova            continue
63765ce869SElvina Yakubova
64765ce869SElvina Yakubova        if cleaned_line.startswith("-"):
65765ce869SElvina Yakubova            if option and description:
66765ce869SElvina Yakubova                # Join description lines, adding an extra newline for
67765ce869SElvina Yakubova                # sub-options that start with '='
68765ce869SElvina Yakubova                add_info(sections, current_section, option, description)
69765ce869SElvina Yakubova                option, description = None, []
70765ce869SElvina Yakubova
71765ce869SElvina Yakubova            parts = cleaned_line.split("  ", 1)
72765ce869SElvina Yakubova            if len(parts) > 1:
73765ce869SElvina Yakubova                option = parts[0].strip()
74765ce869SElvina Yakubova                descr = parts[1].strip()
75765ce869SElvina Yakubova                descr = descr[2].upper() + descr[3:]
76765ce869SElvina Yakubova                description = [descr]
77765ce869SElvina Yakubova                if option.startswith("--print") or option.startswith("--time"):
78765ce869SElvina Yakubova                    current_section = "BOLT printing options:"
79*8bc02bf5SEisuke Kawashima                elif prev_section is not None:
80765ce869SElvina Yakubova                    current_section = prev_section
81765ce869SElvina Yakubova            continue
82765ce869SElvina Yakubova
83765ce869SElvina Yakubova        if cleaned_line.startswith("="):
84765ce869SElvina Yakubova            parts = cleaned_line.split(maxsplit=1)
85765ce869SElvina Yakubova            # Split into two parts: sub-option and description
86765ce869SElvina Yakubova            if len(parts) == 2:
87765ce869SElvina Yakubova                # Rejoin with a single space
88765ce869SElvina Yakubova                cleaned_line = parts[0] + " " + parts[1].rstrip()
89765ce869SElvina Yakubova            description.append(cleaned_line)
90765ce869SElvina Yakubova        elif cleaned_line:  # Multiline description continuation
91765ce869SElvina Yakubova            description.append(cleaned_line)
92765ce869SElvina Yakubova
93765ce869SElvina Yakubova    add_info(sections, current_section, option, description)
94765ce869SElvina Yakubova    return sections
95765ce869SElvina Yakubova
96765ce869SElvina Yakubova
97765ce869SElvina Yakubovadef generate_markdown(sections):
98765ce869SElvina Yakubova    markdown_lines = [
99765ce869SElvina Yakubova        "# BOLT - a post-link optimizer developed to speed up large applications\n",
100765ce869SElvina Yakubova        "## SYNOPSIS\n",
101765ce869SElvina Yakubova        "`llvm-bolt <executable> [-o outputfile] <executable>.bolt "
102765ce869SElvina Yakubova        "[-data=perf.fdata] [options]`\n",
103765ce869SElvina Yakubova        "## OPTIONS",
104765ce869SElvina Yakubova    ]
105765ce869SElvina Yakubova
106765ce869SElvina Yakubova    for section, options in sections.items():
107765ce869SElvina Yakubova        markdown_lines.append(f"\n### {section}")
108765ce869SElvina Yakubova        if section == "BOLT instrumentation options:":
109765ce869SElvina Yakubova            markdown_lines.append(
110765ce869SElvina Yakubova                f"\n`llvm-bolt <executable> -instrument"
111765ce869SElvina Yakubova                " [-o outputfile] <instrumented-executable>`"
112765ce869SElvina Yakubova            )
113765ce869SElvina Yakubova        for option, desc in options:
114765ce869SElvina Yakubova            markdown_lines.append(f"\n- `{option}`\n")
115765ce869SElvina Yakubova            # Split description into lines to handle sub-options
116765ce869SElvina Yakubova            desc_lines = desc.split("\n")
117765ce869SElvina Yakubova            for line in desc_lines:
118765ce869SElvina Yakubova                if line.startswith("="):
119765ce869SElvina Yakubova                    # Sub-option: correct formatting with bullet
120765ce869SElvina Yakubova                    sub_option, sub_desc = line[1:].split(" ", 1)
121765ce869SElvina Yakubova                    markdown_lines.append(f"  - `{sub_option}`: {sub_desc[4:]}")
122765ce869SElvina Yakubova                else:
123765ce869SElvina Yakubova                    # Regular line of description
124765ce869SElvina Yakubova                    if line[2:].startswith("<"):
125765ce869SElvina Yakubova                        line = line.replace("<", "").replace(">", "")
126765ce869SElvina Yakubova                    markdown_lines.append(f"{line}")
127765ce869SElvina Yakubova
128765ce869SElvina Yakubova    return "\n".join(markdown_lines)
129765ce869SElvina Yakubova
130765ce869SElvina Yakubova
131765ce869SElvina Yakubovadef main():
132765ce869SElvina Yakubova    try:
133765ce869SElvina Yakubova        help_output = subprocess.run(
134765ce869SElvina Yakubova            ["llvm-bolt", "--help-hidden"], capture_output=True, text=True, check=True
135765ce869SElvina Yakubova        ).stdout
136765ce869SElvina Yakubova    except subprocess.CalledProcessError as e:
137765ce869SElvina Yakubova        print("Failed to execute llvm-bolt --help:")
138765ce869SElvina Yakubova        print(e)
139765ce869SElvina Yakubova        return
140765ce869SElvina Yakubova
141765ce869SElvina Yakubova    sections = parse_bolt_options(help_output)
142765ce869SElvina Yakubova    markdown = generate_markdown(sections)
143765ce869SElvina Yakubova
144765ce869SElvina Yakubova    with open("CommandLineArgumentReference.md", "w") as md_file:
145765ce869SElvina Yakubova        md_file.write(markdown)
146765ce869SElvina Yakubova
147765ce869SElvina Yakubova
148765ce869SElvina Yakubovaif __name__ == "__main__":
149765ce869SElvina Yakubova    main()
150