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