Skip to content

Commit fe2406d

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 5c5c968 commit fe2406d

12 files changed

+194
-11
lines changed

libcxx/test/libcxx/module_std.gen.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"%{clang-tidy}",
3030
"%{test-tools}/clang_tidy_checks/libcxx-tidy.plugin",
3131
"%{cxx}",
32-
"%{flags} %{compile_flags}",
32+
"%{flags} %{compile_flags} %{module_flags}",
33+
"std",
3334
)
3435

3536

libcxx/test/libcxx/module_std_compat.gen.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"%{clang-tidy}",
3030
"%{test-tools}/clang_tidy_checks/libcxx-tidy.plugin",
3131
"%{cxx}",
32-
"%{flags} %{compile_flags}",
32+
"%{flags} %{compile_flags} %{module_flags}",
33+
"std.compat",
3334
)
3435

3536

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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 module flags are empty when no module is supplied.
10+
11+
// MODULES:
12+
// RUN: echo "%{module_flags}" | grep "^$"
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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-module-support
14+
15+
// Make sure that the module 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+
// MODULES: std std.compat
20+
//
21+
// RUN: echo "%{module_flags}" | grep -- "-fprebuilt-module-path="
22+
// RUN: echo "%{module_flags}" | grep "std.pcm"
23+
// RUN: echo "%{module_flags}" | grep "std.compat.pcm"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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-module-support
14+
15+
// Make sure that the module 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+
// MODULES: std
20+
//
21+
// RUN: echo "%{module_flags}" | grep -- "-fprebuilt-module-path="
22+
// RUN: echo "%{module_flags}" | grep "std.pcm"
23+
24+
// The std module should not provide the std.compat module
25+
// RUN: echo "%{module_flags}" | grep -v "std.compat.pcm"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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-module-support
14+
15+
// Make sure that the module 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+
// MODULES: std.compat
20+
//
21+
// RUN: echo "%{module_flags}" | grep -- "-fprebuilt-module-path="
22+
// RUN: echo "%{module_flags}" | grep "std.compat.pcm"
23+
24+
// It's unspecified whether std.compat is built on the std module.
25+
// Therefore don't test its presence
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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 modules that are unknown fail.
10+
11+
// MODULES: this_module_is_not_a_standard_library_module
12+
13+
// XFAIL: *

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-module-support
1314

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

17+
// MODULES: 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-module-support
1314

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

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

1821
int main(int, char**) {

libcxx/utils/libcxx/test/features.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,19 @@ 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-module-support",
323+
# The libc of these platforms have functions with internal linkages.
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+
),
332+
320333
]
321334

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

libcxx/utils/libcxx/test/format.py

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ def parseScript(test, preamble):
9191
# Parse the test file, including custom directives
9292
additionalCompileFlags = []
9393
fileDependencies = []
94+
modules = [] # The enabled modules
95+
moduleCompileFlags = [] # The compilation flags to use modules
9496
parsers = [
9597
lit.TestRunner.IntegratedTestKeywordParser(
9698
"FILE_DEPENDENCIES:",
@@ -102,6 +104,11 @@ def parseScript(test, preamble):
102104
lit.TestRunner.ParserKind.SPACE_LIST,
103105
initial_value=additionalCompileFlags,
104106
),
107+
lit.TestRunner.IntegratedTestKeywordParser(
108+
"MODULES:",
109+
lit.TestRunner.ParserKind.SPACE_LIST,
110+
initial_value=modules,
111+
),
105112
]
106113

107114
# Add conditional parsers for ADDITIONAL_COMPILE_FLAGS. This should be replaced by first
@@ -131,13 +138,62 @@ def parseScript(test, preamble):
131138
script += preamble
132139
script += scriptInTest
133140

141+
has_std_module = False
142+
has_std_compat_module = False
143+
for module in modules:
144+
if module == "std":
145+
has_std_module = True
146+
elif module == "std.compat":
147+
has_std_compat_module = True
148+
else:
149+
script.insert(0, f"echo \"The module '{module}' is not valid, use 'std' or 'std.compat'\"")
150+
script.insert(1, "false");
151+
return script
152+
153+
if modules:
154+
# This flag is needed for both modules.
155+
moduleCompileFlags.append("-fprebuilt-module-path=%T")
156+
157+
# Building the modules needs to happen before the other script commands
158+
# are executed. Therefore the commands are added to the front of the
159+
# list.
160+
if has_std_compat_module:
161+
script.insert(
162+
0,
163+
"%dbg(MODULE std.compat) %{cxx} %{flags} %{compile_flags} "
164+
"-Wno-reserved-module-identifier -Wno-reserved-user-defined-literal "
165+
"--precompile -o %T/std.compat.pcm -c %{module}/std.compat.cppm",
166+
)
167+
moduleCompileFlags.append("%T/std.compat.pcm")
168+
169+
# Make sure the std module is added before std.compat.
170+
# Libc++'s std.compat module will depend on its std module.
171+
# It is not known whether the compiler expects the modules in the order
172+
# of their dependencies. However it's trivial to provide them in that
173+
# order.
174+
if has_std_module:
175+
script.insert(
176+
0,
177+
"%dbg(MODULE std) %{cxx} %{flags} %{compile_flags} "
178+
"-Wno-reserved-module-identifier -Wno-reserved-user-defined-literal "
179+
"--precompile -o %T/std.pcm -c %{module}/std.cppm",
180+
)
181+
moduleCompileFlags.append("%T/std.pcm")
182+
134183
# Add compile flags specified with ADDITIONAL_COMPILE_FLAGS.
135184
substitutions = [
136185
(s, x + " " + " ".join(additionalCompileFlags))
137186
if s == "%{compile_flags}"
138187
else (s, x)
139188
for (s, x) in substitutions
140189
]
190+
# In order to use modules additional compilation flags are required.
191+
# Adding these to the %{compile_flags} gives a chicken and egg issue:
192+
# - the modules need to be build with the same compilation flags as the
193+
# tests,
194+
# - except for the module dependency, which does not exist.
195+
# The issue is resolved by adding a private substitution.
196+
substitutions.append(("%{module_flags}", " ".join(moduleCompileFlags)))
141197

