xref: /netbsd-src/external/apache2/llvm/dist/libcxx/utils/generate_header_tests.py (revision 4d6fc14bc9b0c5bf3e30be318c143ee82cadd108)
1#!/usr/bin/env python
2
3import glob
4import os
5import posixpath
6import re
7
8
9def get_libcxx_paths():
10    utils_path = os.path.dirname(os.path.abspath(__file__))
11    script_name = os.path.basename(__file__)
12    assert os.path.exists(utils_path)
13    src_root = os.path.dirname(utils_path)
14    include_path = os.path.join(src_root, 'include')
15    assert os.path.exists(include_path)
16    libcxx_test_path = os.path.join(src_root, 'test', 'libcxx')
17    assert os.path.exists(libcxx_test_path)
18    return script_name, src_root, include_path, libcxx_test_path
19
20
21script_name, source_root, include_path, libcxx_test_path = get_libcxx_paths()
22
23header_markup = {
24    "atomic": ["ifndef _LIBCPP_HAS_NO_THREADS"],
25    "barrier": ["ifndef _LIBCPP_HAS_NO_THREADS"],
26    "future": ["ifndef _LIBCPP_HAS_NO_THREADS"],
27    "latch": ["ifndef _LIBCPP_HAS_NO_THREADS"],
28    "mutex": ["ifndef _LIBCPP_HAS_NO_THREADS"],
29    "shared_mutex": ["ifndef _LIBCPP_HAS_NO_THREADS"],
30    "semaphore": ["ifndef _LIBCPP_HAS_NO_THREADS"],
31    "thread": ["ifndef _LIBCPP_HAS_NO_THREADS"],
32
33    "filesystem": ["ifndef _LIBCPP_HAS_NO_FILESYSTEM_LIBRARY"],
34    "experimental/filesystem": ["ifndef _LIBCPP_HAS_NO_FILESYSTEM_LIBRARY"],
35
36    "clocale": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"],
37    "codecvt": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"],
38    "fstream": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"],
39    "iomanip": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"],
40    "ios": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"],
41    "iostream": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"],
42    "istream": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"],
43    "locale": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"],
44    "locale.h": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"],
45    "ostream": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"],
46    "regex": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"],
47    "sstream": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"],
48    "streambuf": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"],
49    "strstream": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"],
50
51    "experimental/coroutine": ["if defined(__cpp_coroutines)"],
52    "experimental/regex": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"],
53}
54
55allowed_extensions = ['', '.h']
56indent_width = 4
57
58
59begin_pattern = """\
60////////////////////////////////////////////////////////////////////////////////
61// BEGIN-GENERATED-HEADERS
62////////////////////////////////////////////////////////////////////////////////
63"""
64
65warning_note = """\
66// WARNING: This test was generated by {script_name}
67// and should not be edited manually.
68
69""".format(script_name=script_name)
70
71end_pattern = """\
72////////////////////////////////////////////////////////////////////////////////
73// END-GENERATED-HEADERS
74////////////////////////////////////////////////////////////////////////////////
75"""
76
77generated_part_pattern = re.compile(re.escape(begin_pattern) + ".*" + re.escape(end_pattern),
78                                    re.MULTILINE | re.DOTALL)
79
80headers_template = """\
81// Top level headers
82{top_level_headers}
83
84// experimental headers
85#if __cplusplus >= 201103L
86{experimental_headers}
87#endif // __cplusplus >= 201103L
88
89// extended headers
90{extended_headers}
91"""
92
93
94def should_keep_header(p, exclusions=None):
95    if os.path.isdir(p):
96        return False
97
98    if exclusions:
99        relpath = os.path.relpath(p, include_path)
100        relpath = posixpath.join(*os.path.split(relpath))
101        if relpath in exclusions:
102            return False
103
104    return os.path.splitext(p)[1] in allowed_extensions
105
106
107def produce_include(relpath, indent_level, post_include=None):
108    relpath = posixpath.join(*os.path.split(relpath))
109    template = "{preambule}#{indentation}include <{include}>{post_include}{postambule}"
110
111    base_indentation = ' '*(indent_width * indent_level)
112    next_indentation = base_indentation + ' '*(indent_width)
113    post_include = "\n{}".format(post_include) if post_include else ''
114
115    markup = header_markup.get(relpath, None)
116    if markup:
117        preambule = '#{indentation}{directive}\n'.format(
118            directive=markup[0],
119            indentation=base_indentation,
120        )
121        postambule = '\n#{indentation}endif'.format(
122            indentation=base_indentation,
123        )
124        indentation = next_indentation
125    else:
126        preambule = ''
127        postambule = ''
128        indentation = base_indentation
129
130    return template.format(
131        include=relpath,
132        post_include=post_include,
133        preambule=preambule,
134        postambule=postambule,
135        indentation=indentation,
136    )
137
138
139def produce_headers(path_parts, indent_level, post_include=None, exclusions=None):
140    pattern = os.path.join(*path_parts, '[a-z]*')
141
142    files = sorted(glob.glob(pattern, recursive=False))
143
144    include_headers = [
145        produce_include(os.path.relpath(p, include_path),
146                        indent_level, post_include=post_include)
147        for p in files
148        if should_keep_header(p, exclusions)
149    ]
150
151    return '\n'.join(include_headers)
152
153
154def produce_top_level_headers(post_include=None, exclusions=None):
155    return produce_headers([include_path], 0, post_include=post_include, exclusions=exclusions)
156
157
158def produce_experimental_headers(post_include=None, exclusions=None):
159    return produce_headers([include_path, 'experimental'], 1, post_include=post_include, exclusions=exclusions)
160
161
162def produce_extended_headers(post_include=None, exclusions=None):
163    return produce_headers([include_path, 'ext'], 0, post_include=post_include, exclusions=exclusions)
164
165
166def replace_generated_headers(test_path, test_str):
167    with open(test_path, 'r') as f:
168        content = f.read()
169
170    preambule = begin_pattern + '\n// clang-format off\n\n' + warning_note
171    postambule = '\n// clang-format on\n\n' + end_pattern
172    content = generated_part_pattern.sub(
173        preambule + test_str + postambule, content)
174
175    with open(test_path, 'w', newline='\n') as f:
176        f.write(content)
177
178
179def produce_test(test_filename, exclusions=None, post_include=None):
180    test_str = headers_template.format(
181        top_level_headers=produce_top_level_headers(
182            post_include=post_include,
183            exclusions=exclusions,
184        ),
185        experimental_headers=produce_experimental_headers(
186            post_include=post_include,
187        ),
188        extended_headers=produce_extended_headers(
189            post_include=post_include,
190        ),
191    )
192
193    replace_generated_headers(os.path.join(
194        libcxx_test_path, test_filename), test_str)
195
196
197def main():
198    produce_test('double_include.sh.cpp')
199    produce_test('min_max_macros.compile.pass.cpp',
200                 post_include='TEST_MACROS();')
201    produce_test('no_assert_include.compile.pass.cpp',
202                 exclusions=['cassert'])
203
204
205if __name__ == '__main__':
206    main()
207