xref: /openbsd-src/gnu/llvm/libcxx/utils/generate_header_inclusion_tests.py (revision 4bdff4bed0e3d54e55670334c7d0077db4170f86)
176d0caaeSpatrick#!/usr/bin/env python
276d0caaeSpatrick
376d0caaeSpatrickimport os
476d0caaeSpatrick
576d0caaeSpatrick
676d0caaeSpatrickdef get_libcxx_paths():
776d0caaeSpatrick    utils_path = os.path.dirname(os.path.abspath(__file__))
876d0caaeSpatrick    script_name = os.path.basename(__file__)
976d0caaeSpatrick    assert os.path.exists(utils_path)
1076d0caaeSpatrick    src_root = os.path.dirname(utils_path)
1176d0caaeSpatrick    test_path = os.path.join(src_root, 'test', 'libcxx', 'inclusions')
1276d0caaeSpatrick    assert os.path.exists(test_path)
1376d0caaeSpatrick    assert os.path.exists(os.path.join(test_path, 'algorithm.inclusions.compile.pass.cpp'))
1476d0caaeSpatrick    return script_name, src_root, test_path
1576d0caaeSpatrick
1676d0caaeSpatrick
1776d0caaeSpatrickscript_name, source_root, test_path = get_libcxx_paths()
1876d0caaeSpatrick
1976d0caaeSpatrick
2076d0caaeSpatrick# This table was produced manually, by grepping the TeX source of the Standard's
2176d0caaeSpatrick# library clauses for the string "#include". Each header's synopsis contains
2276d0caaeSpatrick# explicit "#include" directives for its mandatory inclusions.
2376d0caaeSpatrick# For example, [algorithm.syn] contains "#include <initializer_list>".
2476d0caaeSpatrick#
2576d0caaeSpatrickmandatory_inclusions = {
2676d0caaeSpatrick    "algorithm": ["initializer_list"],
2776d0caaeSpatrick    "array": ["compare", "initializer_list"],
2876d0caaeSpatrick    "bitset": ["iosfwd", "string"],
2976d0caaeSpatrick    "chrono": ["compare"],
3076d0caaeSpatrick    "cinttypes": ["cstdint"],
3176d0caaeSpatrick    "complex.h": ["complex"],
32*4bdff4beSrobert    "coroutine": ["compare"],
3376d0caaeSpatrick    "deque": ["compare", "initializer_list"],
3476d0caaeSpatrick    "filesystem": ["compare"],
3576d0caaeSpatrick    "forward_list": ["compare", "initializer_list"],
3676d0caaeSpatrick    "ios": ["iosfwd"],
3776d0caaeSpatrick    "iostream": ["ios", "istream", "ostream", "streambuf"],
3876d0caaeSpatrick    "iterator": ["compare", "concepts"],
3976d0caaeSpatrick    "list": ["compare", "initializer_list"],
4076d0caaeSpatrick    "map": ["compare", "initializer_list"],
4176d0caaeSpatrick    "memory": ["compare"],
4276d0caaeSpatrick    "optional": ["compare"],
4376d0caaeSpatrick    "queue": ["compare", "initializer_list"],
4476d0caaeSpatrick    "random": ["initializer_list"],
4576d0caaeSpatrick    "ranges": ["compare", "initializer_list", "iterator"],
4676d0caaeSpatrick    "regex": ["compare", "initializer_list"],
4776d0caaeSpatrick    "set": ["compare", "initializer_list"],
4876d0caaeSpatrick    "stack": ["compare", "initializer_list"],
4976d0caaeSpatrick    "string_view": ["compare"],
5076d0caaeSpatrick    "string": ["compare", "initializer_list"],
5176d0caaeSpatrick    # TODO "syncstream": ["ostream"],
5276d0caaeSpatrick    "system_error": ["compare"],
5376d0caaeSpatrick    "tgmath.h": ["cmath", "complex"],
5476d0caaeSpatrick    "thread": ["compare"],
5576d0caaeSpatrick    "tuple": ["compare"],
5676d0caaeSpatrick    "typeindex": ["compare"],
5776d0caaeSpatrick    "unordered_map": ["compare", "initializer_list"],
5876d0caaeSpatrick    "unordered_set": ["compare", "initializer_list"],
5976d0caaeSpatrick    "utility": ["compare", "initializer_list"],
6076d0caaeSpatrick    "valarray": ["initializer_list"],
6176d0caaeSpatrick    "variant": ["compare"],
6276d0caaeSpatrick    "vector": ["compare", "initializer_list"],
6376d0caaeSpatrick}
6476d0caaeSpatrick
6576d0caaeSpatricknew_in_version = {
6676d0caaeSpatrick    "chrono": "11",
6776d0caaeSpatrick    "compare": "20",
6876d0caaeSpatrick    "concepts": "20",
6976d0caaeSpatrick    "coroutine": "20",
70*4bdff4beSrobert    "cuchar": "11",
71*4bdff4beSrobert    "expected": "23",
7276d0caaeSpatrick    "filesystem": "17",
7376d0caaeSpatrick    "initializer_list": "11",
7476d0caaeSpatrick    "optional": "17",
7576d0caaeSpatrick    "ranges": "20",
7676d0caaeSpatrick    "string_view": "17",
7776d0caaeSpatrick    "syncstream": "20",
7876d0caaeSpatrick    "system_error": "11",
7976d0caaeSpatrick    "thread": "11",
8076d0caaeSpatrick    "tuple": "11",
81*4bdff4beSrobert    "uchar.h": "11",
8276d0caaeSpatrick    "unordered_map": "11",
8376d0caaeSpatrick    "unordered_set": "11",
8476d0caaeSpatrick    "variant": "17",
8576d0caaeSpatrick}
8676d0caaeSpatrick
8776d0caaeSpatrickassert all(v == sorted(v) for k, v in mandatory_inclusions.items())
8876d0caaeSpatrick
8976d0caaeSpatrick# Map from each header to the Lit annotations that should be used for
9076d0caaeSpatrick# tests that include that header.
9176d0caaeSpatrick#
92*4bdff4beSrobert# For example, when threads are not supported, any test that includes
93*4bdff4beSrobert# <thread> should be marked as UNSUPPORTED, because including <thread>
94*4bdff4beSrobert# is a hard error in that case.
9576d0caaeSpatricklit_markup = {
96*4bdff4beSrobert  "barrier": ["UNSUPPORTED: no-threads"],
97*4bdff4beSrobert  "filesystem": ["UNSUPPORTED: no-filesystem"],
9876d0caaeSpatrick  "format": ["UNSUPPORTED: libcpp-has-no-incomplete-format"],
99*4bdff4beSrobert  "iomanip": ["UNSUPPORTED: no-localization"],
100*4bdff4beSrobert  "ios": ["UNSUPPORTED: no-localization"],
101*4bdff4beSrobert  "iostream": ["UNSUPPORTED: no-localization"],
102*4bdff4beSrobert  "istream": ["UNSUPPORTED: no-localization"],
103*4bdff4beSrobert  "latch": ["UNSUPPORTED: no-threads"],
104*4bdff4beSrobert  "locale": ["UNSUPPORTED: no-localization"],
105*4bdff4beSrobert  "mutex": ["UNSUPPORTED: no-threads"],
106*4bdff4beSrobert  "ostream": ["UNSUPPORTED: no-localization"],
107*4bdff4beSrobert  "regex": ["UNSUPPORTED: no-localization"],
108*4bdff4beSrobert  "semaphore": ["UNSUPPORTED: no-threads"],
109*4bdff4beSrobert  "shared_mutex": ["UNSUPPORTED: no-threads"],
110*4bdff4beSrobert  "thread": ["UNSUPPORTED: no-threads"]
11176d0caaeSpatrick}
11276d0caaeSpatrick
11376d0caaeSpatrick
11476d0caaeSpatrickdef get_std_ver_test(includee):
11576d0caaeSpatrick    v = new_in_version.get(includee, "03")
11676d0caaeSpatrick    if v == "03":
11776d0caaeSpatrick        return ''
11876d0caaeSpatrick    versions = ["03", "11", "14", "17", "20"]
11976d0caaeSpatrick    return 'TEST_STD_VER > {} && '.format(max(i for i in versions if i < v))
12076d0caaeSpatrick
12176d0caaeSpatrick
12276d0caaeSpatrickdef get_unsupported_line(includee):
12376d0caaeSpatrick    v = new_in_version.get(includee, "03")
12476d0caaeSpatrick    return {
12576d0caaeSpatrick        "03": [],
12676d0caaeSpatrick        "11": ['UNSUPPORTED: c++03'],
12776d0caaeSpatrick        "14": ['UNSUPPORTED: c++03, c++11'],
12876d0caaeSpatrick        "17": ['UNSUPPORTED: c++03, c++11, c++14'],
12976d0caaeSpatrick        "20": ['UNSUPPORTED: c++03, c++11, c++14, c++17'],
13076d0caaeSpatrick        "2b": ['UNSUPPORTED: c++03, c++11, c++14, c++17, c++20'],
13176d0caaeSpatrick    }[v]
13276d0caaeSpatrick
13376d0caaeSpatrick
13476d0caaeSpatrickdef get_libcpp_header_symbol(header_name):
13576d0caaeSpatrick    return '_LIBCPP_' + header_name.upper().replace('.', '_')
13676d0caaeSpatrick
13776d0caaeSpatrick
13876d0caaeSpatrickdef get_includer_symbol_test(includer):
13976d0caaeSpatrick    symbol = get_libcpp_header_symbol(includer)
14076d0caaeSpatrick    return """
14176d0caaeSpatrick#if !defined({symbol})
14276d0caaeSpatrick #   error "{message}"
14376d0caaeSpatrick#endif
14476d0caaeSpatrick    """.strip().format(
14576d0caaeSpatrick        symbol=symbol,
14676d0caaeSpatrick        message="<{}> was expected to define {}".format(includer, symbol),
14776d0caaeSpatrick    )
14876d0caaeSpatrick
14976d0caaeSpatrick
15076d0caaeSpatrickdef get_ifdef(includer, includee):
15176d0caaeSpatrick    version = max(new_in_version.get(h, "03") for h in [includer, includee])
15276d0caaeSpatrick    symbol = get_libcpp_header_symbol(includee)
15376d0caaeSpatrick    return """
15476d0caaeSpatrick#if {includee_test}!defined({symbol})
15576d0caaeSpatrick #   error "{message}"
15676d0caaeSpatrick#endif
15776d0caaeSpatrick    """.strip().format(
15876d0caaeSpatrick        includee_test=get_std_ver_test(includee),
15976d0caaeSpatrick        symbol=symbol,
16076d0caaeSpatrick        message="<{}> should include <{}> in C++{} and later".format(includer, includee, version)
16176d0caaeSpatrick    )
16276d0caaeSpatrick
16376d0caaeSpatrick
16476d0caaeSpatricktest_body_template = """
16576d0caaeSpatrick//===----------------------------------------------------------------------===//
16676d0caaeSpatrick//
16776d0caaeSpatrick// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
16876d0caaeSpatrick// See https://llvm.org/LICENSE.txt for license information.
16976d0caaeSpatrick// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
17076d0caaeSpatrick//
17176d0caaeSpatrick//===----------------------------------------------------------------------===//
17276d0caaeSpatrick//
17376d0caaeSpatrick// WARNING: This test was generated by {script_name}
17476d0caaeSpatrick// and should not be edited manually.
17576d0caaeSpatrick//
17676d0caaeSpatrick// clang-format off
17776d0caaeSpatrick{markup}
17876d0caaeSpatrick// <{header}>
17976d0caaeSpatrick
18076d0caaeSpatrick// Test that <{header}> includes all the other headers it's supposed to.
18176d0caaeSpatrick
18276d0caaeSpatrick#include <{header}>
18376d0caaeSpatrick#include "test_macros.h"
18476d0caaeSpatrick
18576d0caaeSpatrick{test_includers_symbol}
18676d0caaeSpatrick{test_per_includee}
18776d0caaeSpatrick""".strip()
18876d0caaeSpatrick
18976d0caaeSpatrick
19076d0caaeSpatrickdef produce_tests():
19176d0caaeSpatrick    for includer, includees in mandatory_inclusions.items():
19276d0caaeSpatrick        markup_tags = get_unsupported_line(includer) + lit_markup.get(includer, [])
19376d0caaeSpatrick        test_body = test_body_template.format(
19476d0caaeSpatrick            script_name=script_name,
19576d0caaeSpatrick            header=includer,
19676d0caaeSpatrick            markup=('\n' + '\n'.join('// ' + m for m in markup_tags) + '\n') if markup_tags else '',
19776d0caaeSpatrick            test_includers_symbol=get_includer_symbol_test(includer),
19876d0caaeSpatrick            test_per_includee='\n'.join(get_ifdef(includer, includee) for includee in includees),
19976d0caaeSpatrick        )
20076d0caaeSpatrick        test_name = "{header}.inclusions.compile.pass.cpp".format(header=includer)
20176d0caaeSpatrick        out_path = os.path.join(test_path, test_name)
20276d0caaeSpatrick        with open(out_path, 'w', newline='\n') as f:
20376d0caaeSpatrick            f.write(test_body + '\n')
20476d0caaeSpatrick
20576d0caaeSpatrick
20676d0caaeSpatrickif __name__ == '__main__':
20776d0caaeSpatrick    produce_tests()
208