mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-03 07:46:06 +00:00

Adds a new CMake option to disable the usage of incomplete headers. These incomplete headers are not guaranteed to be ABI stable. This option is intended to be used by vendors so they can avoid their users from code that's not ready for production usage. The option is enabled by default. Differential Revision: https://reviews.llvm.org/D106763
209 lines
6.7 KiB
Python
Executable File
209 lines
6.7 KiB
Python
Executable File
#!/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 = {
|
|
"atomic": ["ifndef _LIBCPP_HAS_NO_THREADS"],
|
|
"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"],
|
|
|
|
"experimental/coroutine": ["if defined(__cpp_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()
|