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 9from libcxx.test.dsl import * 10import re 11import shutil 12import sys 13import subprocess 14 15_isClang = lambda cfg: '__clang__' in compilerMacros(cfg) and '__apple_build_version__' not in compilerMacros(cfg) 16_isAppleClang = lambda cfg: '__apple_build_version__' in compilerMacros(cfg) 17_isGCC = lambda cfg: '__GNUC__' in compilerMacros(cfg) and '__clang__' not in compilerMacros(cfg) 18_isMSVC = lambda cfg: '_MSC_VER' in compilerMacros(cfg) 19_msvcVersion = lambda cfg: (int(compilerMacros(cfg)['_MSC_VER']) // 100, int(compilerMacros(cfg)['_MSC_VER']) % 100) 20 21def _getSuitableClangTidy(cfg): 22 try: 23 # If we didn't build the libcxx-tidy plugin via CMake, we can't run the clang-tidy tests. 24 if runScriptExitCode(cfg, ['stat %{test-tools}/clang_tidy_checks/libcxx-tidy.plugin']) != 0: 25 return None 26 27 # TODO This should be the last stable release. 28 if runScriptExitCode(cfg, ['clang-tidy-16 --version']) == 0: 29 return 'clang-tidy-16' 30 31 if int(re.search('[0-9]+', commandOutput(cfg, ['clang-tidy --version'])).group()) >= 16: 32 return 'clang-tidy' 33 34 except ConfigurationRuntimeError: 35 return None 36 37DEFAULT_FEATURES = [ 38 Feature(name='fcoroutines-ts', 39 when=lambda cfg: hasCompileFlag(cfg, '-fcoroutines-ts') and 40 featureTestMacros(cfg, flags='-fcoroutines-ts').get('__cpp_coroutines', 0) >= 201703, 41 actions=[AddCompileFlag('-fcoroutines-ts')]), 42 43 Feature(name='thread-safety', 44 when=lambda cfg: hasCompileFlag(cfg, '-Werror=thread-safety'), 45 actions=[AddCompileFlag('-Werror=thread-safety')]), 46 47 Feature(name='diagnose-if-support', 48 when=lambda cfg: hasCompileFlag(cfg, '-Wuser-defined-warnings'), 49 actions=[AddCompileFlag('-Wuser-defined-warnings')]), 50 51 # Tests to validate whether the compiler has a way to set the maximum number 52 # of steps during constant evaluation. Since the flag differs per compiler 53 # store the "valid" flag as a feature. This allows passing the proper compile 54 # flag to the compiler: 55 # // ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=12345678 56 # // ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=12345678 57 Feature(name='has-fconstexpr-steps', 58 when=lambda cfg: hasCompileFlag(cfg, '-fconstexpr-steps=1')), 59 60 Feature(name='has-fconstexpr-ops-limit', 61 when=lambda cfg: hasCompileFlag(cfg, '-fconstexpr-ops-limit=1')), 62 63 Feature(name='has-fblocks', when=lambda cfg: hasCompileFlag(cfg, '-fblocks')), 64 Feature(name='-fsized-deallocation', when=lambda cfg: hasCompileFlag(cfg, '-fsized-deallocation')), 65 Feature(name='-faligned-allocation', when=lambda cfg: hasCompileFlag(cfg, '-faligned-allocation')), 66 Feature(name='fdelayed-template-parsing', when=lambda cfg: hasCompileFlag(cfg, '-fdelayed-template-parsing')), 67 Feature(name='libcpp-no-coroutines', when=lambda cfg: featureTestMacros(cfg).get('__cpp_impl_coroutine', 0) < 201902), 68 Feature(name='has-fobjc-arc', when=lambda cfg: hasCompileFlag(cfg, '-xobjective-c++ -fobjc-arc') and 69 sys.platform.lower().strip() == 'darwin'), # TODO: this doesn't handle cross-compiling to Apple platforms. 70 Feature(name='objective-c++', when=lambda cfg: hasCompileFlag(cfg, '-xobjective-c++ -fobjc-arc')), 71 Feature(name='verify-support', when=lambda cfg: hasCompileFlag(cfg, '-Xclang -verify-ignore-unexpected')), 72 73 Feature(name='non-lockfree-atomics', 74 when=lambda cfg: sourceBuilds(cfg, """ 75 #include <atomic> 76 struct Large { int storage[100]; }; 77 std::atomic<Large> x; 78 int main(int, char**) { (void)x.load(); return 0; } 79 """)), 80 # TODO: Remove this feature once compiler-rt includes __atomic_is_lockfree() 81 # on all supported platforms. 82 Feature(name='is-lockfree-runtime-function', 83 when=lambda cfg: sourceBuilds(cfg, """ 84 #include <atomic> 85 struct Large { int storage[100]; }; 86 std::atomic<Large> x; 87 int main(int, char**) { return x.is_lock_free(); } 88 """)), 89 90 # Some tests rely on creating shared libraries which link in the C++ Standard Library. In some 91 # cases, this doesn't work (e.g. if the library was built as a static archive and wasn't compiled 92 # as position independent). This feature informs the test suite of whether it's possible to create 93 # a shared library in a shell test by using the '-shared' compiler flag. 94 # 95 # Note: To implement this check properly, we need to make sure that we use something inside the 96 # compiled library, not only in the headers. It should be safe to assume that all implementations 97 # define `operator new` in the compiled library. 98 Feature(name='cant-build-shared-library', 99 when=lambda cfg: not sourceBuilds(cfg, """ 100 void f() { new int(3); } 101 """, ['-shared'])), 102 103 # Check for a Windows UCRT bug (fixed in UCRT/Windows 10.0.20348.0): 104 # https://developercommunity.visualstudio.com/t/utf-8-locales-break-ctype-functions-for-wchar-type/1653678 105 Feature(name='win32-broken-utf8-wchar-ctype', 106 when=lambda cfg: not '_LIBCPP_HAS_NO_LOCALIZATION' in compilerMacros(cfg) and '_WIN32' in compilerMacros(cfg) and not programSucceeds(cfg, """ 107 #include <locale.h> 108 #include <wctype.h> 109 int main(int, char**) { 110 setlocale(LC_ALL, "en_US.UTF-8"); 111 return towlower(L'\\xDA') != L'\\xFA'; 112 } 113 """)), 114 115 # Check for a Windows UCRT bug (fixed in UCRT/Windows 10.0.19041.0). 116 # https://developercommunity.visualstudio.com/t/printf-formatting-with-g-outputs-too/1660837 117 Feature(name='win32-broken-printf-g-precision', 118 when=lambda cfg: '_WIN32' in compilerMacros(cfg) and not programSucceeds(cfg, """ 119 #include <stdio.h> 120 #include <string.h> 121 int main(int, char**) { 122 char buf[100]; 123 snprintf(buf, sizeof(buf), "%#.*g", 0, 0.0); 124 return strcmp(buf, "0."); 125 } 126 """)), 127 128 # Check for Glibc < 2.27, where the ru_RU.UTF-8 locale had 129 # mon_decimal_point == ".", which our tests don't handle. 130 Feature(name='glibc-old-ru_RU-decimal-point', 131 when=lambda cfg: not '_LIBCPP_HAS_NO_LOCALIZATION' in compilerMacros(cfg) and not programSucceeds(cfg, """ 132 #include <locale.h> 133 #include <string.h> 134 int main(int, char**) { 135 setlocale(LC_ALL, "ru_RU.UTF-8"); 136 return strcmp(localeconv()->mon_decimal_point, ","); 137 } 138 """)), 139 140 Feature(name='has-unix-headers', 141 when=lambda cfg: sourceBuilds(cfg, """ 142 #include <unistd.h> 143 #include <sys/wait.h> 144 int main(int, char**) { 145 return 0; 146 } 147 """)), 148 149 # Whether Bash can run on the executor. 150 # This is not always the case, for example when running on embedded systems. 151 # 152 # For the corner case of bash existing, but it being missing in the path 153 # set in %{exec} as "--env PATH=one-single-dir", the executor does find 154 # and executes bash, but bash then can't find any other common shell 155 # utilities. Test executing "bash -c 'bash --version'" to see if bash 156 # manages to find binaries to execute. 157 Feature(name='executor-has-no-bash', 158 when=lambda cfg: runScriptExitCode(cfg, ['%{exec} bash -c \'bash --version\'']) != 0), 159 Feature(name='has-clang-tidy', 160 when=lambda cfg: _getSuitableClangTidy(cfg) is not None, 161 actions=[AddSubstitution('%{clang-tidy}', lambda cfg: _getSuitableClangTidy(cfg))]), 162 163 Feature(name='apple-clang', when=_isAppleClang), 164 Feature(name=lambda cfg: 'apple-clang-{__clang_major__}'.format(**compilerMacros(cfg)), when=_isAppleClang), 165 Feature(name=lambda cfg: 'apple-clang-{__clang_major__}.{__clang_minor__}'.format(**compilerMacros(cfg)), when=_isAppleClang), 166 Feature(name=lambda cfg: 'apple-clang-{__clang_major__}.{__clang_minor__}.{__clang_patchlevel__}'.format(**compilerMacros(cfg)), when=_isAppleClang), 167 168 Feature(name='clang', when=_isClang), 169 Feature(name=lambda cfg: 'clang-{__clang_major__}'.format(**compilerMacros(cfg)), when=_isClang), 170 Feature(name=lambda cfg: 'clang-{__clang_major__}.{__clang_minor__}'.format(**compilerMacros(cfg)), when=_isClang), 171 Feature(name=lambda cfg: 'clang-{__clang_major__}.{__clang_minor__}.{__clang_patchlevel__}'.format(**compilerMacros(cfg)), when=_isClang), 172 173 # Note: Due to a GCC bug (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104760), we must disable deprecation warnings 174 # on GCC or spurious diagnostics are issued. 175 # 176 # TODO: 177 # - Enable -Wplacement-new with GCC. 178 # - Enable -Wclass-memaccess with GCC. 179 Feature(name='gcc', when=_isGCC, 180 actions=[AddCompileFlag('-D_LIBCPP_DISABLE_DEPRECATION_WARNINGS'), 181 AddCompileFlag('-Wno-placement-new'), 182 AddCompileFlag('-Wno-class-memaccess')]), 183 Feature(name=lambda cfg: 'gcc-{__GNUC__}'.format(**compilerMacros(cfg)), when=_isGCC), 184 Feature(name=lambda cfg: 'gcc-{__GNUC__}.{__GNUC_MINOR__}'.format(**compilerMacros(cfg)), when=_isGCC), 185 Feature(name=lambda cfg: 'gcc-{__GNUC__}.{__GNUC_MINOR__}.{__GNUC_PATCHLEVEL__}'.format(**compilerMacros(cfg)), when=_isGCC), 186 187 Feature(name='msvc', when=_isMSVC), 188 Feature(name=lambda cfg: 'msvc-{}'.format(*_msvcVersion(cfg)), when=_isMSVC), 189 Feature(name=lambda cfg: 'msvc-{}.{}'.format(*_msvcVersion(cfg)), when=_isMSVC), 190] 191 192# Deduce and add the test features that that are implied by the #defines in 193# the <__config_site> header. 194# 195# For each macro of the form `_LIBCPP_XXX_YYY_ZZZ` defined below that 196# is defined after including <__config_site>, add a Lit feature called 197# `libcpp-xxx-yyy-zzz`. When a macro is defined to a specific value 198# (e.g. `_LIBCPP_ABI_VERSION=2`), the feature is `libcpp-xxx-yyy-zzz=<value>`. 199# 200# Note that features that are more strongly tied to libc++ are named libcpp-foo, 201# while features that are more general in nature are not prefixed with 'libcpp-'. 202macros = { 203 '_LIBCPP_HAS_NO_MONOTONIC_CLOCK': 'no-monotonic-clock', 204 '_LIBCPP_HAS_NO_THREADS': 'no-threads', 205 '_LIBCPP_HAS_THREAD_API_EXTERNAL': 'libcpp-has-thread-api-external', 206 '_LIBCPP_HAS_THREAD_API_PTHREAD': 'libcpp-has-thread-api-pthread', 207 '_LIBCPP_NO_VCRUNTIME': 'libcpp-no-vcruntime', 208 '_LIBCPP_ABI_VERSION': 'libcpp-abi-version', 209 '_LIBCPP_HAS_NO_FILESYSTEM_LIBRARY': 'no-filesystem', 210 '_LIBCPP_HAS_NO_RANDOM_DEVICE': 'no-random-device', 211 '_LIBCPP_HAS_NO_LOCALIZATION': 'no-localization', 212 '_LIBCPP_HAS_NO_FSTREAM': 'no-fstream', 213 '_LIBCPP_HAS_NO_WIDE_CHARACTERS': 'no-wide-characters', 214 '_LIBCPP_HAS_NO_UNICODE': 'libcpp-has-no-unicode', 215 '_LIBCPP_ENABLE_DEBUG_MODE': 'libcpp-has-debug-mode', 216} 217for macro, feature in macros.items(): 218 DEFAULT_FEATURES.append( 219 Feature(name=lambda cfg, m=macro, f=feature: f + ('={}'.format(compilerMacros(cfg)[m]) if compilerMacros(cfg)[m] else ''), 220 when=lambda cfg, m=macro: m in compilerMacros(cfg)) 221 ) 222 223 224# Mapping from canonical locale names (used in the tests) to possible locale 225# names on various systems. Each locale is considered supported if any of the 226# alternative names is supported. 227locales = { 228 'en_US.UTF-8': ['en_US.UTF-8', 'en_US.utf8', 'English_United States.1252'], 229 'fr_FR.UTF-8': ['fr_FR.UTF-8', 'fr_FR.utf8', 'French_France.1252'], 230 'ja_JP.UTF-8': ['ja_JP.UTF-8', 'ja_JP.utf8', 'Japanese_Japan.923'], 231 'ru_RU.UTF-8': ['ru_RU.UTF-8', 'ru_RU.utf8', 'Russian_Russia.1251'], 232 'zh_CN.UTF-8': ['zh_CN.UTF-8', 'zh_CN.utf8', 'Chinese_China.936'], 233 'fr_CA.ISO8859-1': ['fr_CA.ISO8859-1', 'French_Canada.1252'], 234 'cs_CZ.ISO8859-2': ['cs_CZ.ISO8859-2', 'Czech_Czech Republic.1250'] 235} 236for locale, alts in locales.items(): 237 # Note: Using alts directly in the lambda body here will bind it to the value at the 238 # end of the loop. Assigning it to a default argument works around this issue. 239 DEFAULT_FEATURES.append(Feature(name='locale.{}'.format(locale), 240 when=lambda cfg, alts=alts: hasAnyLocale(cfg, alts))) 241 242 243# Add features representing the target platform name: darwin, linux, windows, etc... 244DEFAULT_FEATURES += [ 245 Feature(name='darwin', when=lambda cfg: '__APPLE__' in compilerMacros(cfg)), 246 Feature(name='windows', when=lambda cfg: '_WIN32' in compilerMacros(cfg)), 247 Feature(name='windows-dll', when=lambda cfg: '_WIN32' in compilerMacros(cfg) and programSucceeds(cfg, """ 248 #include <iostream> 249 #include <windows.h> 250 #include <winnt.h> 251 int main(int, char**) { 252 // Get a pointer to a data member that gets linked from the C++ 253 // library. This must be a data member (functions can get 254 // thunk inside the calling executable), and must not be 255 // something that is defined inline in headers. 256 void *ptr = &std::cout; 257 // Get a handle to the current main executable. 258 void *exe = GetModuleHandle(NULL); 259 // The handle points at the PE image header. Navigate through 260 // the header structure to find the size of the PE image (the 261 // executable). 262 PIMAGE_DOS_HEADER dosheader = (PIMAGE_DOS_HEADER)exe; 263 PIMAGE_NT_HEADERS ntheader = (PIMAGE_NT_HEADERS)((BYTE *)dosheader + dosheader->e_lfanew); 264 PIMAGE_OPTIONAL_HEADER peheader = &ntheader->OptionalHeader; 265 void *exeend = (BYTE*)exe + peheader->SizeOfImage; 266 // Check if the tested pointer - the data symbol from the 267 // C++ library - is located within the exe. 268 if (ptr >= exe && ptr <= exeend) 269 return 1; 270 // Return success if it was outside of the executable, i.e. 271 // loaded from a DLL. 272 return 0; 273 } 274 """), actions=[AddCompileFlag('-DTEST_WINDOWS_DLL')]), 275 Feature(name='linux', when=lambda cfg: '__linux__' in compilerMacros(cfg)), 276 Feature(name='netbsd', when=lambda cfg: '__NetBSD__' in compilerMacros(cfg)), 277 Feature(name='freebsd', when=lambda cfg: '__FreeBSD__' in compilerMacros(cfg)), 278 Feature(name='LIBCXX-FREEBSD-FIXME', when=lambda cfg: '__FreeBSD__' in compilerMacros(cfg)), 279] 280 281# Add features representing the build host platform name. 282# The build host could differ from the target platform for cross-compilation. 283DEFAULT_FEATURES += [ 284 Feature(name='buildhost={}'.format(sys.platform.lower().strip())), 285 # sys.platform can often be represented by a "sub-system", such as 'win32', 'cygwin', 'mingw', freebsd13 & etc. 286 # We define a consolidated feature on a few platforms. 287 Feature(name='buildhost=windows', when=lambda cfg: platform.system().lower().startswith('windows')), 288 Feature(name='buildhost=freebsd', when=lambda cfg: platform.system().lower().startswith('freebsd')), 289 Feature(name='buildhost=aix', when=lambda cfg: platform.system().lower().startswith('aix')) 290] 291 292# Detect whether GDB is on the system, has Python scripting and supports 293# adding breakpoint commands. If so add a substitution to access it. 294def check_gdb(cfg): 295 gdb_path = shutil.which('gdb') 296 if gdb_path is None: 297 return False 298 299 # Check that we can set breakpoint commands, which was added in 8.3. 300 # Using the quit command here means that gdb itself exits, not just 301 # the "python <...>" command. 302 test_src = """\ 303try: 304 gdb.Breakpoint(\"main\").commands=\"foo\" 305except AttributeError: 306 gdb.execute(\"quit 1\") 307gdb.execute(\"quit\")""" 308 309 try: 310 stdout = subprocess.check_output( 311 [gdb_path, "-ex", "python " + test_src, "--batch"], 312 stderr=subprocess.DEVNULL, universal_newlines=True) 313 except subprocess.CalledProcessError: 314 # We can't set breakpoint commands 315 return False 316 317 # Check we actually ran the Python 318 return not "Python scripting is not supported" in stdout 319 320DEFAULT_FEATURES += [ 321 Feature(name='host-has-gdb-with-python', 322 when=check_gdb, 323 actions=[AddSubstitution('%{gdb}', lambda cfg: shutil.which('gdb'))] 324 ) 325] 326