Skip to content

Commit eebe9b2

Browse files
committed
[libc++][modules] Adds module testing.
This adds a new module test infrastructure. This requires tagging tests using modules. The test runner uses this information to determine the compiler flags needed to build and use the module. Currently modules are build per test, which allows testing them for tests with ADDITIONAL_COMPILE_FLAGS. At the moment only 4 tests use modules. Therefore the performance penalty is not measurable. If in the future more tests use modules it would be good to measure the overhead and determine whether it's acceptable.
1 parent 13d7990 commit eebe9b2

13 files changed

+190
-5
lines changed

libcxx/docs/TestingLibcxx.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ Custom Directives
394394
~~~~~~~~~~~~~~~~~
395395

396396
Lit has many directives built in (e.g., ``DEFINE``, ``UNSUPPORTED``). In addition to those directives, libc++ adds two additional libc++-specific directives that makes
397-
writing tests easier. See `libc++-specific Lit Directives`_ for more information about the ``FILE_DEPENDENCIES`` and ``ADDITIONAL_COMPILE_FLAGS`` libc++-specific directives.
397+
writing tests easier. See `libc++-specific Lit Directives`_ for more information about the ``FILE_DEPENDENCIES``, ``ADDITIONAL_COMPILE_FLAGS``, and ``MODULE_DEPENDENCIES`` libc++-specific directives.
398398

399399
.. _libc++-specific Lit Directives:
400400
.. list-table:: libc++-specific Lit Directives
@@ -417,6 +417,13 @@ writing tests easier. See `libc++-specific Lit Directives`_ for more information
417417
- The additional compiler flags specified by a space-separated list to the ``ADDITIONAL_COMPILE_FLAGS`` libc++-specific Lit directive will be added to the end of the ``%{compile_flags}``
418418
substitution for the test that contains it. This libc++-specific Lit directive makes it possible to add special compilation flags without having to resort to writing a ``.sh.cpp`` test (see
419419
`Lit Meaning of libc++ Test Filenames`_), more powerful but perhaps overkill.
420+
* - ``MODULE_DEPENDENCIES``
421+
- ``// MODULE_DEPENDENCIES: std std.compat``
422+
- This directive will build the required C++23 standard library
423+
modules and add the additional compiler flags in
424+
%{compile_flags}. (Libc++ offers these modules in C++20 as an
425+
extension.)
426+
420427

421428
Benchmarks
422429
==========

libcxx/modules/std/memory.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,9 @@ export namespace std {
156156
using std::reinterpret_pointer_cast;
157157
using std::static_pointer_cast;
158158

159+
#ifndef _LIBCPP_HAS_NO_RTTI
159160
using std::get_deleter;
161+
#endif // _LIBCPP_HAS_NO_RTTI
160162

161163
// [util.smartptr.shared.io], shared_ptr I/O
162164

libcxx/test/libcxx/module_std.gen.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"%{test-tools}/clang_tidy_checks/libcxx-tidy.plugin",
3131
"%{cxx}",
3232
"%{flags} %{compile_flags}",
33+
"std",
3334
)
3435

3536

libcxx/test/libcxx/module_std_compat.gen.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"%{test-tools}/clang_tidy_checks/libcxx-tidy.plugin",
3131
"%{cxx}",
3232
"%{flags} %{compile_flags}",
33+
"std.compat",
3334
)
3435

