xref: /llvm-project/libc/utils/hdrgen/main.py (revision cdbba15c6cd53291358bf95a9a9057042fcbf163)
1#!/usr/bin/env python3
2#
3# ===- Generate headers for libc functions  ------------------*- python -*--==#
4#
5# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
6# See https://llvm.org/LICENSE.txt for license information.
7# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8#
9# ==------------------------------------------------------------------------==#
10
11import argparse
12import sys
13from pathlib import Path
14
15from header import HeaderFile
16from yaml_to_classes import load_yaml_file, fill_public_api
17
18
19def main():
20    parser = argparse.ArgumentParser(description="Generate header files from YAML")
21    parser.add_argument(
22        "yaml_file",
23        help="Path to the YAML file containing header specification",
24        metavar="FILE",
25        type=Path,
26        nargs=1,
27    )
28    parser.add_argument(
29        "-o",
30        "--output",
31        help="Path to write generated header file",
32        type=Path,
33        required=True,
34    )
35    parser.add_argument(
36        "--depfile",
37        help="Path to write a depfile",
38        type=Path,
39    )
40    parser.add_argument(
41        "--write-if-changed",
42        help="Write the output file only if its contents have changed",
43        action="store_true",
44        default=False,
45    )
46    parser.add_argument(
47        "-e",
48        "--entry-point",
49        help="Entry point to include; may be given many times",
50        metavar="SYMBOL",
51        action="append",
52    )
53    args = parser.parse_args()
54
55    [yaml_file] = args.yaml_file
56    files_read = {yaml_file}
57
58    def write_depfile():
59        if not args.depfile:
60            return
61        deps = " ".join(str(f) for f in sorted(files_read))
62        args.depfile.parent.mkdir(parents=True, exist_ok=True)
63        with open(args.depfile, "w") as depfile:
64            depfile.write(f"{args.output}: {deps}\n")
65
66    header = load_yaml_file(yaml_file, HeaderFile, args.entry_point)
67
68    if not header.template_file:
69        print(f"{yaml_file}: Missing header_template", sys.stderr)
70        return 2
71
72    # The header_template path is relative to the containing YAML file.
73    template_path = yaml_file.parent / header.template_file
74
75    files_read.add(template_path)
76    with open(template_path) as template:
77        contents = fill_public_api(header.public_api(), template.read())
78
79    write_depfile()
80
81    if (
82        not args.write_if_changed
83        or not args.output.exists()
84        or args.output.read_text() != contents
85    ):
86        args.output.parent.mkdir(parents=True, exist_ok=True)
87        args.output.write_text(contents)
88
89
90if __name__ == "__main__":
91    sys.exit(main())
92