142198
# Perform substitutions in the script itself.
143199
script = lit.TestRunner.applySubstitutions(
@@ -191,6 +247,7 @@ class CxxStandardLibraryTest(lit.formats.FileBasedTest):
191247
constructs:
192248
%{cxx} - A command that can be used to invoke the compiler
193249
%{compile_flags} - Flags to use when compiling a test case
250+
%{module_flags} - Flags to use when compiling a test case that imports modules
194251
%{link_flags} - Flags to use when linking a test case
195252
%{flags} - Flags to use either when compiling or linking a test case
196253
%{exec} - A command to prefix the execution of executables
@@ -223,6 +280,12 @@ class CxxStandardLibraryTest(lit.formats.FileBasedTest):
223280
allows adding special compilation flags without having to use a
224281
.sh.cpp test, which would be more powerful but perhaps overkill.
225282
283+
// MODULE: std std.compat
284+
285+
This directive will build the required C++23 standard library
286+
modules and add the provide the additional compiler flags in
287+
%{module_flags}. (Libc++ offers these modules in C++20 as an
288+
extenstion.)
226289
227290
Additional provided substitutions and features
228291
==============================================
@@ -288,22 +351,22 @@ def execute(self, test, litConfig):
288351
".compile.pass.mm"
289352
):
290353
steps = [
291-
"%dbg(COMPILED WITH) %{cxx} %s %{flags} %{compile_flags} -fsyntax-only"
354+
"%dbg(COMPILED WITH) %{cxx} %s %{flags} %{compile_flags} %{module_flags} -fsyntax-only"
292355
]
293356
return self._executeShTest(test, litConfig, steps)
294357
elif filename.endswith(".compile.fail.cpp"):
295358
steps = [
296-
"%dbg(COMPILED WITH) ! %{cxx} %s %{flags} %{compile_flags} -fsyntax-only"
359+
"%dbg(COMPILED WITH) ! %{cxx} %s %{flags} %{compile_flags} %{module_flags} -fsyntax-only"
297360
]
298361
return self._executeShTest(test, litConfig, steps)
299362
elif filename.endswith(".link.pass.cpp") or filename.endswith(".link.pass.mm"):
300363
steps = [
301-
"%dbg(COMPILED WITH) %{cxx} %s %{flags} %{compile_flags} %{link_flags} -o %t.exe"
364+
"%dbg(COMPILED WITH) %{cxx} %s %{flags} %{compile_flags} %{module_flags} %{link_flags} -o %t.exe"
302365
]
303366
return self._executeShTest(test, litConfig, steps)
304367
elif filename.endswith(".link.fail.cpp"):
305368
steps = [
306-
"%dbg(COMPILED WITH) %{cxx} %s %{flags} %{compile_flags} -c -o %t.o",
369+
"%dbg(COMPILED WITH) %{cxx} %s %{flags} %{compile_flags} %{module_flags} -c -o %t.o",
307370
"%dbg(LINKED WITH) ! %{cxx} %t.o %{flags} %{link_flags} -o %t.exe",
308371
]
309372
return self._executeShTest(test, litConfig, steps)
@@ -321,7 +384,7 @@ def execute(self, test, litConfig):
321384
# suffixes above too.
322385
elif filename.endswith(".pass.cpp") or filename.endswith(".pass.mm"):
323386
steps = [
324-
"%dbg(COMPILED WITH) %{cxx} %s %{flags} %{compile_flags} %{link_flags} -o %t.exe",
387+
"%dbg(COMPILED WITH) %{cxx} %s %{flags} %{compile_flags} %{module_flags} %{link_flags} -o %t.exe",
325388
"%dbg(EXECUTED AS) %{exec} %t.exe",
326389
]
327390
return self._executeShTest(test, litConfig, steps)

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+
// MODULES: {self.module}
130+
130131
// RUN: echo -n > {self.tmp_prefix}.all_partitions
131132
"""
132133
)

0 commit comments

Comments
 (0)