3536

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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+
// Make sure that the compile flags contain no module information.
10+
11+
// MODULE_DEPENDENCIES:
12+
13+
// RUN: echo "%{compile_flags}" | grep -v "std.pcm"
14+
// RUN: echo "%{compile_flags}" | grep -v "std.compat.pcm"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10+
// UNSUPPORTED: clang-modules-build
11+
// UNSUPPORTED: gcc
12+
13+
// XFAIL: has-no-cxx-module-support
14+
15+
// Make sure that the compile flags contain the expected elements.
16+
// The tests only look for the expected components and not the exact flags.
17+
// Otherwise changing the location of the module breaks this test.
18+
19+
// MODULE_DEPENDENCIES: std std.compat
20+
21+
// RUN: echo "%{compile_flags}" | grep -- "-fmodule-file=std=.*/std.pcm .*/std.pcm"
22+
// RUN: echo "%{compile_flags}" | grep -- "-fmodule-file=std.compat=.*/std.compat.pcm .*/std.compat.pcm"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10+
// UNSUPPORTED: clang-modules-build
11+
// UNSUPPORTED: gcc
12+
13+
// XFAIL: has-no-cxx-module-support
14+
15+
// Make sure that the compile flags contain the expected elements.
16+
// The tests only look for the expected components and not the exact flags.
17+
// Otherwise changing the location of the module breaks this test.
18+
19+
// MODULE_DEPENDENCIES: std
20+
21+
// RUN: echo "%{compile_flags}" | grep -- "-fmodule-file=std=.*/std.pcm .*/std.pcm"
22+
23+
// The std module should not provide the std.compat module
24+
// RUN: echo "%{compile_flags}" | grep -v "std.compat.pcm"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10+
// UNSUPPORTED: clang-modules-build
11+
// UNSUPPORTED: gcc
12+
13+
// XFAIL: has-no-cxx-module-support
14+
15+
// Make sure that the compile flags contain the expected elements.
16+
// The tests only look for the expected components and not the exact flags.
17+
// Otherwise changing the location of the module breaks this test.
18+
19+
// MODULE_DEPENDENCIES: std.compat
20+
21+
// RUN: echo "%{compile_flags}" | grep -- "-fmodule-file=std.compat=.*/std.compat.pcm .*/std.compat.pcm"
22+
23+
// It's unspecified whether std.compat is built on the std module.
24+
// Therefore don't test its presence

libcxx/test/std/modules/std.compat.pass.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@
88

99
// UNSUPPORTED: c++03, c++11, c++14, c++17
1010
// UNSUPPORTED: clang-modules-build
11+
// UNSUPPORTED: gcc
1112

12-
// XFAIL: *
13+
// XFAIL: has-no-cxx-module-support
1314

1415
// A minimal test to validate import works.
1516

17+
// MODULE_DEPENDENCIES: std.compat
18+
1619
import std.compat;
1720

1821
int main(int, char**) { return !(::strlen("Hello modular world") == 19); }

libcxx/test/std/modules/std.pass.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@
88

99
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
1010
// UNSUPPORTED: clang-modules-build
11+
// UNSUPPORTED: gcc
1112

12-
// XFAIL: *
13+
// XFAIL: has-no-cxx-module-support
1314

1415
// A minimal test to validate import works.
1516

17+
// MODULE_DEPENDENCIES: std
18+
1619
import std;
1720

1821
int main(int, char**) {

libcxx/utils/libcxx/test/features.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,18 @@ def _getAndroidDeviceApi(cfg):
317317
AddSubstitution("%{clang-tidy}", lambda cfg: _getSuitableClangTidy(cfg))
318318
],
319319
),
320+
# Whether module support for the platform is available.
321+
Feature(
322+
name="has-no-cxx-module-support",
323+
# The libc of these platforms have functions with internal linkage.
324+
# This is not allowed per C11 7.1.2 Standard headers/6
325+
# Any declaration of a library function shall have external linkage.
326+
when=lambda cfg: "__ANDROID__" in compilerMacros(cfg)
327+
or "__PICOLIBC__" in compilerMacros(cfg)
328+
or platform.system().lower().startswith("aix")
329+
# Avoid building on platforms that don't support modules properly.
330+
or not hasCompileFlag(cfg, "-Wno-reserved-module-identifier"),
331+
),
320332
]
321333

322334
# Deduce and add the test features that that are implied by the #defines in

libcxx/utils/libcxx/test/format.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,21 @@ def _executeScriptInternal(test, litConfig, commands):
5252
return (out, err, exitCode, timeoutInfo, parsedCommands)
5353

5454

