| #!/usr/bin/env python3 | |
| # A tool to parse the output of `llvm-bolt --help-hidden` and update the | |
| # documentation in CommandLineArgumentReference.md automatically. | |
| # Run from the directory in which this file is located to update the docs. | |
| import subprocess | |
| from textwrap import wrap | |
| LINE_LIMIT = 80 | |
| def wrap_text(text, indent, limit=LINE_LIMIT): | |
| wrapped_lines = wrap(text, width=limit - len(indent)) | |
| wrapped_text = ("\n" + indent).join(wrapped_lines) | |
| return wrapped_text | |
| def add_info(sections, section, option, description): | |
| indent = " " | |
| wrapped_description = "\n".join( | |
| [ | |
| wrap_text(line, indent) if len(line) > LINE_LIMIT else line | |
| for line in description | |
| ] | |
| ) | |
| sections[section].append((option, indent + wrapped_description)) | |
| def parse_bolt_options(output): | |
| section_headers = [ | |
| "Generic options:", | |
| "Output options:", | |
| "BOLT generic options:", | |
| "BOLT optimization options:", | |
| "BOLT options in relocation mode:", | |
| "BOLT instrumentation options:", | |
| "BOLT printing options:", | |
| ] | |
| sections = {key: [] for key in section_headers} | |
| current_section, prev_section = None, None | |
| option, description = None, [] | |
| for line in output.split("\n"): | |
| cleaned_line = line.strip() | |
| if cleaned_line.casefold() in map(str.casefold, section_headers): | |
| if prev_section is not None: # Save last option from prev section | |
| add_info(sections, current_section, option, description) | |
| option, description = None, [] | |
| cleaned_line = cleaned_line.split() | |
| # Apply lowercase to all words except the first one | |
| cleaned_line = [cleaned_line[0]] + [ | |
| word.lower() for word in cleaned_line[1:] | |
| ] | |
| # Join the words back together into a string | |
| cleaned_line = " ".join(cleaned_line) | |
| current_section = cleaned_line | |
| prev_section = current_section | |
| continue | |
| if cleaned_line.startswith("-"): | |
| if option and description: | |
| # Join description lines, adding an extra newline for | |
| # sub-options that start with '=' | |
| add_info(sections, current_section, option, description) | |
| option, description = None, [] | |
| parts = cleaned_line.split(" ", 1) | |
| if len(parts) > 1: | |
| option = parts[0].strip() | |
| descr = parts[1].strip() | |
| descr = descr[2].upper() + descr[3:] | |
| description = [descr] | |
| if option.startswith("--print") or option.startswith("--time"): | |
| current_section = "BOLT printing options:" | |
| elif prev_section is not None: | |
| current_section = prev_section | |
| continue | |
| if cleaned_line.startswith("="): | |
| parts = cleaned_line.split(maxsplit=1) | |
| # Split into two parts: sub-option and description | |
| if len(parts) == 2: | |
| # Rejoin with a single space | |
| cleaned_line = parts[0] + " " + parts[1].rstrip() | |
| description.append(cleaned_line) | |
| elif cleaned_line: # Multiline description continuation | |
| description.append(cleaned_line) | |
| add_info(sections, current_section, option, description) | |
| return sections | |
| def generate_markdown(sections): | |
| markdown_lines = [ | |
| "# BOLT - a post-link optimizer developed to speed up large applications\n", | |
| "## SYNOPSIS\n", | |
| "`llvm-bolt <executable> [-o outputfile] <executable>.bolt " | |
| "[-data=perf.fdata] [options]`\n", | |
| "## OPTIONS", | |
| ] | |
| for section, options in sections.items(): | |
| markdown_lines.append(f"\n### {section}") | |
| if section == "BOLT instrumentation options:": | |
| markdown_lines.append( | |
| f"\n`llvm-bolt <executable> -instrument" | |
| " [-o outputfile] <instrumented-executable>`" | |
| ) | |
| for option, desc in options: | |
| markdown_lines.append(f"\n- `{option}`\n") | |
| # Split description into lines to handle sub-options | |
| desc_lines = desc.split("\n") | |
| for line in desc_lines: | |
| if line.startswith("="): | |
| # Sub-option: correct formatting with bullet | |
| sub_option, sub_desc = line[1:].split(" ", 1) | |
| markdown_lines.append(f" - `{sub_option}`: {sub_desc[4:]}") | |
| else: | |
| # Regular line of description | |
| if line[2:].startswith("<"): | |
| line = line.replace("<", "").replace(">", "") | |
| markdown_lines.append(f"{line}") | |
| return "\n".join(markdown_lines) | |
| def main(): | |
| try: | |
| help_output = subprocess.run( | |
| ["llvm-bolt", "--help-hidden"], capture_output=True, text=True, check=True | |
| ).stdout | |
| except subprocess.CalledProcessError as e: | |
| print("Failed to execute llvm-bolt --help:") | |
| print(e) | |
| return | |
| sections = parse_bolt_options(help_output) | |
| markdown = generate_markdown(sections) | |
| with open("CommandLineArgumentReference.md", "w") as md_file: | |
| md_file.write(markdown) | |
| if __name__ == "__main__": | |
| main() |