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 "wctype.h": ["ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS"], 54 "cwctype": ["ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS"], 55 "cwchar": ["ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS"], 56 "wchar.h": ["ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS"], 57 58 "experimental/coroutine": ["if defined(__cpp_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 = "{preambule}#{indentation}include <{include}>{post_include}{postambule}" 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 preambule = '#{indentation}{directive}\n'.format( 125 directive=markup[0], 126 indentation=base_indentation, 127 ) 128 postambule = '\n#{indentation}endif'.format( 129 indentation=base_indentation, 130 ) 131 indentation = next_indentation 132 else: 133 preambule = '' 134 postambule = '' 135 indentation = base_indentation 136 137 return template.format( 138 include=relpath, 139 post_include=post_include, 140 preambule=preambule, 141 postambule=postambule, 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 preambule = begin_pattern + '\n// clang-format off\n\n' + warning_note 178 postambule = '\n// clang-format on\n\n' + end_pattern 179 content = generated_part_pattern.sub( 180 preambule + test_str + postambule, 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', 207 post_include='TEST_MACROS();') 208 produce_test('no_assert_include.compile.pass.cpp', 209 exclusions=['cassert']) 210 211 212if __name__ == '__main__': 213 main() 214