• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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# ===----------------------------------------------------------------------===##
8import sys
9import re
10import shlex
11from pathlib import Path
12
13from libcxx.test.dsl import *
14from libcxx.test.features import _isClang, _isAppleClang, _isGCC, _isMSVC
15
16
17_warningFlags = [
18    "-Werror",
19    "-Wall",
20    "-Wctad-maybe-unsupported",
21    "-Wextra",
22    "-Wshadow",
23    "-Wundef",
24    "-Wunused-template",
25    "-Wno-unused-command-line-argument",
26    "-Wno-attributes",
27    "-Wno-pessimizing-move",
28    "-Wno-noexcept-type",
29    "-Wno-aligned-allocation-unavailable",
30    "-Wno-atomic-alignment",
31    "-Wno-reserved-module-identifier",
32    '-Wdeprecated-copy',
33    '-Wdeprecated-copy-dtor',
34    # GCC warns about places where we might want to add sized allocation/deallocation
35    # functions, but we know better what we're doing/testing in the test suite.
36    "-Wno-sized-deallocation",
37    # Turn off warnings about user-defined literals with reserved suffixes. Those are
38    # just noise since we are testing the Standard Library itself.
39    "-Wno-literal-suffix",  # GCC
40    "-Wno-user-defined-literals",  # Clang
41    # GCC warns about this when TEST_IS_CONSTANT_EVALUATED is used on a non-constexpr
42    # function. (This mostly happens in C++11 mode.)
43    # TODO(mordante) investigate a solution for this issue.
44    "-Wno-tautological-compare",
45    # -Wstringop-overread and -Wstringop-overflow seem to be a bit buggy currently
46    "-Wno-stringop-overread",
47    "-Wno-stringop-overflow",
48    # These warnings should be enabled in order to support the MSVC
49    # team using the test suite; They enable the warnings below and
50    # expect the test suite to be clean.
51    "-Wsign-compare",
52    "-Wunused-variable",
53    "-Wunused-parameter",
54    "-Wunreachable-code",
55    "-Wno-unused-local-typedef",
56
57    # Disable warnings for extensions used in C++03
58    "-Wno-local-type-template-args",
59    "-Wno-c++11-extensions",
60
61    # TODO(philnik) This fails with the PSTL.
62    "-Wno-unknown-pragmas",
63    # Don't fail compilation in case the compiler fails to perform the requested
64    # loop vectorization.
65    "-Wno-pass-failed",
66
67    # TODO: Find out why GCC warns in lots of places (is this a problem with always_inline?)
68    "-Wno-dangling-reference",
69    "-Wno-mismatched-new-delete",
70    "-Wno-redundant-move",
71
72    # This doesn't make sense in real code, but we have to test it because the standard requires us to not break
73    "-Wno-self-move",
74]
75
76_allStandards = ["c++03", "c++11", "c++14", "c++17", "c++20", "c++23", "c++26"]
77
78
79def getStdFlag(cfg, std):
80    if hasCompileFlag(cfg, "-std=" + std):
81        return "-std=" + std
82    # TODO(LLVM-19) Remove the fallbacks needed for Clang 16.
83    fallbacks = {
84        "c++23": "c++2b",
85    }
86    if std in fallbacks and hasCompileFlag(cfg, "-std=" + fallbacks[std]):
87        return "-std=" + fallbacks[std]
88    return None
89
90
91def getDefaultStdValue(cfg):
92    viable = [s for s in reversed(_allStandards) if getStdFlag(cfg, s)]
93
94    if not viable:
95        raise RuntimeError(
96            "Unable to successfully detect the presence of any -std=c++NN flag. This likely indicates an issue with your compiler."
97        )
98
99    return viable[0]
100
101
102def getSpeedOptimizationFlag(cfg):
103    if _isClang(cfg) or _isAppleClang(cfg) or _isGCC(cfg):
104        return "-O3"
105    elif _isMSVC(cfg):
106        return "/O2"
107    else:
108        raise RuntimeError(
109            "Can't figure out what compiler is used in the configuration"
110        )
111
112
113def getSizeOptimizationFlag(cfg):
114    if _isClang(cfg) or _isAppleClang(cfg) or _isGCC(cfg):
115        return "-Os"
116    elif _isMSVC(cfg):
117        return "/O1"
118    else:
119        raise RuntimeError(
120            "Can't figure out what compiler is used in the configuration"
121        )
122
123
124def testClangTidy(cfg, version, executable):
125    try:
126        if version in commandOutput(cfg, [f"{executable} --version"]):
127            return executable
128    except ConfigurationRuntimeError:
129        return None
130
131
132def getSuitableClangTidy(cfg):
133    # If we didn't build the libcxx-tidy plugin via CMake, we can't run the clang-tidy tests.
134    if (
135        runScriptExitCode(
136            cfg, ["stat %{test-tools-dir}/clang_tidy_checks/libcxx-tidy.plugin"]
137        )
138        != 0
139    ):
140        return None
141
142    version = "{__clang_major__}.{__clang_minor__}.{__clang_patchlevel__}".format(
143        **compilerMacros(cfg)
144    )
145    exe = testClangTidy(
146        cfg, version, "clang-tidy-{__clang_major__}".format(**compilerMacros(cfg))
147    )
148
149    if not exe:
150        exe = testClangTidy(cfg, version, "clang-tidy")
151
152    return exe
153
154
155# fmt: off
156DEFAULT_PARAMETERS = [
157    Parameter(
158        name="compiler",
159        type=str,
160        help="The path of the compiler to use for testing.",
161        actions=lambda cxx: [
162            AddSubstitution("%{cxx}", shlex.quote(cxx)),
163        ],
164    ),
165    Parameter(
166        name="target_triple",
167        type=str,
168        help="The target triple to compile the test suite for. This must be "
169        "compatible with the target that the tests will be run on.",
170        actions=lambda triple: filter(
171            None,
172            [
173                AddFeature("target={}".format(triple)),
174                AddFlagIfSupported("--target={}".format(triple)),
175                AddSubstitution("%{triple}", triple),
176            ],
177        ),
178    ),
179    Parameter(
180        name="std",
181        choices=_allStandards,
182        type=str,
183        help="The version of the standard to compile the test suite with.",
184        default=lambda cfg: getDefaultStdValue(cfg),
185        actions=lambda std: [
186            AddFeature(std),
187            AddSubstitution("%{cxx_std}", re.sub(r"\+", "x", std)),
188            AddCompileFlag(lambda cfg: getStdFlag(cfg, std)),
189        ],
190    ),
191    Parameter(
192        name="optimization",
193        choices=["none", "speed", "size"],
194        type=str,
195        help="The optimization level to use when compiling the test suite.",
196        default="none",
197        actions=lambda opt: filter(None, [
198            AddCompileFlag(lambda cfg: getSpeedOptimizationFlag(cfg)) if opt == "speed" else None,
199            AddCompileFlag(lambda cfg: getSizeOptimizationFlag(cfg)) if opt == "size" else None,
200            AddFeature(f'optimization={opt}'),
201        ]),
202    ),
203    Parameter(
204        name="enable_modules",
205        choices=["none", "clang", "clang-lsv"],
206        type=str,
207        help="Whether to build the test suite with modules enabled. "
208             "Select `clang` for Clang modules, and 'clang-lsv' for Clang modules with Local Submodule Visibility.",
209        default="none",
210        actions=lambda modules: filter(None, [
211            AddFeature("clang-modules-build")           if modules in ("clang", "clang-lsv") else None,
212
213            # Note: AppleClang disregards -fmodules entirely when compiling C++, so we also pass -fcxx-modules
214            #       to enable modules for C++.
215            AddCompileFlag("-fmodules -fcxx-modules")   if modules in ("clang", "clang-lsv") else None,
216
217            # Note: We use a custom modules cache path to make sure that we don't reuse
218            #       the default one, which can be shared across CI builds with different
219            #       configurations.
220            AddCompileFlag(lambda cfg: f"-fmodules-cache-path={cfg.test_exec_root}/ModuleCache") if modules in ("clang", "clang-lsv") else None,
221
222            AddCompileFlag("-Xclang -fmodules-local-submodule-visibility") if modules == "clang-lsv" else None,
223        ])
224    ),
225    Parameter(
226        name="enable_exceptions",
227        choices=[True, False],
228        type=bool,
229        default=True,
230        help="Whether to enable exceptions when compiling the test suite.",
231        actions=lambda exceptions: [] if exceptions else [
232            AddFeature("no-exceptions"),
233            AddCompileFlag("-fno-exceptions")
234        ],
235    ),
236    Parameter(
237        name="enable_rtti",
238        choices=[True, False],
239        type=bool,
240        default=True,
241        help="Whether to enable RTTI when compiling the test suite.",
242        actions=lambda rtti: [] if rtti else [
243            AddFeature("no-rtti"),
244            AddCompileFlag("-fno-rtti")
245        ],
246    ),
247    Parameter(
248        name="stdlib",
249        choices=["llvm-libc++", "apple-libc++", "libstdc++", "msvc"],
250        type=str,
251        default="llvm-libc++",
252        help="""The C++ Standard Library implementation being tested.
253
254                 Note that this parameter can also be used to encode different 'flavors' of the same
255                 standard library, such as libc++ as shipped by a different vendor, if it has different
256                 properties worth testing.
257
258                 The Standard libraries currently supported are:
259                 - llvm-libc++: The 'upstream' libc++ as shipped with LLVM.
260                 - apple-libc++: libc++ as shipped by Apple. This is basically like the LLVM one, but
261                                 there are a few differences like installation paths, the use of
262                                 universal dylibs and the existence of availability markup.
263                 - libstdc++: The GNU C++ library typically shipped with GCC.
264                 - msvc: The Microsoft implementation of the C++ Standard Library.
265                """,
266        actions=lambda stdlib: filter(
267            None,
268            [
269                AddFeature("stdlib={}".format(stdlib)),
270                # Also add an umbrella feature 'stdlib=libc++' for all flavors of libc++, to simplify
271                # the test suite.
272                AddFeature("stdlib=libc++") if re.match(r".+-libc\+\+", stdlib) else None,
273            ],
274        ),
275    ),
276    Parameter(
277        name="using_system_stdlib",
278        choices=[True, False],
279        type=bool,
280        default=False,
281        help="""Whether the Standard Library being tested is the one that shipped with the system by default.
282
283                This is different from the 'stdlib' parameter, which describes the flavor of libc++ being
284                tested. 'using_system_stdlib' describes whether the target system passed with 'target_triple'
285                also corresponds to the version of the library being tested.
286             """,
287        actions=lambda is_system: [AddFeature("stdlib=system")] if is_system else [],
288    ),
289    Parameter(
290        name="enable_warnings",
291        choices=[True, False],
292        type=bool,
293        default=True,
294        help="Whether to enable warnings when compiling the test suite.",
295        actions=lambda warnings: [] if not warnings else
296            [AddOptionalWarningFlag(w) for w in _warningFlags] +
297            [AddCompileFlag("-D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER")],
298    ),
299    Parameter(
300        name="use_sanitizer",
301        choices=[
302            "",
303            "Address",
304            "HWAddress",
305            "Undefined",
306            "Memory",
307            "MemoryWithOrigins",
308            "Thread",
309            "DataFlow",
310            "Leaks",
311        ],
312        type=str,
313        default="",
314        help="An optional sanitizer to enable when building and running the test suite.",
315        actions=lambda sanitizer: filter(
316            None,
317            [
318                AddFlag("-g -fno-omit-frame-pointer") if sanitizer else None,
319
320                AddFlag("-fsanitize=undefined -fno-sanitize=float-divide-by-zero -fno-sanitize-recover=all") if sanitizer == "Undefined" else None,
321                AddFeature("ubsan")                                                                          if sanitizer == "Undefined" else None,
322
323                AddFlag("-fsanitize=address") if sanitizer == "Address" else None,
324                AddFeature("asan")            if sanitizer == "Address" else None,
325
326                AddFlag("-fsanitize=hwaddress") if sanitizer == "HWAddress" else None,
327                AddFeature("hwasan")            if sanitizer == "HWAddress" else None,
328
329                AddFlag("-fsanitize=memory")               if sanitizer in ["Memory", "MemoryWithOrigins"] else None,
330                AddFeature("msan")                         if sanitizer in ["Memory", "MemoryWithOrigins"] else None,
331                AddFlag("-fsanitize-memory-track-origins") if sanitizer == "MemoryWithOrigins" else None,
332
333                AddFlag("-fsanitize=thread") if sanitizer == "Thread" else None,
334                AddFeature("tsan")           if sanitizer == "Thread" else None,
335
336                AddFlag("-fsanitize=dataflow") if sanitizer == "DataFlow" else None,
337                AddFlag("-fsanitize=leaks")    if sanitizer == "Leaks" else None,
338
339                AddFeature("sanitizer-new-delete") if sanitizer in ["Address", "HWAddress", "Memory", "MemoryWithOrigins", "Thread"] else None,
340                AddFeature("lsan") if sanitizer in ["Address", "HWAddress", "Leaks"] else None,
341            ]
342        )
343    ),
344    Parameter(
345        name="enable_experimental",
346        choices=[True, False],
347        type=bool,
348        default=True,
349        help="Whether to enable tests for experimental C++ Library features.",
350        actions=lambda experimental: [
351            # When linking in MSVC mode via the Clang driver, a -l<foo>
352            # maps to <foo>.lib, so we need to use -llibc++experimental here
353            # to make it link against the static libc++experimental.lib.
354            # We can't check for the feature 'msvc' in available_features
355            # as those features are added after processing parameters.
356            AddFeature("c++experimental"),
357            PrependLinkFlag(lambda cfg: "-llibc++experimental" if _isMSVC(cfg) else "-lc++experimental"),
358            AddCompileFlag("-D_LIBCPP_ENABLE_EXPERIMENTAL"),
359        ]
360        if experimental
361        else [
362            AddFeature("libcpp-has-no-incomplete-pstl"),
363            AddFeature("libcpp-has-no-experimental-tzdb"),
364            AddFeature("libcpp-has-no-experimental-syncstream"),
365        ],
366    ),
367    # TODO: This can be improved once we use a version of GoogleBenchmark that supports the dry-run mode.
368    #       See https://github.com/google/benchmark/issues/1827.
369    Parameter(
370        name="enable_benchmarks",
371        choices=["no", "run", "dry-run"],
372        type=str,
373        default="run",
374        help="Whether to run the benchmarks in the test suite, to only dry-run them or to disable them entirely.",
375        actions=lambda mode: [AddFeature(f"enable-benchmarks={mode}")],
376    ),
377    Parameter(
378        name="long_tests",
379        choices=[True, False],
380        type=bool,
381        default=True,
382        help="Whether to enable tests that take longer to run. This can be useful when running on a very slow device.",
383        actions=lambda enabled: [] if not enabled else [AddFeature("long_tests")],
384    ),
385    Parameter(
386        name="large_tests",
387        choices=[True, False],
388        type=bool,
389        default=True,
390        help="Whether to enable tests that use a lot of memory. This can be useful when running on a device with limited amounts of memory.",
391        actions=lambda enabled: [] if not enabled else [AddFeature("large_tests")],
392    ),
393    Parameter(
394        name="hardening_mode",
395        choices=["none", "fast", "extensive", "debug", "undefined"],
396        type=str,
397        default="undefined",
398        help="Whether to enable one of the hardening modes when compiling the test suite. This is only "
399        "meaningful when running the tests against libc++. By default, no hardening mode is specified "
400        "so the default hardening mode of the standard library will be used (if any).",
401        actions=lambda hardening_mode: filter(
402            None,
403            [
404                AddCompileFlag("-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE")      if hardening_mode == "none" else None,
405                AddCompileFlag("-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST")      if hardening_mode == "fast" else None,
406                AddCompileFlag("-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE") if hardening_mode == "extensive" else None,
407                AddCompileFlag("-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG")     if hardening_mode == "debug" else None,
408                AddFeature("libcpp-hardening-mode={}".format(hardening_mode))               if hardening_mode != "undefined" else None,
409            ],
410        ),
411    ),
412    Parameter(
413        name="additional_features",
414        type=list,
415        default=[],
416        help="A comma-delimited list of additional features that will be enabled when running the tests. "
417        "This should be used sparingly since specifying ad-hoc features manually is error-prone and "
418        "brittle in the long run as changes are made to the test suite.",
419        actions=lambda features: [AddFeature(f) for f in features],
420    ),
421    Parameter(
422        name="enable_transitive_includes",
423        choices=[True, False],
424        type=bool,
425        default=True,
426        help="Whether to enable backwards-compatibility transitive includes when running the tests. This "
427        "is provided to ensure that the trimmed-down version of libc++ does not bit-rot in between "
428        "points at which we bulk-remove transitive includes.",
429        actions=lambda enabled: [] if enabled else [
430            AddFeature("transitive-includes-disabled"),
431            AddCompileFlag("-D_LIBCPP_REMOVE_TRANSITIVE_INCLUDES"),
432        ],
433    ),
434    Parameter(
435        name="executor",
436        type=str,
437        default=f"{shlex.quote(sys.executable)} {shlex.quote(str(Path(__file__).resolve().parent.parent.parent / 'run.py'))}",
438        help="Custom executor to use instead of the configured default.",
439        actions=lambda executor: [AddSubstitution("%{executor}", executor)],
440    ),
441    Parameter(
442        name='clang-tidy-executable',
443        type=str,
444        default=lambda cfg: getSuitableClangTidy(cfg),
445        help="Selects the clang-tidy executable to use.",
446        actions=lambda exe: [] if exe is None else [
447            AddFeature('has-clang-tidy'),
448            AddSubstitution('%{clang-tidy}', exe),
449        ]
450    ),
451]
452# fmt: on
453