| #!/usr/bin/env python |
| |
| import glob |
| import os |
| import posixpath |
| import re |
| |
| |
| def get_libcxx_paths(): |
| utils_path = os.path.dirname(os.path.abspath(__file__)) |
| script_name = os.path.basename(__file__) |
| assert os.path.exists(utils_path) |
| src_root = os.path.dirname(utils_path) |
| include_path = os.path.join(src_root, 'include') |
| assert os.path.exists(include_path) |
| libcxx_test_path = os.path.join(src_root, 'test', 'libcxx') |
| assert os.path.exists(libcxx_test_path) |
| return script_name, src_root, include_path, libcxx_test_path |
| |
| |
| script_name, source_root, include_path, libcxx_test_path = get_libcxx_paths() |
| |
| header_markup = { |
| "barrier": ["ifndef _LIBCPP_HAS_NO_THREADS"], |
| "future": ["ifndef _LIBCPP_HAS_NO_THREADS"], |
| "latch": ["ifndef _LIBCPP_HAS_NO_THREADS"], |
| "mutex": ["ifndef _LIBCPP_HAS_NO_THREADS"], |
| "semaphore": ["ifndef _LIBCPP_HAS_NO_THREADS"], |
| "shared_mutex": ["ifndef _LIBCPP_HAS_NO_THREADS"], |
| "thread": ["ifndef _LIBCPP_HAS_NO_THREADS"], |
| |
| "experimental/filesystem": ["ifndef _LIBCPP_HAS_NO_FILESYSTEM_LIBRARY"], |
| "filesystem": ["ifndef _LIBCPP_HAS_NO_FILESYSTEM_LIBRARY"], |
| "format": ["ifndef _LIBCPP_HAS_NO_INCOMPLETE_FORMAT"], |
| |
| "clocale": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], |
| "codecvt": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], |
| "fstream": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], |
| "iomanip": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], |
| "ios": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], |
| "iostream": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], |
| "istream": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], |
| "locale.h": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], |
| "locale": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], |
| "ostream": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], |
| "ranges": ["ifndef _LIBCPP_HAS_NO_INCOMPLETE_RANGES"], |
| "regex": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], |
| "sstream": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], |
| "streambuf": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], |
| "strstream": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], |
| |
| "wctype.h": ["ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS"], |
| "cwctype": ["ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS"], |
| "cwchar": ["ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS"], |
| "wchar.h": ["ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS"], |
| |
| "experimental/coroutine": ["ifndef _LIBCPP_HAS_NO_EXPERIMENTAL_COROUTINES"], |
| "coroutine": ["ifndef _LIBCPP_HAS_NO_CXX20_COROUTINES"], |
| "experimental/regex": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], |
| } |
| |
| allowed_extensions = ['', '.h'] |
| indent_width = 4 |
| |
| |
| begin_pattern = """\ |
| //////////////////////////////////////////////////////////////////////////////// |
| // BEGIN-GENERATED-HEADERS |
| //////////////////////////////////////////////////////////////////////////////// |
| """ |
| |
| warning_note = """\ |
| // WARNING: This test was generated by {script_name} |
| // and should not be edited manually. |
| |
| """.format(script_name=script_name) |
| |
| end_pattern = """\ |
| //////////////////////////////////////////////////////////////////////////////// |
| // END-GENERATED-HEADERS |
| //////////////////////////////////////////////////////////////////////////////// |
| """ |
| |
| generated_part_pattern = re.compile(re.escape(begin_pattern) + ".*" + re.escape(end_pattern), |
| re.MULTILINE | re.DOTALL) |
| |
| headers_template = """\ |
| // Top level headers |
| {top_level_headers} |
| |
| // experimental headers |
| #if __cplusplus >= 201103L |
| {experimental_headers} |
| #endif // __cplusplus >= 201103L |
| |
| // extended headers |
| {extended_headers} |
| """ |
| |
| |
| def should_keep_header(p, exclusions=None): |
| if os.path.isdir(p): |
| return False |
| |
| if exclusions: |
| relpath = os.path.relpath(p, include_path) |
| relpath = posixpath.join(*os.path.split(relpath)) |
| if relpath in exclusions: |
| return False |
| |
| return os.path.splitext(p)[1] in allowed_extensions |
| |
| |
| def produce_include(relpath, indent_level, post_include=None): |
| relpath = posixpath.join(*os.path.split(relpath)) |
| template = "{preambule}#{indentation}include <{include}>{post_include}{postambule}" |
| |
| base_indentation = ' '*(indent_width * indent_level) |
| next_indentation = base_indentation + ' '*(indent_width) |
| post_include = "\n{}".format(post_include) if post_include else '' |
| |
| markup = header_markup.get(relpath, None) |
| if markup: |
| preambule = '#{indentation}{directive}\n'.format( |
| directive=markup[0], |
| indentation=base_indentation, |
| ) |
| postambule = '\n#{indentation}endif'.format( |
| indentation=base_indentation, |
| ) |
| indentation = next_indentation |
| else: |
| preambule = '' |
| postambule = '' |
| indentation = base_indentation |
| |
| return template.format( |
| include=relpath, |
| post_include=post_include, |
| preambule=preambule, |
| postambule=postambule, |
| indentation=indentation, |
| ) |
| |
| |
| def produce_headers(path_parts, indent_level, post_include=None, exclusions=None): |
| pattern = os.path.join(*path_parts, '[a-z]*') |
| |
| files = sorted(glob.glob(pattern, recursive=False)) |
| |
| include_headers = [ |
| produce_include(os.path.relpath(p, include_path), |
| indent_level, post_include=post_include) |
| for p in files |
| if should_keep_header(p, exclusions) |
| ] |
| |
| return '\n'.join(include_headers) |
| |
| |
| def produce_top_level_headers(post_include=None, exclusions=None): |
| return produce_headers([include_path], 0, post_include=post_include, exclusions=exclusions) |
| |
| |
| def produce_experimental_headers(post_include=None, exclusions=None): |
| return produce_headers([include_path, 'experimental'], 1, post_include=post_include, exclusions=exclusions) |
| |
| |
| def produce_extended_headers(post_include=None, exclusions=None): |
| return produce_headers([include_path, 'ext'], 0, post_include=post_include, exclusions=exclusions) |
| |
| |
| def replace_generated_headers(test_path, test_str): |
| with open(test_path, 'r') as f: |
| content = f.read() |
| |
| preambule = begin_pattern + '\n// clang-format off\n\n' + warning_note |
| postambule = '\n// clang-format on\n\n' + end_pattern |
| content = generated_part_pattern.sub( |
| preambule + test_str + postambule, content) |
| |
| with open(test_path, 'w', newline='\n') as f: |
| f.write(content) |
| |
| |
| def produce_test(test_filename, exclusions=None, post_include=None): |
| test_str = headers_template.format( |
| top_level_headers=produce_top_level_headers( |
| post_include=post_include, |
| exclusions=exclusions, |
| ), |
| experimental_headers=produce_experimental_headers( |
| post_include=post_include, |
| ), |
| extended_headers=produce_extended_headers( |
| post_include=post_include, |
| ), |
| ) |
| |
| replace_generated_headers(os.path.join( |
| libcxx_test_path, test_filename), test_str) |
| |
| |
| def main(): |
| produce_test('double_include.sh.cpp') |
| produce_test('min_max_macros.compile.pass.cpp', |
| post_include='TEST_MACROS();') |
| produce_test('no_assert_include.compile.pass.cpp', |
| exclusions=['cassert']) |
| |
| |
| if __name__ == '__main__': |
| main() |