diff --git a/libcxx/test/configs/cmake-bridge.cfg.in b/libcxx/test/configs/cmake-bridge.cfg.in --- a/libcxx/test/configs/cmake-bridge.cfg.in +++ b/libcxx/test/configs/cmake-bridge.cfg.in @@ -30,3 +30,12 @@ config.substitutions.append(('%{target-include}', '@LIBCXX_GENERATED_INCLUDE_TARGET_DIR@')) config.substitutions.append(('%{lib}', '@LIBCXX_LIBRARY_DIR@')) config.substitutions.append(('%{executor}', '@LIBCXX_EXECUTOR@')) +# +# +# +# +# +# +# +# ldionne: What would be the best way to generate an artifacts directory in this directory? +config.substitutions.append(('%{test-exec-root}', config.test_exec_root)) diff --git a/libcxx/test/libcxx/trace_includes.sanitize.py b/libcxx/test/libcxx/trace_includes.sanitize.py new file mode 100755 --- /dev/null +++ b/libcxx/test/libcxx/trace_includes.sanitize.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# ===----------------------------------------------------------------------===## +# +# 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 +# +# ===----------------------------------------------------------------------===## + +# This script reads lines from standard input and looks for the names of public +# C++ headers. Specifically, it looks for lines of the form 'c++/v1/header' +# where 'header' is the name of a public C++ header, excluding C compatibility +# headers. (Note it keeps the libc++ implementation detail .h files.) + +# Note --trace-includes keeps cycles in the output. However when not in a cycle +# duplicates are not in the tree. For example the header version which is +# included by most headers only appears once. This output can't be used to see +# the exact nesting-level of a header. + +# The filtered output will be like: +# type_traits +# . algorithm +# ... cstddef +# .... __type_traits/enable_if.h +# .... __type_traits/integral_constant.h +# .... __type_traits/is_integral.h +# ..... __type_traits/remove_cv.h +# ...... __type_traits/remove_const.h +# ...... __type_traits/remove_volatile.h +# .... version + +import re +import sys + +headers = [] +for line in sys.stdin.readlines(): + match = re.match(r"(\.+).*c\+\+(?:/|\\\\)v[0-9]+(?:/|\\\\)(.+)", line) + if not match: + continue + + header = match.group(2) + # Skip C headers, but accept libc++ detail headers. + if header.startswith("__"): + if not header.endswith(".h"): + continue + elif header.endswith(".h"): + continue + + print(f"{match.group(1)} {match.group(2)}") diff --git a/libcxx/test/libcxx/trace_includes.sh.cpp b/libcxx/test/libcxx/trace_includes.sh.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/trace_includes.sh.cpp @@ -0,0 +1,553 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Test that we don't remove transitive includes of public C++ headers in the library accidentally. +// When we remove a transitive public include, clients tend to break because they don't always +// properly include what they use. Note that we don't check which system (C) headers are +// included transitively, because that is too unstable across platforms, and hence difficult +// to test for. +// +// This is not meant to block libc++ from removing unused transitive includes +// forever, however we do try to group removals for a couple of releases +// to avoid breaking users at every release. + +// This test doesn't support being run when some headers are not available, since we +// would need to add significant complexity to make that work. +// UNSUPPORTED: no-localization, no-threads, no-wide-characters, no-filesystem, libcpp-has-no-incomplete-format + +// Include graphs for older language versions aren't created by default. +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// When built with modules, this test doesn't work because --trace-includes doesn't +// report the stack of includes correctly. +// UNSUPPORTED: modules-build + +// This test uses --trace-includes, which is not supported by GCC. +// UNSUPPORTED: gcc + +// This test doesn't work on AIX, but it should. Needs investigation. +// XFAIL: buildhost=aix + +// This test is not supported when we remove the transitive includes provided for backwards +// compatibility. When we bulk-remove them, we'll adjust the includes that are expected by +// this test instead. +// XFAIL: transitive-includes-disabled + +// Prevent from generating deprecated warnings for this test. +#if defined(__DEPRECATED) +# undef __DEPRECATED +#endif + +/* +BEGIN-SCRIPT + +import re + +# Used because the sequence of tokens RUN : can't appear anywhere or it'll confuse Lit. +RUN = "RUN" + +print(f"// {RUN}: mkdir %t") + +for i, header in enumerate(public_headers): + if header.endswith('.h'): # Skip C compatibility or detail headers + continue + + normalized_header = re.sub('/', '_', header) + trace_includes = "%{{cxx}} %s %{{flags}} %{{compile_flags}} --trace-includes -fsyntax-only -DTEST_{} 2>&1".format(i) + + print(f"// {RUN}: {trace_includes} | %{{python}} %S/trace_includes.sanitize.py > %t/{normalized_header}") + + print(f"#if defined(TEST_{i})") + print(f"#include <{header}>") + print("#endif") + +print(f"// {RUN}: %{{python}} %S/../../utils/graph_header_dependencies.py %t > %{{test-exec-root}}/includes.dot") +END-SCRIPT +*/ + +// DO NOT MANUALLY EDIT ANYTHING BETWEEN THE MARKERS BELOW +// GENERATED-MARKER +// RUN: mkdir %t +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_0 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/algorithm +#if defined(TEST_0) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_1 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/any +#if defined(TEST_1) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_2 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/array +#if defined(TEST_2) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_3 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/atomic +#if defined(TEST_3) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_4 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/barrier +#if defined(TEST_4) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_5 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/bit +#if defined(TEST_5) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_6 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/bitset +#if defined(TEST_6) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_7 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/cassert +#if defined(TEST_7) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_8 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/ccomplex +#if defined(TEST_8) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_9 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/cctype +#if defined(TEST_9) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_10 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/cerrno +#if defined(TEST_10) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_11 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/cfenv +#if defined(TEST_11) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_12 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/cfloat +#if defined(TEST_12) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_13 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/charconv +#if defined(TEST_13) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_14 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/chrono +#if defined(TEST_14) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_15 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/cinttypes +#if defined(TEST_15) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_16 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/ciso646 +#if defined(TEST_16) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_17 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/climits +#if defined(TEST_17) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_18 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/clocale +#if defined(TEST_18) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_19 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/cmath +#if defined(TEST_19) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_20 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/codecvt +#if defined(TEST_20) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_21 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/compare +#if defined(TEST_21) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_22 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/complex +#if defined(TEST_22) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_24 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/concepts +#if defined(TEST_24) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_25 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/condition_variable +#if defined(TEST_25) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_26 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/coroutine +#if defined(TEST_26) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_27 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/csetjmp +#if defined(TEST_27) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_28 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/csignal +#if defined(TEST_28) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_29 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/cstdarg +#if defined(TEST_29) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_30 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/cstdbool +#if defined(TEST_30) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_31 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/cstddef +#if defined(TEST_31) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_32 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/cstdint +#if defined(TEST_32) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_33 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/cstdio +#if defined(TEST_33) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_34 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/cstdlib +#if defined(TEST_34) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_35 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/cstring +#if defined(TEST_35) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_36 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/ctgmath +#if defined(TEST_36) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_37 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/ctime +#if defined(TEST_37) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_39 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/cuchar +#if defined(TEST_39) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_40 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/cwchar +#if defined(TEST_40) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_41 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/cwctype +#if defined(TEST_41) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_42 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/deque +#if defined(TEST_42) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_44 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/exception +#if defined(TEST_44) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_45 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/execution +#if defined(TEST_45) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_47 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/filesystem +#if defined(TEST_47) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_49 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/format +#if defined(TEST_49) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_50 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/forward_list +#if defined(TEST_50) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_51 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/fstream +#if defined(TEST_51) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_52 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/functional +#if defined(TEST_52) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_53 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/future +#if defined(TEST_53) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_54 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/initializer_list +#if defined(TEST_54) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_56 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/iomanip +#if defined(TEST_56) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_57 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/ios +#if defined(TEST_57) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_58 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/iosfwd +#if defined(TEST_58) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_59 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/iostream +#if defined(TEST_59) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_60 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/istream +#if defined(TEST_60) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_61 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/iterator +#if defined(TEST_61) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_62 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/latch +#if defined(TEST_62) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_63 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/limits +#if defined(TEST_63) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_65 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/list +#if defined(TEST_65) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_66 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/locale +#if defined(TEST_66) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_68 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/map +#if defined(TEST_68) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_70 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/memory +#if defined(TEST_70) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_71 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/mutex +#if defined(TEST_71) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_72 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/new +#if defined(TEST_72) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_73 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/numbers +#if defined(TEST_73) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_74 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/numeric +#if defined(TEST_74) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_75 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/optional +#if defined(TEST_75) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_76 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/ostream +#if defined(TEST_76) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_77 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/queue +#if defined(TEST_77) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_78 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/random +#if defined(TEST_78) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_79 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/ranges +#if defined(TEST_79) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_80 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/ratio +#if defined(TEST_80) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_81 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/regex +#if defined(TEST_81) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_82 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/scoped_allocator +#if defined(TEST_82) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_83 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/semaphore +#if defined(TEST_83) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_84 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/set +#if defined(TEST_84) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_86 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/shared_mutex +#if defined(TEST_86) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_87 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/span +#if defined(TEST_87) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_88 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/sstream +#if defined(TEST_88) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_89 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/stack +#if defined(TEST_89) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_93 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/stdexcept +#if defined(TEST_93) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_97 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/streambuf +#if defined(TEST_97) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_98 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/string +#if defined(TEST_98) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_100 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/string_view +#if defined(TEST_100) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_101 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/strstream +#if defined(TEST_101) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_102 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/system_error +#if defined(TEST_102) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_104 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/thread +#if defined(TEST_104) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_105 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/tuple +#if defined(TEST_105) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_106 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/type_traits +#if defined(TEST_106) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_107 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/typeindex +#if defined(TEST_107) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_108 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/typeinfo +#if defined(TEST_108) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_110 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/unordered_map +#if defined(TEST_110) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_111 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/unordered_set +#if defined(TEST_111) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_112 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/utility +#if defined(TEST_112) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_113 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/valarray +#if defined(TEST_113) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_114 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/variant +#if defined(TEST_114) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_115 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/vector +#if defined(TEST_115) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_116 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/version +#if defined(TEST_116) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_119 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_algorithm +#if defined(TEST_119) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_120 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_coroutine +#if defined(TEST_120) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_121 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_deque +#if defined(TEST_121) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_122 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_forward_list +#if defined(TEST_122) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_123 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_functional +#if defined(TEST_123) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_124 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_iterator +#if defined(TEST_124) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_125 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_list +#if defined(TEST_125) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_126 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_map +#if defined(TEST_126) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_127 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_memory_resource +#if defined(TEST_127) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_128 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_propagate_const +#if defined(TEST_128) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_129 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_regex +#if defined(TEST_129) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_130 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_set +#if defined(TEST_130) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_131 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_simd +#if defined(TEST_131) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_132 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_string +#if defined(TEST_132) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_133 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_type_traits +#if defined(TEST_133) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_134 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_unordered_map +#if defined(TEST_134) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_135 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_unordered_set +#if defined(TEST_135) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_136 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_utility +#if defined(TEST_136) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_137 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/experimental_vector +#if defined(TEST_137) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_138 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/ext_hash_map +#if defined(TEST_138) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fsyntax-only -DTEST_139 2>&1 | %{python} %S/trace_includes.sanitize.py > %t/ext_hash_set +#if defined(TEST_139) +#include +#endif +// RUN: %{python} %S/../../utils/graph_header_dependencies.py %t > %{test-exec-root}/includes.dot +// GENERATED-MARKER diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml --- a/libcxx/utils/ci/buildkite-pipeline.yml +++ b/libcxx/utils/ci/buildkite-pipeline.yml @@ -84,6 +84,7 @@ artifact_paths: - "**/test-results.xml" - "**/*.abilist" + - "**/includes.dot" env: CC: "clang-${LLVM_HEAD_VERSION}" CXX: "clang++-${LLVM_HEAD_VERSION}" diff --git a/libcxx/utils/generate_header_tests.py b/libcxx/utils/generate_header_tests.py --- a/libcxx/utils/generate_header_tests.py +++ b/libcxx/utils/generate_header_tests.py @@ -144,6 +144,7 @@ produce(test.joinpath('libcxx/nasty_macros.compile.pass.cpp'), variables) produce(test.joinpath('libcxx/no_assert_include.compile.pass.cpp'), variables) produce(test.joinpath('libcxx/private_headers.verify.cpp'), variables) + produce(test.joinpath('libcxx/trace_includes.sh.cpp'), variables) produce(test.joinpath('libcxx/transitive_includes.sh.cpp'), variables) diff --git a/libcxx/utils/graph_header_dependencies.py b/libcxx/utils/graph_header_dependencies.py new file mode 100755 --- /dev/null +++ b/libcxx/utils/graph_header_dependencies.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# ===----------------------------------------------------------------------===## +# +# 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 +# +# ===----------------------------------------------------------------------===## + +from dataclasses import dataclass, field +import re +import sys +import pathlib + + +@dataclass +class header: + name: str = None + level: int = -1 + + +def parse_line(line: str) -> header: + match = re.match(r"(\.+) (.+)", line) + if not match: + sys.exit(f"File {file} line {line} contains invalid data.") + + # The number of periods in front of the header name is the nesting level of + # that header. + return header(match.group(2), len(match.group(1))) + + +# Generates the list of transitive includes of a header. +# +# The input contains two kinds of headers +# * Standard headers (algorithm, string, format, etc) +# * Detail headers (__algorithm/copy.h, __algorithm/copy_n.h, etc) +# The output contains the transitive includes of the Standard header being +# processed. The detail headers are omitted from the output, but their +# transitive includes are parsed and added, if they are a Standard header. +# +# This effectively generates the dependency graph of a Standard header. +def parse_file(file: pathlib.Path) -> list[str]: + result = list() + with file.open(encoding="utf-8") as f: + level = 999 + + # The first line contains the Standard header being processed. + # The transitive includes of this Standard header should be processed. + header = parse_line(f.readline()) + assert header.level == 1 + result.append(header.name) + + for line in f.readlines(): + header = parse_line(line) + + # Skip deeper nested headers for Standard headers. + if header.level > level: + continue + + # Process deeper nested headers for detail headers. + if header.name.startswith("__") or header.name.__contains__("/__"): + level = 999 + continue + + # Add the Standard header. + level = header.level + result.append(header.name) + + return result + + +def create_include_graph(path: pathlib.Path) -> list[str]: + result = list() + for file in path.glob("*"): + includes = parse_file(file) + if len(includes) > 1: + result.append(includes) + return result + + +def create_include_graphviz(graph: list[str]) -> str: + # Sets the defaults to look nice for the current dependency graph. + # These might need tweaking when libc++'s internal dependencies change + # significantly. + result = """digraph includes { +graph [nodesep=0.5, ranksep=1]; +node [shape=box, width=4]; +""" + + for includes in graph: + header = includes[0] + for include in includes[1:]: + if header == include: + sys.exit(f"Cycle detected: header {header} includes itself.") + result += f'\t"{header}" -> "{include}"\n' + + result += "}" + return result + + +if __name__ == "__main__": + if len(sys.argv) != 2: + sys.exit("Specify the directory with the input files") + + output_directory = sys.argv[1] + root = pathlib.Path(output_directory) + + graph = create_include_graph(root) + result = create_include_graphviz(graph) + print(result)