Files
clang-p2996/libcxx/utils/generate_private_header_tests.py
Christopher Di Bella e37bbfe59c [libcxx][modules] protects users from relying on libc++ detail headers (1/n)
libc++ has started splicing standard library headers into much more
fine-grained content for maintainability. It's very likely that outdated
and naive tooling (some of which is outside of LLVM's scope) will
suggest users include things such as `<__algorithm/find.h>` instead of
`<algorithm>`, and Hyrum's law suggests that users will eventually begin
to rely on this without the help of tooling. As such, this commit
intends to protect users from themselves, by making it a hard error for
anyone outside of the standard library to include libc++ detail headers.

This is the first of four patches. Patch #2 will solve the problem for
pre-processor `#include`s; patches #3 and #4 will solve the problem for
`<__tree>` and `<__hash_table>` (since I've never touched the test cases
that are failing for these two, I want to split them out into their own
commits to be extra careful). Patch #5 will concern itself with
`<__threading_support>`, which intersects with libcxxabi (which I know
even less about).

Differential Revision: https://reviews.llvm.org/D105932
2021-07-16 22:39:18 +00:00

84 lines
2.8 KiB
Python
Executable File

#!/usr/bin/env python
import os
import re
import shutil
from pathlib import Path
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)
docs_path = os.path.join(src_root, 'docs')
assert os.path.exists(docs_path)
detail_header_test_root = os.path.join(src_root, 'test', 'libcxx',
'diagnostics', 'detail.headers')
assert os.path.exists(detail_header_test_root)
shutil.rmtree(detail_header_test_root)
Path(f'{detail_header_test_root}').mkdir()
assert os.path.exists(detail_header_test_root)
return script_name, src_root, include_path, docs_path, detail_header_test_root
script_name, source_root, include_path, docs_path, detail_header_test_root = get_libcxx_paths(
)
def generate_test(header):
return f'''// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// REQUIRES: modules-build
// WARNING: This test was generated by '{script_name}'
// and should not be edited manually.
// expected-error@*:* {{{{use of private header from outside its module: '{header}'}}}}
#include <{header}>
'''
def relative_path(path):
return path.as_posix()[len(include_path + '/'):]
def is_still_public(path):
rp = relative_path(path)
return not rp.startswith('__support') and rp not in [
"__bsd_locale_defaults.h", "__bsd_locale_fallbacks.h", "__config",
"__config_site.in", "__debug", "__hash_table", "__functional_base",
"__libcpp_version", "__nullptr", "__threading_support", "__tree",
"__undef_macros"
]
def main():
paths = [
relative_path(p) for p in Path(include_path).rglob('*')
if relative_path(p).startswith('__') and not p.is_dir()
and is_still_public(p)
]
for path in paths:
path_with_subdir = re.search(r'__(\w+)/(\w+)', path)
directory = path_with_subdir.group(1) + '/' if path_with_subdir else ""
file = path_with_subdir.group(2) if path_with_subdir else path[2:]
path_to_write = f'{detail_header_test_root}/{directory}{file}.module.verify.cpp'
Path(f'{detail_header_test_root}/{directory}').mkdir(exist_ok=True)
assert os.path.exists(f'{detail_header_test_root}/{directory}')
with open(path_to_write, 'w') as f:
f.write(generate_test(path))
if __name__ == '__main__':
main()