xref: /openbsd-src/gnu/llvm/libcxx/utils/generate_header_tests.py (revision 4bdff4bed0e3d54e55670334c7d0077db4170f86)
176d0caaeSpatrick#!/usr/bin/env python
276d0caaeSpatrick
3*4bdff4beSrobertimport contextlib
476d0caaeSpatrickimport glob
5*4bdff4beSrobertimport io
676d0caaeSpatrickimport os
7*4bdff4beSrobertimport pathlib
876d0caaeSpatrickimport re
976d0caaeSpatrick
10*4bdff4beSrobertheader_restrictions = {
11*4bdff4beSrobert    "barrier": "!defined(_LIBCPP_HAS_NO_THREADS)",
12*4bdff4beSrobert    "future": "!defined(_LIBCPP_HAS_NO_THREADS)",
13*4bdff4beSrobert    "latch": "!defined(_LIBCPP_HAS_NO_THREADS)",
14*4bdff4beSrobert    "mutex": "!defined(_LIBCPP_HAS_NO_THREADS)",
15*4bdff4beSrobert    "semaphore": "!defined(_LIBCPP_HAS_NO_THREADS)",
16*4bdff4beSrobert    "shared_mutex": "!defined(_LIBCPP_HAS_NO_THREADS)",
17*4bdff4beSrobert    "stdatomic.h": "__cplusplus > 202002L && !defined(_LIBCPP_HAS_NO_THREADS)",
18*4bdff4beSrobert    "thread": "!defined(_LIBCPP_HAS_NO_THREADS)",
1976d0caaeSpatrick
20*4bdff4beSrobert    "filesystem": "!defined(_LIBCPP_HAS_NO_FILESYSTEM_LIBRARY)",
2176d0caaeSpatrick
22*4bdff4beSrobert    # TODO LLVM17: simplify this to __cplusplus >= 202002L
23*4bdff4beSrobert    "coroutine": "(defined(__cpp_impl_coroutine) && __cpp_impl_coroutine >= 201902L) || (defined(__cpp_coroutines) && __cpp_coroutines >= 201703L)",
2476d0caaeSpatrick
25*4bdff4beSrobert    "clocale": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
26*4bdff4beSrobert    "codecvt": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
27*4bdff4beSrobert    "fstream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_FSTREAM)",
28*4bdff4beSrobert    "iomanip": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
29*4bdff4beSrobert    "ios": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
30*4bdff4beSrobert    "iostream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
31*4bdff4beSrobert    "istream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
32*4bdff4beSrobert    "locale.h": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
33*4bdff4beSrobert    "locale": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
34*4bdff4beSrobert    "ostream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
35*4bdff4beSrobert    "regex": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
36*4bdff4beSrobert    "sstream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
37*4bdff4beSrobert    "streambuf": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
38*4bdff4beSrobert    "strstream": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
3976d0caaeSpatrick
40*4bdff4beSrobert    "wctype.h": "!defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)",
41*4bdff4beSrobert    "cwctype": "!defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)",
42*4bdff4beSrobert    "cwchar": "!defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)",
43*4bdff4beSrobert    "wchar.h": "!defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)",
4476d0caaeSpatrick
45*4bdff4beSrobert    "experimental/algorithm": "__cplusplus >= 201103L",
46*4bdff4beSrobert    "experimental/coroutine": "__cplusplus >= 202002L && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_COROUTINES)",
47*4bdff4beSrobert    "experimental/deque": "__cplusplus >= 201103L",
48*4bdff4beSrobert    "experimental/forward_list": "__cplusplus >= 201103L",
49*4bdff4beSrobert    "experimental/functional": "__cplusplus >= 201103L",
50*4bdff4beSrobert    "experimental/iterator": "__cplusplus >= 201103L",
51*4bdff4beSrobert    "experimental/list": "__cplusplus >= 201103L",
52*4bdff4beSrobert    "experimental/map": "__cplusplus >= 201103L",
53*4bdff4beSrobert    "experimental/memory_resource": "__cplusplus >= 201103L",
54*4bdff4beSrobert    "experimental/propagate_const": "__cplusplus >= 201103L",
55*4bdff4beSrobert    "experimental/regex": "!defined(_LIBCPP_HAS_NO_LOCALIZATION) && __cplusplus >= 201103L",
56*4bdff4beSrobert    "experimental/set": "__cplusplus >= 201103L",
57*4bdff4beSrobert    "experimental/simd": "__cplusplus >= 201103L",
58*4bdff4beSrobert    "experimental/span": "__cplusplus >= 201103L",
59*4bdff4beSrobert    "experimental/string": "__cplusplus >= 201103L",
60*4bdff4beSrobert    "experimental/type_traits": "__cplusplus >= 201103L",
61*4bdff4beSrobert    "experimental/unordered_map": "__cplusplus >= 201103L",
62*4bdff4beSrobert    "experimental/unordered_set": "__cplusplus >= 201103L",
63*4bdff4beSrobert    "experimental/utility": "__cplusplus >= 201103L",
64*4bdff4beSrobert    "experimental/vector": "__cplusplus >= 201103L",
6576d0caaeSpatrick}
6676d0caaeSpatrick
67*4bdff4beSrobertprivate_headers_still_public_in_modules = [
68*4bdff4beSrobert    '__assert', '__bsd_locale_defaults.h', '__bsd_locale_fallbacks.h', '__config',
69*4bdff4beSrobert    '__config_site.in', '__debug', '__hash_table',
70*4bdff4beSrobert    '__threading_support', '__tree', '__undef_macros', '__verbose_abort'
7176d0caaeSpatrick]
7276d0caaeSpatrick
73*4bdff4beSrobertdef find_script(file):
74*4bdff4beSrobert    """Finds the script used to generate a file inside the file itself. The script is delimited by
75*4bdff4beSrobert       BEGIN-SCRIPT and END-SCRIPT markers.
76*4bdff4beSrobert    """
77*4bdff4beSrobert    with open(file, 'r') as f:
7876d0caaeSpatrick        content = f.read()
7976d0caaeSpatrick
80*4bdff4beSrobert    match = re.search(r'^BEGIN-SCRIPT$(.+)^END-SCRIPT$', content, flags=re.MULTILINE | re.DOTALL)
81*4bdff4beSrobert    if not match:
82*4bdff4beSrobert        raise RuntimeError("Was unable to find a script delimited with BEGIN-SCRIPT/END-SCRIPT markers in {}".format(test_file))
83*4bdff4beSrobert    return match.group(1)
8476d0caaeSpatrick
85*4bdff4beSrobertdef execute_script(script, variables):
86*4bdff4beSrobert    """Executes the provided Mako template with the given variables available during the
87*4bdff4beSrobert       evaluation of the script, and returns the result.
88*4bdff4beSrobert    """
89*4bdff4beSrobert    code = compile(script, 'fake-filename', 'exec')
90*4bdff4beSrobert    output = io.StringIO()
91*4bdff4beSrobert    with contextlib.redirect_stdout(output):
92*4bdff4beSrobert        exec(code, variables)
93*4bdff4beSrobert        output = output.getvalue()
94*4bdff4beSrobert    return output
9576d0caaeSpatrick
96*4bdff4beSrobertdef generate_new_file(file, new_content):
97*4bdff4beSrobert    """Generates the new content of the file by inserting the new content in-between
98*4bdff4beSrobert       two '// GENERATED-MARKER' markers located in the file.
99*4bdff4beSrobert    """
100*4bdff4beSrobert    with open(file, 'r') as f:
101*4bdff4beSrobert        old_content = f.read()
10276d0caaeSpatrick
103*4bdff4beSrobert    try:
104*4bdff4beSrobert        before, begin_marker, _, end_marker, after = re.split(r'(// GENERATED-MARKER\n)', old_content, flags=re.MULTILINE | re.DOTALL)
105*4bdff4beSrobert    except ValueError:
106*4bdff4beSrobert        raise RuntimeError("Failed to split {} based on markers, please make sure the file has exactly two '// GENERATED-MARKER' occurrences".format(file))
10776d0caaeSpatrick
108*4bdff4beSrobert    return before + begin_marker + new_content + end_marker + after
109*4bdff4beSrobert
110*4bdff4beSrobertdef produce(test_file, variables):
111*4bdff4beSrobert    script = find_script(test_file)
112*4bdff4beSrobert    result = execute_script(script, variables)
113*4bdff4beSrobert    new_content = generate_new_file(test_file, result)
114*4bdff4beSrobert    with open(test_file, 'w', newline='\n') as f:
115*4bdff4beSrobert        f.write(new_content)
116*4bdff4beSrobert
117*4bdff4beSrobertdef is_header(file):
118*4bdff4beSrobert    """Returns whether the given file is a header (i.e. not a directory or the modulemap file)."""
119*4bdff4beSrobert    return not file.is_dir() and not file.name == 'module.modulemap.in' and file.name != 'libcxx.imp'
12076d0caaeSpatrick
12176d0caaeSpatrick
12276d0caaeSpatrickdef main():
123*4bdff4beSrobert    monorepo_root = pathlib.Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
124*4bdff4beSrobert    include = pathlib.Path(os.path.join(monorepo_root, 'libcxx', 'include'))
125*4bdff4beSrobert    test = pathlib.Path(os.path.join(monorepo_root, 'libcxx', 'test'))
126*4bdff4beSrobert    assert(monorepo_root.exists())
127*4bdff4beSrobert
128*4bdff4beSrobert    toplevel_headers     = sorted(str(p.relative_to(include)) for p in include.glob('[a-z]*') if is_header(p))
129*4bdff4beSrobert    experimental_headers = sorted(str(p.relative_to(include)) for p in include.glob('experimental/[a-z]*') if is_header(p))
130*4bdff4beSrobert    extended_headers     = sorted(str(p.relative_to(include)) for p in include.glob('ext/[a-z]*') if is_header(p))
131*4bdff4beSrobert    public_headers       = toplevel_headers + experimental_headers + extended_headers
132*4bdff4beSrobert    private_headers      = sorted(str(p.relative_to(include)) for p in include.rglob('*') if is_header(p) and str(p.relative_to(include)).startswith('__'))
133*4bdff4beSrobert    variables = {
134*4bdff4beSrobert        'toplevel_headers': toplevel_headers,
135*4bdff4beSrobert        'experimental_headers': experimental_headers,
136*4bdff4beSrobert        'extended_headers': extended_headers,
137*4bdff4beSrobert        'public_headers': public_headers,
138*4bdff4beSrobert        'private_headers': private_headers,
139*4bdff4beSrobert        'header_restrictions': header_restrictions,
140*4bdff4beSrobert        'private_headers_still_public_in_modules': private_headers_still_public_in_modules
141*4bdff4beSrobert    }
142*4bdff4beSrobert
143*4bdff4beSrobert    produce(test.joinpath('libcxx/assertions/headers_declare_verbose_abort.sh.cpp'), variables)
144*4bdff4beSrobert    produce(test.joinpath('libcxx/clang_tidy.sh.cpp'), variables)
145*4bdff4beSrobert    produce(test.joinpath('libcxx/double_include.sh.cpp'), variables)
146*4bdff4beSrobert    produce(test.joinpath('libcxx/min_max_macros.compile.pass.cpp'), variables)
147*4bdff4beSrobert    produce(test.joinpath('libcxx/modules_include.sh.cpp'), variables)
148*4bdff4beSrobert    produce(test.joinpath('libcxx/nasty_macros.compile.pass.cpp'), variables)
149*4bdff4beSrobert    produce(test.joinpath('libcxx/no_assert_include.compile.pass.cpp'), variables)
150*4bdff4beSrobert    produce(test.joinpath('libcxx/private_headers.verify.cpp'), variables)
151*4bdff4beSrobert    produce(test.joinpath('libcxx/transitive_includes.sh.cpp'), variables)
15276d0caaeSpatrick
15376d0caaeSpatrick
15476d0caaeSpatrickif __name__ == '__main__':
15576d0caaeSpatrick    main()
156