1#===----------------------------------------------------------------------===## 2# 3# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4# See https://llvm.org/LICENSE.txt for license information. 5# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6# 7#===----------------------------------------------------------------------===## 8 9# Test that we don't remove transitive includes of public C++ headers in the library accidentally. 10# When we remove a transitive public include, clients tend to break because they don't always 11# properly include what they use. Note that we don't check which system (C) headers are 12# included transitively, because that is too unstable across platforms, and hence difficult 13# to test for. 14# 15# This is not meant to block libc++ from removing unused transitive includes 16# forever, however we do try to group removals for a couple of releases 17# to avoid breaking users at every release. 18 19# RUN: %{python} %s %{libcxx-dir}/utils 20 21import sys 22sys.path.append(sys.argv[1]) 23from libcxx.header_information import lit_header_restrictions, public_headers 24 25import re 26 27# To re-generate the list of expected headers, temporarily set this to True, and run this test. 28# Note that this needs to be done for all supported language versions of libc++: 29# for std in c++03 c++11 c++14 c++17 c++20 c++23 c++26; do <build>/bin/llvm-lit --param std=$std libcxx/test/libcxx/transitive_includes.gen.py; done 30regenerate_expected_results = False 31 32BLOCKLIT = '' # block Lit from interpreting a RUN/XFAIL/etc inside the generation script 33if regenerate_expected_results: 34 print(f"""\ 35//--- generate-transitive-includes.sh.cpp 36// RUN{BLOCKLIT}: mkdir %t 37""") 38 39 all_traces = [] 40 for header in sorted(public_headers): 41 if header.endswith('.h'): # Skip C compatibility or detail headers 42 continue 43 44 normalized_header = re.sub('/', '_', header) 45 print(f"""\ 46// RUN{BLOCKLIT}: echo "#include <{header}>" | %{{cxx}} -xc++ - %{{flags}} %{{compile_flags}} --trace-includes -fshow-skipped-includes --preprocess > /dev/null 2> %t/trace-includes.{normalized_header}.txt 47""") 48 all_traces.append(f'%t/trace-includes.{normalized_header}.txt') 49 50 print(f"""\ 51// RUN{BLOCKLIT}: %{{python}} %{{libcxx-dir}}/test/libcxx/transitive_includes_to_csv.py {' '.join(all_traces)} > %{{libcxx-dir}}/test/libcxx/transitive_includes/%{{cxx_std}}.csv 52""") 53 54else: 55 for header in public_headers: 56 if header.endswith('.h'): # Skip C compatibility or detail headers 57 continue 58 59 # Escape slashes for the awk command below 60 escaped_header = header.replace('/', '\/') 61 62 print(f"""\ 63//--- {header}.sh.cpp 64{lit_header_restrictions.get(header, '')} 65 66// TODO: Fix this test to make it work with localization or wide characters disabled 67// UNSUPPORTED{BLOCKLIT}: no-localization, no-wide-characters, no-threads, no-filesystem, libcpp-has-no-incomplete-tzdb, no-tzdb 68 69// When built with modules, this test doesn't work because --trace-includes doesn't 70// report the stack of includes correctly. 71// UNSUPPORTED{BLOCKLIT}: clang-modules-build 72 73// This test uses --trace-includes, which is not supported by GCC. 74// UNSUPPORTED{BLOCKLIT}: gcc 75 76// This test is not supported when we remove the transitive includes provided for backwards 77// compatibility. When we bulk-remove them, we'll adjust the includes that are expected by 78// this test instead. 79// UNSUPPORTED{BLOCKLIT}: transitive-includes-disabled 80 81// TODO: Figure out why <stdatomic.h> doesn't work on FreeBSD 82// UNSUPPORTED{BLOCKLIT}: LIBCXX-FREEBSD-FIXME 83 84// RUN{BLOCKLIT}: mkdir %t 85// RUN{BLOCKLIT}: %{{cxx}} %s %{{flags}} %{{compile_flags}} --trace-includes -fshow-skipped-includes --preprocess > /dev/null 2> %t/trace-includes.txt 86// RUN{BLOCKLIT}: %{{python}} %{{libcxx-dir}}/test/libcxx/transitive_includes_to_csv.py %t/trace-includes.txt > %t/actual_transitive_includes.csv 87// RUN{BLOCKLIT}: cat %{{libcxx-dir}}/test/libcxx/transitive_includes/%{{cxx_std}}.csv | awk '/^{escaped_header} / {{ print }}' > %t/expected_transitive_includes.csv 88// RUN{BLOCKLIT}: diff -w %t/expected_transitive_includes.csv %t/actual_transitive_includes.csv 89#include <{header}> 90""") 91