Skip to content

Commit a6f3772

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 d06ae33 commit a6f3772

13 files changed

+184
-11
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/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 would break 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 would break 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 would break 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/config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ def _getSubstitution(substitution, config):
1616
raise ValueError("Substitution {} is not in the config.".format(substitution))
1717

1818

19+
def _appendToSubstitution(substitutions, key, value):
20+
return [(k, v + " " + value) if k == key else (k, v) for (k, v) in substitutions]
21+
22+
1923
def configure(parameters, features, config, lit_config):
2024
note = lambda s: lit_config.note("({}) {}".format(config.name, s))
2125
config.environment = dict(os.environ)

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: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# ===----------------------------------------------------------------------===##
88

99
import lit
10+
import libcxx.test.config as config
1011
import lit.formats
1112
import os
1213
import re
@@ -52,6 +53,14 @@ def _executeScriptInternal(test, litConfig, commands):
5253
return (out, err, exitCode, timeoutInfo, parsedCommands)
5354

5455

56+
def _validateModuleDependencies(modules):
57+
for m in modules:
58+
if m not in ("std", "std.compat"):
59+
raise RuntimeError(
60+
f"Invalid module dependency '{m}', only 'std' and 'std.compat' are valid"
61+
)
62+
63+
5564
def parseScript(test, preamble):
5665
"""
5766
Extract the script from a test, with substitutions applied.
@@ -91,6 +100,8 @@ def parseScript(test, preamble):
91100
# Parse the test file, including custom directives
92101
additionalCompileFlags = []
93102
fileDependencies = []
103+
modules = [] # The enabled modules
104+
moduleCompileFlags = [] # The compilation flags to use modules
94105
parsers = [
95106
lit.TestRunner.IntegratedTestKeywordParser(
96107
"FILE_DEPENDENCIES:",
@@ -102,6 +113,11 @@ def parseScript(test, preamble):
102113
lit.TestRunner.ParserKind.SPACE_LIST,
103114
initial_value=additionalCompileFlags,
104115
),
116+
lit.TestRunner.IntegratedTestKeywordParser(
117+
"MODULE_DEPENDENCIES:",
118+
lit.TestRunner.ParserKind.SPACE_LIST,
119+
initial_value=modules,
120+
),
105121
]
106122

107123
# Add conditional parsers for ADDITIONAL_COMPILE_FLAGS. This should be replaced by first
@@ -132,12 +148,53 @@ def parseScript(test, preamble):
132148
script += scriptInTest
133149

134150
# Add compile flags specified with ADDITIONAL_COMPILE_FLAGS.
135-
substitutions = [
136-
(s, x + " " + " ".join(additionalCompileFlags))
137-
if s == "%{compile_flags}"
138-
else (s, x)
139-
for (s, x) in substitutions
140-
]
151+
# Modules need to be built with the same compilation flags as the
152+
# test. So add these flags before adding the modules.
153+
substitutions = config._appendToSubstitution(
154+
substitutions, "%{compile_flags}", " ".join(additionalCompileFlags)
155+
)
156+
157+
if modules:
158+
_validateModuleDependencies(modules)
159+
160+
# The moduleCompileFlags are added to the %{compile_flags}, but
161+
# the modules need to be built without these flags. So expand the
162+
# %{compile_flags} eagerly and hardcode them in the build script.
163+
compileFlags = config._getSubstitution("%{compile_flags}", test.config)
164+
165+
# Building the modules needs to happen before the other script
166+
# commands are executed. Therefore the commands are added to the
167+
# front of the list.
168+
if "std.compat" in modules:
169+
script.insert(
170+
0,
171+
"%dbg(MODULE std.compat) %{cxx} %{flags} "
172+
f"{compileFlags} "
173+
"-Wno-reserved-module-identifier -Wno-reserved-user-defined-literal "
174+
"--precompile -o %T/std.compat.pcm -c %{module}/std.compat.cppm",
175+
)
176+
moduleCompileFlags.extend(
177+
["-fmodule-file=std.compat=%T/std.compat.pcm", "%T/std.compat.pcm"]
178+
)
179+
180+
# Make sure the std module is built before std.compat. Libc++'s
181+
# std.compat module depends on the std module. It is not
182+
# known whether the compiler expects the modules in the order of
183+
# their dependencies. However it's trivial to provide them in
184+
# that order.
185+
script.insert(
186+
0,
187+
"%dbg(MODULE std) %{cxx} %{flags} "
188+
f"{compileFlags} "
189+
"-Wno-reserved-module-identifier -Wno-reserved-user-defined-literal "
190+
"--precompile -o %T/std.pcm -c %{module}/std.cppm",
191+
)
192+
moduleCompileFlags.extend(["-fmodule-file=std=%T/std.pcm", "%T/std.pcm"])
193+
194+
# Add compile flags required for the modules.
195+
substitutions = config._appendToSubstitution(
196+
substitutions, "%{compile_flags}", " ".join(moduleCompileFlags)
197+
)
141198

142199
# Perform substitutions in the script itself.
143200
script = lit.TestRunner.applySubstitutions(

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)