llvm-project/libcxx/utils/generate_header_tests.py
Louis Dionne 92832e4889 [libc++] Enable <atomic> when threads are disabled
std::atomic is, for the most part, just a thin veneer on top of compiler
builtins. Hence, it should be available even when threads are not available
on the system, and in fact there has been requests for such support.

This patch:
- Moves __libcpp_thread_poll_with_backoff to its own header so it can
  be used in <atomic> when threads are disabled.
- Adds a dummy backoff policy for atomic polling that doesn't know about
  threads.
- Adjusts the <atomic> feature-test macros so they are provided even when
  threads are disabled.
- Runs the <atomic> tests when threads are disabled.

rdar://77873569

Differential Revision: https://reviews.llvm.org/D114109
2021-11-17 23:02:58 -05:00

214 lines
6.9 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 = {
"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()