55+
def _validateModuleDependencies(modules):
56+
for m in modules:
57+
if m not in ("std", "std.compat"):
58+
raise RuntimeError(
59+
f"Invalid module dependency '{m}', only 'std' and 'std.compat' are valid"
60+
)
61+
62+
63+
def _getSubstitution(substitution, config):
64+
for (orig, replacement) in config.substitutions:
65+
if orig == substitution:
66+
return replacement
67+
raise ValueError("Substitution {} is not in the config.".format(substitution))
68+
69+
5570
def parseScript(test, preamble):
5671
"""
5772
Extract the script from a test, with substitutions applied.
@@ -91,6 +106,8 @@ def parseScript(test, preamble):
91106
# Parse the test file, including custom directives
92107
additionalCompileFlags = []
93108
fileDependencies = []
109+
modules = [] # The enabled modules
110+
moduleCompileFlags = [] # The compilation flags to use modules
94111
parsers = [
95112
lit.TestRunner.IntegratedTestKeywordParser(
96113
"FILE_DEPENDENCIES:",
@@ -102,6 +119,11 @@ def parseScript(test, preamble):
102119
lit.TestRunner.ParserKind.SPACE_LIST,
103120
initial_value=additionalCompileFlags,
104121
),
122+
lit.TestRunner.IntegratedTestKeywordParser(
123+
"MODULE_DEPENDENCIES:",
124+
lit.TestRunner.ParserKind.SPACE_LIST,
125+
initial_value=modules,
126+
),
105127
]
106128

107129
# Add conditional parsers for ADDITIONAL_COMPILE_FLAGS. This should be replaced by first
@@ -132,13 +154,62 @@ def parseScript(test, preamble):
132154
script += scriptInTest
133155

134156
# Add compile flags specified with ADDITIONAL_COMPILE_FLAGS.
157+
# Modules need to be build with the same compilation flags as the
158+
# test. So add these flags before adding the modules.
135159
substitutions = [
136160
(s, x + " " + " ".join(additionalCompileFlags))
137161
if s == "%{compile_flags}"
138162
else (s, x)
139163
for (s, x) in substitutions
140164
]
141165

166+
if modules:
167+
_validateModuleDependencies(modules)
168+
169+
# This flag is needed for both modules.
170+
#moduleCompileFlags.append("-fprebuilt-module-path=%T")
171+
172+
# The moduleCompileFlags are added to the %{compile_flags}, but
173+
# the modules need should be built without these flags. So
174+
# expand the compile_flags and add the expanded value to the
175+
# build script.
176+
compileFlags = _getSubstitution("%{compile_flags}", test.config)
177+
178+
# Building the modules needs to happen before the other script
179+
# commands are executed. Therefore the commands are added to the
180+
# front of the list.
181+
if "std.compat" in modules:
182+
script.insert(
183+
0,
184+
"%dbg(MODULE std.compat) %{cxx} %{flags} "
185+
f"{compileFlags} "
186+
"-Wno-reserved-module-identifier -Wno-reserved-user-defined-literal "
187+
"--precompile -o %T/std.compat.pcm -c %{module}/std.compat.cppm",
188+
)
189+
moduleCompileFlags.append("-fmodule-file=std.compat=%T/std.compat.pcm %T/std.compat.pcm")
190+
191+
# Make sure the std module is added before std.compat. Libc++'s
192+
# std.compat module will depend on its std module. It is not
193+
# known whether the compiler expects the modules in the order of
194+
# their dependencies. However it's trivial to provide them in
195+
# that order.
196+
script.insert(
197+
0,
198+
"%dbg(MODULE std) %{cxx} %{flags} "
199+
f"{compileFlags} "
200+
"-Wno-reserved-module-identifier -Wno-reserved-user-defined-literal "
201+
"--precompile -o %T/std.pcm -c %{module}/std.cppm",
202+
)
203+
moduleCompileFlags.append("-fmodule-file=std=%T/std.pcm %T/std.pcm")
204+
205+
# Add compile flags required for the modules.
206+
substitutions = [
207+
(s, x + " " + " ".join(moduleCompileFlags))
208+
if s == "%{compile_flags}"
209+
else (s, x)
210+
for (s, x) in substitutions
211+
]
212+
142213
# Perform substitutions in the script itself.
143214
script = lit.TestRunner.applySubstitutions(
144215
script, substitutions, recursion_limit=test.config.recursiveExpansionLimit

libcxx/utils/libcxx/test/modules.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,20 +113,21 @@ class module_test_generator:
113113
clang_tidy_plugin: str
114114
compiler: str
115115
compiler_flags: str
116+
module: str
116117

117118
def write_lit_configuration(self):
118119
print(
119120
f"""\
120121
// UNSUPPORTED: c++03, c++11, c++14, c++17
121122
// UNSUPPORTED: clang-modules-build
122123
123-
// XFAIL: *
124-
125124
// REQUIRES: has-clang-tidy
126125
127126
// The GCC compiler flags are not always compatible with clang-tidy.
128127
// UNSUPPORTED: gcc
129128
129+
// MODULE_DEPENDENCIES: {self.module}
130+
130131
// RUN: echo -n > {self.tmp_prefix}.all_partitions
131132
"""
132133
)

0 commit comments

Comments
 (0)