File: vendor_boost_preprocessor.py

package info (click to toggle)
gridtools 2.3.9-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 29,480 kB
  • sloc: cpp: 228,792; python: 17,561; javascript: 9,164; ansic: 4,101; sh: 850; makefile: 231; f90: 201
file content (124 lines) | stat: -rwxr-xr-x 3,796 bytes parent folder | download
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
#!/usr/bin/env python3

import os
import shutil
import argparse
import re


def extract_transitive_includes(header_path):
    includes = set()

    with open(header_path, "r", encoding="utf-8") as f:
        for line in f:
            # NOTE: This also has cover files included indirectly through macro
            # definitions, e.g.: #define PP1 <boost/preprocessor/iterate.hpp>,
            # so there's no "#include" in the pattern.
            match = re.search(r'[<"]?(boost/[^">]+)[">]', line)
            if match:
                includes.add(match.group(1))

    return includes


def find_headers(input_dir, initial_headers):
    all_headers = set(initial_headers)
    queue = list(initial_headers)

    while queue:
        current_header = queue.pop()
        current_path = os.path.join(input_dir, current_header)

        includes = extract_transitive_includes(current_path)

        for include in includes:
            if include not in all_headers:
                all_headers.add(include)
                queue.append(include)

    return all_headers


def vendor_header(input_path, output_path, macro_prefix, include_prefix):
    with open(input_path, "r", encoding="utf-8") as f:
        content = f.read()

    # Replace BOOST_* macros. NOTE: The pattern has to be able to match multiple
    # define BOOST_PP_NOT_EQUAL_CHECK_BOOST_PP_NOT_EQUAL_513(c, y) 0
    macro_prefix_upper = macro_prefix.upper()
    content = re.sub(r"BOOST_([A-Z0-9]+)", rf"{macro_prefix_upper}_\1", content)

    # Replace boost/* includes
    content = re.sub(r'(\s+[<"]?)boost/', rf"\1{include_prefix}/", content)

    with open(output_path, "w", encoding="utf-8") as f:
        f.write(content)


def vendor_headers(input_dir, output_dir, headers, macro_prefix, include_prefix):
    os.makedirs(output_dir, exist_ok=True)

    for header in headers:
        # All includes are assumed to be relative to the Boost.Preprocessor
        # input directory
        input_path = os.path.join(input_dir, header)
        output_path = os.path.join(
            output_dir, header.replace("boost", include_prefix, 1)
        )

        # Ensure the output directory structure exists
        os.makedirs(os.path.dirname(output_path), exist_ok=True)

        vendor_header(input_path, output_path, macro_prefix, include_prefix)


def main():
    parser = argparse.ArgumentParser(description="Vendor Boost.Preprocessor.")

    parser.add_argument(
        "-i",
        "--input-dir",
        required=True,
        help="Path to the include directory of Boost.Preprocessor.",
    )
    parser.add_argument(
        "-o",
        "--output-dir",
        required=True,
        help="Path to the include directory where Boost.Preprocessor is vendored.",
    )
    parser.add_argument(
        "-e",
        "--headers",
        nargs="+",
        required=True,
        help="List of headers from Boost.Preprocessor to include. Transitive includes are detected from this list.",
    )
    parser.add_argument(
        "--macro-prefix",
        required=True,
        help="Prefix to use for replacing BOOST_PP in macros.",
    )
    parser.add_argument(
        "--include-prefix",
        required=True,
        help="Prefix to use for include directories of the vendored Boost.Preprocessor. Files will be copied to this prefix under the output directory, and includes changed to use this prefix.",
    )

    args = parser.parse_args()

    # Find all files in Boost.Preprocessor given the list of headers, including
    # transitive dependencies
    all_headers = find_headers(args.input_dir, set(args.headers))

    vendor_headers(
        args.input_dir,
        args.output_dir,
        all_headers,
        args.macro_prefix,
        args.include_prefix,
    )


if __name__ == "__main__":
    main()