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 "barrier": ["ifndef _LIBCPP_HAS_NO_THREADS"], 25 "future": ["ifndef _LIBCPP_HAS_NO_THREADS"], 26 "latch": ["ifndef _LIBCPP_HAS_NO_THREADS"], 27 "mutex": ["ifndef _LIBCPP_HAS_NO_THREADS"], 28 "semaphore": ["ifndef _LIBCPP_HAS_NO_THREADS"], 29 "shared_mutex": ["ifndef _LIBCPP_HAS_NO_THREADS"], 30 "thread": ["ifndef _LIBCPP_HAS_NO_THREADS"], 31 32 "experimental/filesystem": ["ifndef _LIBCPP_HAS_NO_FILESYSTEM_LIBRARY"], 33 "filesystem": ["ifndef _LIBCPP_HAS_NO_FILESYSTEM_LIBRARY"], 34 "format": ["ifndef _LIBCPP_HAS_NO_INCOMPLETE_FORMAT"], 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.h": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], 44 "locale": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], 45 "ostream": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], 46 "ranges": ["ifndef _LIBCPP_HAS_NO_INCOMPLETE_RANGES"], 47 "regex": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], 48 "sstream": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], 49 "streambuf": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], 50 "strstream": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], 51 52 "wctype.h": ["ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS"], 53 "cwctype": ["ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS"], 54 "cwchar": ["ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS"], 55 "wchar.h": ["ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS"], 56 57 "experimental/coroutine": ["ifndef _LIBCPP_HAS_NO_EXPERIMENTAL_COROUTINES"], 58 "coroutine": ["ifndef _LIBCPP_HAS_NO_CXX20_COROUTINES"], 59 "experimental/regex": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], 60} 61 62allowed_extensions = ['', '.h'] 63indent_width = 4 64 65 66begin_pattern = """\ 67//////////////////////////////////////////////////////////////////////////////// 68// BEGIN-GENERATED-HEADERS 69//////////////////////////////////////////////////////////////////////////////// 70""" 71 72warning_note = """\ 73// WARNING: This test was generated by {script_name} 74// and should not be edited manually. 75 76""".format(script_name=script_name) 77 78end_pattern = """\ 79//////////////////////////////////////////////////////////////////////////////// 80// END-GENERATED-HEADERS 81//////////////////////////////////////////////////////////////////////////////// 82""" 83 84generated_part_pattern = re.compile(re.escape(begin_pattern) + ".*" + re.escape(end_pattern), 85 re.MULTILINE | re.DOTALL) 86 87headers_template = """\ 88// Top level headers 89{top_level_headers} 90 91// experimental headers 92#if __cplusplus >= 201103L 93{experimental_headers} 94#endif // __cplusplus >= 201103L 95 96// extended headers 97{extended_headers} 98""" 99 100 101def should_keep_header(p, exclusions=None): 102 if os.path.isdir(p): 103 return False 104 105 if exclusions: 106 relpath = os.path.relpath(p, include_path) 107 relpath = posixpath.join(*os.path.split(relpath)) 108 if relpath in exclusions: 109 return False 110 111 return os.path.splitext(p)[1] in allowed_extensions 112 113 114def produce_include(relpath, indent_level, post_include=None): 115 relpath = posixpath.join(*os.path.split(relpath)) 116 template = "{preamble}#{indentation}include <{include}>{post_include}{postamble}" 117 118 base_indentation = ' '*(indent_width * indent_level) 119 next_indentation = base_indentation + ' '*(indent_width) 120 post_include = "\n{}".format(post_include) if post_include else '' 121 122 markup = header_markup.get(relpath, None) 123 if markup: 124 preamble = '#{indentation}{directive}\n'.format( 125 directive=markup[0], 126 indentation=base_indentation, 127 ) 128 postamble = '\n#{indentation}endif'.format( 129 indentation=base_indentation, 130 ) 131 indentation = next_indentation 132 else: 133 preamble = '' 134 postamble = '' 135 indentation = base_indentation 136 137 return template.format( 138 include=relpath, 139 post_include=post_include, 140 preamble=preamble, 141 postamble=postamble, 142 indentation=indentation, 143 ) 144 145 146def produce_headers(path_parts, indent_level, post_include=None, exclusions=None): 147 pattern = os.path.join(*path_parts, '[a-z]*') 148 149 files = sorted(glob.glob(pattern, recursive=False)) 150 151 include_headers = [ 152 produce_include(os.path.relpath(p, include_path), 153 indent_level, post_include=post_include) 154 for p in files 155 if should_keep_header(p, exclusions) 156 ] 157 158 return '\n'.join(include_headers) 159 160 161def produce_top_level_headers(post_include=None, exclusions=None): 162 return produce_headers([include_path], 0, post_include=post_include, exclusions=exclusions) 163 164 165def produce_experimental_headers(post_include=None, exclusions=None): 166 return produce_headers([include_path, 'experimental'], 1, post_include=post_include, exclusions=exclusions) 167 168 169def produce_extended_headers(post_include=None, exclusions=None): 170 return produce_headers([include_path, 'ext'], 0, post_include=post_include, exclusions=exclusions) 171 172 173def replace_generated_headers(test_path, test_str): 174 with open(test_path, 'r') as f: 175 content = f.read() 176 177 preamble = begin_pattern + '\n// clang-format off\n\n' + warning_note 178 postamble = '\n// clang-format on\n\n' + end_pattern 179 content = generated_part_pattern.sub( 180 preamble + test_str + postamble, content) 181 182 with open(test_path, 'w', newline='\n') as f: 183 f.write(content) 184 185 186def produce_test(test_filename, exclusions=None, post_include=None): 187 test_str = headers_template.format( 188 top_level_headers=produce_top_level_headers( 189 post_include=post_include, 190 exclusions=exclusions, 191 ), 192 experimental_headers=produce_experimental_headers( 193 post_include=post_include, 194 ), 195 extended_headers=produce_extended_headers( 196 post_include=post_include, 197 ), 198 ) 199 200 replace_generated_headers(os.path.join( 201 libcxx_test_path, test_filename), test_str) 202 203 204def main(): 205 produce_test('double_include.sh.cpp') 206 produce_test('min_max_macros.compile.pass.cpp', post_include='TEST_MACROS();') 207 produce_test('nasty_macros.compile.pass.cpp') 208 produce_test('no_assert_include.compile.pass.cpp', exclusions=['cassert']) 209 210 211if __name__ == '__main__': 212 main() 213