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