xref: /llvm-project/compiler-rt/test/lit.common.cfg.py (revision d084bc291a21895fa2ecc74e2d1c9d1818ba4fd7)
1# -*- Python -*-
2
3# Configuration file for 'lit' test runner.
4# This file contains common rules for various compiler-rt testsuites.
5# It is mostly copied from lit.cfg.py used by Clang.
6import os
7import platform
8import re
9import shlex
10import subprocess
11import json
12
13import lit.formats
14import lit.util
15
16
17def get_path_from_clang(args, allow_failure):
18    clang_cmd = [
19        config.clang.strip(),
20        f"--target={config.target_triple}",
21        *args,
22    ]
23    path = None
24    try:
25        result = subprocess.run(
26            clang_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True
27        )
28        path = result.stdout.decode().strip()
29    except subprocess.CalledProcessError as e:
30        msg = f"Failed to run {clang_cmd}\nrc:{e.returncode}\nstdout:{e.stdout}\ne.stderr{e.stderr}"
31        if allow_failure:
32            lit_config.warning(msg)
33        else:
34            lit_config.fatal(msg)
35    return path, clang_cmd
36
37
38def find_compiler_libdir():
39    """
40    Returns the path to library resource directory used
41    by the compiler.
42    """
43    if config.compiler_id != "Clang":
44        lit_config.warning(
45            f"Determining compiler's runtime directory is not supported for {config.compiler_id}"
46        )
47        # TODO: Support other compilers.
48        return None
49
50    # Try using `-print-runtime-dir`. This is only supported by very new versions of Clang.
51    # so allow failure here.
52    runtime_dir, clang_cmd = get_path_from_clang(
53        shlex.split(config.target_cflags) + ["-print-runtime-dir"], allow_failure=True
54    )
55    if runtime_dir:
56        if os.path.exists(runtime_dir):
57            return os.path.realpath(runtime_dir)
58        # TODO(dliew): This should be a fatal error but it seems to trip the `llvm-clang-win-x-aarch64`
59        # bot which is likely misconfigured
60        lit_config.warning(
61            f'Path reported by clang does not exist: "{runtime_dir}". '
62            f"This path was found by running {clang_cmd}."
63        )
64        return None
65
66    # Fall back for older AppleClang that doesn't support `-print-runtime-dir`
67    # Note `-print-file-name=<path to compiler-rt lib>` was broken for Apple
68    # platforms so we can't use that approach here (see https://reviews.llvm.org/D101682).
69    if config.host_os == "Darwin":
70        lib_dir, _ = get_path_from_clang(["-print-file-name=lib"], allow_failure=False)
71        runtime_dir = os.path.join(lib_dir, "darwin")
72        if not os.path.exists(runtime_dir):
73            lit_config.fatal(f"Path reported by clang does not exist: {runtime_dir}")
74        return os.path.realpath(runtime_dir)
75
76    lit_config.warning("Failed to determine compiler's runtime directory")
77    return None
78
79
80def push_dynamic_library_lookup_path(config, new_path):
81    if platform.system() == "Windows":
82        dynamic_library_lookup_var = "PATH"
83    elif platform.system() == "Darwin":
84        dynamic_library_lookup_var = "DYLD_LIBRARY_PATH"
85    elif platform.system() == "Haiku":
86        dynamic_library_lookup_var = "LIBRARY_PATH"
87    else:
88        dynamic_library_lookup_var = "LD_LIBRARY_PATH"
89
90    new_ld_library_path = os.path.pathsep.join(
91        (new_path, config.environment.get(dynamic_library_lookup_var, ""))
92    )
93    config.environment[dynamic_library_lookup_var] = new_ld_library_path
94
95    if platform.system() == "FreeBSD":
96        dynamic_library_lookup_var = "LD_32_LIBRARY_PATH"
97        new_ld_32_library_path = os.path.pathsep.join(
98            (new_path, config.environment.get(dynamic_library_lookup_var, ""))
99        )
100        config.environment[dynamic_library_lookup_var] = new_ld_32_library_path
101
102    if platform.system() == "SunOS":
103        dynamic_library_lookup_var = "LD_LIBRARY_PATH_32"
104        new_ld_library_path_32 = os.path.pathsep.join(
105            (new_path, config.environment.get(dynamic_library_lookup_var, ""))
106        )
107        config.environment[dynamic_library_lookup_var] = new_ld_library_path_32
108
109        dynamic_library_lookup_var = "LD_LIBRARY_PATH_64"
110        new_ld_library_path_64 = os.path.pathsep.join(
111            (new_path, config.environment.get(dynamic_library_lookup_var, ""))
112        )
113        config.environment[dynamic_library_lookup_var] = new_ld_library_path_64
114
115
116# Choose between lit's internal shell pipeline runner and a real shell.  If
117# LIT_USE_INTERNAL_SHELL is in the environment, we use that as an override.
118use_lit_shell = os.environ.get("LIT_USE_INTERNAL_SHELL")
119if use_lit_shell:
120    # 0 is external, "" is default, and everything else is internal.
121    execute_external = use_lit_shell == "0"
122else:
123    # Otherwise we default to internal on Windows and external elsewhere, as
124    # bash on Windows is usually very slow.
125    execute_external = not sys.platform in ["win32"]
126
127# Allow expanding substitutions that are based on other substitutions
128config.recursiveExpansionLimit = 10
129
130# Setup test format.
131config.test_format = lit.formats.ShTest(execute_external)
132if execute_external:
133    config.available_features.add("shell")
134
135target_is_msvc = bool(re.match(r".*-windows-msvc$", config.target_triple))
136target_is_windows = bool(re.match(r".*-windows.*$", config.target_triple))
137
138compiler_id = getattr(config, "compiler_id", None)
139if compiler_id == "Clang":
140    if not (platform.system() == "Windows" and target_is_msvc):
141        config.cxx_mode_flags = ["--driver-mode=g++"]
142    else:
143        config.cxx_mode_flags = []
144    # We assume that sanitizers should provide good enough error
145    # reports and stack traces even with minimal debug info.
146    config.debug_info_flags = ["-gline-tables-only"]
147    if platform.system() == "Windows" and target_is_msvc:
148        # On MSVC, use CodeView with column info instead of DWARF. Both VS and
149        # windbg do not behave well when column info is enabled, but users have
150        # requested it because it makes ASan reports more precise.
151        config.debug_info_flags.append("-gcodeview")
152        config.debug_info_flags.append("-gcolumn-info")
153elif compiler_id == "MSVC":
154    config.debug_info_flags = ["/Z7"]
155    config.cxx_mode_flags = []
156elif compiler_id == "GNU":
157    config.cxx_mode_flags = ["-x c++"]
158    config.debug_info_flags = ["-g"]
159else:
160    lit_config.fatal("Unsupported compiler id: %r" % compiler_id)
161# Add compiler ID to the list of available features.
162config.available_features.add(compiler_id)
163
164# When LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=on, the initial value of
165# config.compiler_rt_libdir (COMPILER_RT_RESOLVED_LIBRARY_OUTPUT_DIR) has the
166# host triple as the trailing path component. The value is incorrect for 32-bit
167# tests on 64-bit hosts and vice versa. Adjust config.compiler_rt_libdir
168# accordingly.
169if config.enable_per_target_runtime_dir:
170    if config.target_arch == "i386":
171        config.compiler_rt_libdir = re.sub(
172            r"/x86_64(?=-[^/]+$)", "/i386", config.compiler_rt_libdir
173        )
174    elif config.target_arch == "x86_64":
175        config.compiler_rt_libdir = re.sub(
176            r"/i386(?=-[^/]+$)", "/x86_64", config.compiler_rt_libdir
177        )
178    if config.target_arch == "sparc":
179        config.compiler_rt_libdir = re.sub(
180            r"/sparcv9(?=-[^/]+$)", "/sparc", config.compiler_rt_libdir
181        )
182    elif config.target_arch == "sparcv9":
183        config.compiler_rt_libdir = re.sub(
184            r"/sparc(?=-[^/]+$)", "/sparcv9", config.compiler_rt_libdir
185        )
186
187# Check if the test compiler resource dir matches the local build directory
188# (which happens with -DLLVM_ENABLE_PROJECTS=clang;compiler-rt) or if we are
189# using an installed clang to test compiler-rt standalone. In the latter case
190# we may need to override the resource dir to match the path of the just-built
191# compiler-rt libraries.
192test_cc_resource_dir, _ = get_path_from_clang(
193    shlex.split(config.target_cflags) + ["-print-resource-dir"], allow_failure=True
194)
195# Normalize the path for comparison
196if test_cc_resource_dir is not None:
197    test_cc_resource_dir = os.path.realpath(test_cc_resource_dir)
198if lit_config.debug:
199    lit_config.note(f"Resource dir for {config.clang} is {test_cc_resource_dir}")
200local_build_resource_dir = os.path.realpath(config.compiler_rt_output_dir)
201if test_cc_resource_dir != local_build_resource_dir and config.test_standalone_build_libs:
202    if config.compiler_id == "Clang":
203        if lit_config.debug:
204            lit_config.note(
205                f"Overriding test compiler resource dir to use "
206                f'libraries in "{config.compiler_rt_libdir}"'
207            )
208        # Ensure that we use the just-built static libraries when linking by
209        # overriding the Clang resource directory. Additionally, we want to use
210        # the builtin headers shipped with clang (e.g. stdint.h), so we
211        # explicitly add this as an include path (since the headers are not
212        # going to be in the current compiler-rt build directory).
213        # We also tell the linker to add an RPATH entry for the local library
214        # directory so that the just-built shared libraries are used.
215        config.target_cflags += f" -nobuiltininc"
216        config.target_cflags += f" -I{config.compiler_rt_src_root}/include"
217        config.target_cflags += f" -idirafter {test_cc_resource_dir}/include"
218        config.target_cflags += f" -resource-dir={config.compiler_rt_output_dir}"
219        if not target_is_windows:
220            # Avoid passing -rpath on Windows where it is not supported.
221            config.target_cflags += f" -Wl,-rpath,{config.compiler_rt_libdir}"
222    else:
223        lit_config.warning(
224            f"Cannot override compiler-rt library directory with non-Clang "
225            f"compiler: {config.compiler_id}"
226        )
227
228
229# Ask the compiler for the path to libraries it is going to use. If this
230# doesn't match config.compiler_rt_libdir then it means we might be testing the
231# compiler's own runtime libraries rather than the ones we just built.
232# Warn about this and handle appropriately.
233compiler_libdir = find_compiler_libdir()
234if compiler_libdir:
235    compiler_rt_libdir_real = os.path.realpath(config.compiler_rt_libdir)
236    if compiler_libdir != compiler_rt_libdir_real:
237        lit_config.warning(
238            "Compiler lib dir != compiler-rt lib dir\n"
239            f'Compiler libdir:     "{compiler_libdir}"\n'
240            f'compiler-rt libdir:  "{compiler_rt_libdir_real}"'
241        )
242        if config.test_standalone_build_libs:
243            # Use just built runtime libraries, i.e. the libraries this build just built.
244            if not config.test_suite_supports_overriding_runtime_lib_path:
245                # Test suite doesn't support this configuration.
246                # TODO(dliew): This should be an error but it seems several bots are
247                # testing incorrectly and having this as an error breaks them.
248                lit_config.warning(
249                    "COMPILER_RT_TEST_STANDALONE_BUILD_LIBS=ON, but this test suite "
250                    "does not support testing the just-built runtime libraries "
251                    "when the test compiler is configured to use different runtime "
252                    "libraries. Either modify this test suite to support this test "
253                    "configuration, or set COMPILER_RT_TEST_STANDALONE_BUILD_LIBS=OFF "
254                    "to test the runtime libraries included in the compiler instead."
255                )
256        else:
257            # Use Compiler's resource library directory instead.
258            config.compiler_rt_libdir = compiler_libdir
259        lit_config.note(f'Testing using libraries in "{config.compiler_rt_libdir}"')
260
261# If needed, add cflag for shadow scale.
262if config.asan_shadow_scale != "":
263    config.target_cflags += " -mllvm -asan-mapping-scale=" + config.asan_shadow_scale
264if config.memprof_shadow_scale != "":
265    config.target_cflags += (
266        " -mllvm -memprof-mapping-scale=" + config.memprof_shadow_scale
267    )
268
269# Clear some environment variables that might affect Clang.
270possibly_dangerous_env_vars = [
271    "ASAN_OPTIONS",
272    "DFSAN_OPTIONS",
273    "HWASAN_OPTIONS",
274    "LSAN_OPTIONS",
275    "MSAN_OPTIONS",
276    "UBSAN_OPTIONS",
277    "COMPILER_PATH",
278    "RC_DEBUG_OPTIONS",
279    "CINDEXTEST_PREAMBLE_FILE",
280    "CPATH",
281    "C_INCLUDE_PATH",
282    "CPLUS_INCLUDE_PATH",
283    "OBJC_INCLUDE_PATH",
284    "OBJCPLUS_INCLUDE_PATH",
285    "LIBCLANG_TIMING",
286    "LIBCLANG_OBJTRACKING",
287    "LIBCLANG_LOGGING",
288    "LIBCLANG_BGPRIO_INDEX",
289    "LIBCLANG_BGPRIO_EDIT",
290    "LIBCLANG_NOTHREADS",
291    "LIBCLANG_RESOURCE_USAGE",
292    "LIBCLANG_CODE_COMPLETION_LOGGING",
293    "XRAY_OPTIONS",
294]
295# Clang/MSVC may refer to %INCLUDE%. vsvarsall.bat sets it.
296if not (platform.system() == "Windows" and target_is_msvc):
297    possibly_dangerous_env_vars.append("INCLUDE")
298for name in possibly_dangerous_env_vars:
299    if name in config.environment:
300        del config.environment[name]
301
302# Tweak PATH to include llvm tools dir.
303if (not config.llvm_tools_dir) or (not os.path.exists(config.llvm_tools_dir)):
304    lit_config.fatal(
305        "Invalid llvm_tools_dir config attribute: %r" % config.llvm_tools_dir
306    )
307path = os.path.pathsep.join((config.llvm_tools_dir, config.environment["PATH"]))
308config.environment["PATH"] = path
309
310# Help MSVS link.exe find the standard libraries.
311# Make sure we only try to use it when targetting Windows.
312if platform.system() == "Windows" and target_is_msvc:
313    config.environment["LIB"] = os.environ["LIB"]
314
315config.available_features.add(config.host_os.lower())
316
317if config.target_triple.startswith("ppc") or config.target_triple.startswith("powerpc"):
318    config.available_features.add("ppc")
319
320if re.match(r"^x86_64.*-linux", config.target_triple):
321    config.available_features.add("x86_64-linux")
322
323config.available_features.add("host-byteorder-" + sys.byteorder + "-endian")
324
325if config.have_zlib:
326    config.available_features.add("zlib")
327    config.substitutions.append(("%zlib_include_dir", config.zlib_include_dir))
328    config.substitutions.append(("%zlib_library", config.zlib_library))
329
330if config.have_internal_symbolizer:
331    config.available_features.add("internal_symbolizer")
332
333# Use ugly construction to explicitly prohibit "clang", "clang++" etc.
334# in RUN lines.
335config.substitutions.append(
336    (
337        " clang",
338        """\n\n*** Do not use 'clangXXX' in tests,
339     instead define '%clangXXX' substitution in lit config. ***\n\n""",
340    )
341)
342
343if config.host_os == "NetBSD":
344    nb_commands_dir = os.path.join(
345        config.compiler_rt_src_root, "test", "sanitizer_common", "netbsd_commands"
346    )
347    config.netbsd_noaslr_prefix = "sh " + os.path.join(nb_commands_dir, "run_noaslr.sh")
348    config.netbsd_nomprotect_prefix = "sh " + os.path.join(
349        nb_commands_dir, "run_nomprotect.sh"
350    )
351    config.substitutions.append(("%run_nomprotect", config.netbsd_nomprotect_prefix))
352else:
353    config.substitutions.append(("%run_nomprotect", "%run"))
354
355# Copied from libcxx's config.py
356def get_lit_conf(name, default=None):
357    # Allow overriding on the command line using --param=<name>=<val>
358    val = lit_config.params.get(name, None)
359    if val is None:
360        val = getattr(config, name, None)
361        if val is None:
362            val = default
363    return val
364
365
366emulator = get_lit_conf("emulator", None)
367
368
369def get_ios_commands_dir():
370    return os.path.join(
371        config.compiler_rt_src_root, "test", "sanitizer_common", "ios_commands"
372    )
373
374
375# Allow tests to be executed on a simulator or remotely.
376if emulator:
377    config.substitutions.append(("%run", emulator))
378    config.substitutions.append(("%env ", "env "))
379    # TODO: Implement `%device_rm` to perform removal of files in the emulator.
380    # For now just make it a no-op.
381    lit_config.warning("%device_rm is not implemented")
382    config.substitutions.append(("%device_rm", "echo "))
383    config.compile_wrapper = ""
384elif config.host_os == "Darwin" and config.apple_platform != "osx":
385    # Darwin tests can be targetting macOS, a device or a simulator. All devices
386    # are declared as "ios", even for iOS derivatives (tvOS, watchOS). Similarly,
387    # all simulators are "iossim". See the table below.
388    #
389    # =========================================================================
390    # Target             | Feature set
391    # =========================================================================
392    # macOS              | darwin
393    # iOS device         | darwin, ios
394    # iOS simulator      | darwin, ios, iossim
395    # tvOS device        | darwin, ios, tvos
396    # tvOS simulator     | darwin, ios, iossim, tvos, tvossim
397    # watchOS device     | darwin, ios, watchos
398    # watchOS simulator  | darwin, ios, iossim, watchos, watchossim
399    # =========================================================================
400
401    ios_or_iossim = "iossim" if config.apple_platform.endswith("sim") else "ios"
402
403    config.available_features.add("ios")
404    device_id_env = "SANITIZER_" + ios_or_iossim.upper() + "_TEST_DEVICE_IDENTIFIER"
405    if ios_or_iossim == "iossim":
406        config.available_features.add("iossim")
407        if device_id_env not in os.environ:
408            lit_config.fatal(
409                "{} must be set in the environment when running iossim tests".format(
410                    device_id_env
411                )
412            )
413    if config.apple_platform != "ios" and config.apple_platform != "iossim":
414        config.available_features.add(config.apple_platform)
415
416    ios_commands_dir = get_ios_commands_dir()
417
418    run_wrapper = os.path.join(ios_commands_dir, ios_or_iossim + "_run.py")
419    env_wrapper = os.path.join(ios_commands_dir, ios_or_iossim + "_env.py")
420    compile_wrapper = os.path.join(ios_commands_dir, ios_or_iossim + "_compile.py")
421    prepare_script = os.path.join(ios_commands_dir, ios_or_iossim + "_prepare.py")
422
423    if device_id_env in os.environ:
424        config.environment[device_id_env] = os.environ[device_id_env]
425    config.substitutions.append(("%run", run_wrapper))
426    config.substitutions.append(("%env ", env_wrapper + " "))
427    # Current implementation of %device_rm uses the run_wrapper to do
428    # the work.
429    config.substitutions.append(("%device_rm", "{} rm ".format(run_wrapper)))
430    config.compile_wrapper = compile_wrapper
431
432    try:
433        prepare_output = (
434            subprocess.check_output(
435                [prepare_script, config.apple_platform, config.clang]
436            )
437            .decode()
438            .strip()
439        )
440    except subprocess.CalledProcessError as e:
441        print("Command failed:")
442        print(e.output)
443        raise e
444    if len(prepare_output) > 0:
445        print(prepare_output)
446    prepare_output_json = prepare_output.split("\n")[-1]
447    prepare_output = json.loads(prepare_output_json)
448    config.environment.update(prepare_output["env"])
449elif config.android:
450    config.available_features.add("android")
451    compile_wrapper = (
452        os.path.join(
453            config.compiler_rt_src_root,
454            "test",
455            "sanitizer_common",
456            "android_commands",
457            "android_compile.py",
458        )
459        + " "
460    )
461    config.compile_wrapper = compile_wrapper
462    config.substitutions.append(("%run", ""))
463    config.substitutions.append(("%env ", "env "))
464else:
465    config.substitutions.append(("%run", ""))
466    config.substitutions.append(("%env ", "env "))
467    # When running locally %device_rm is a no-op.
468    config.substitutions.append(("%device_rm", "echo "))
469    config.compile_wrapper = ""
470
471# Define CHECK-%os to check for OS-dependent output.
472config.substitutions.append(("CHECK-%os", ("CHECK-" + config.host_os)))
473
474# Define %arch to check for architecture-dependent output.
475config.substitutions.append(("%arch", (config.host_arch)))
476
477if config.host_os == "Windows":
478    # FIXME: This isn't quite right. Specifically, it will succeed if the program
479    # does not crash but exits with a non-zero exit code. We ought to merge
480    # KillTheDoctor and not --crash to make the latter more useful and remove the
481    # need for this substitution.
482    config.expect_crash = "not KillTheDoctor "
483else:
484    config.expect_crash = "not --crash "
485
486config.substitutions.append(("%expect_crash ", config.expect_crash))
487
488target_arch = getattr(config, "target_arch", None)
489if target_arch:
490    config.available_features.add(target_arch + "-target-arch")
491    if target_arch in ["x86_64", "i386"]:
492        config.available_features.add("x86-target-arch")
493    config.available_features.add(target_arch + "-" + config.host_os.lower())
494
495compiler_rt_debug = getattr(config, "compiler_rt_debug", False)
496if not compiler_rt_debug:
497    config.available_features.add("compiler-rt-optimized")
498
499libdispatch = getattr(config, "compiler_rt_intercept_libdispatch", False)
500if libdispatch:
501    config.available_features.add("libdispatch")
502
503sanitizer_can_use_cxxabi = getattr(config, "sanitizer_can_use_cxxabi", True)
504if sanitizer_can_use_cxxabi:
505    config.available_features.add("cxxabi")
506
507if not getattr(config, "sanitizer_uses_static_cxxabi", False):
508    config.available_features.add("shared_cxxabi")
509
510if not getattr(config, "sanitizer_uses_static_unwind", False):
511    config.available_features.add("shared_unwind")
512
513if config.has_lld:
514    config.available_features.add("lld-available")
515
516if config.aarch64_sme:
517    config.available_features.add("aarch64-sme-available")
518
519if config.use_lld:
520    config.available_features.add("lld")
521
522if config.can_symbolize:
523    config.available_features.add("can-symbolize")
524
525if config.gwp_asan:
526    config.available_features.add("gwp_asan")
527
528lit.util.usePlatformSdkOnDarwin(config, lit_config)
529
530min_macos_deployment_target_substitutions = [
531    (10, 11),
532    (10, 12),
533]
534# TLS requires watchOS 3+
535config.substitutions.append(
536    ("%darwin_min_target_with_tls_support", "%min_macos_deployment_target=10.12")
537)
538
539if config.host_os == "Darwin":
540    osx_version = (10, 0, 0)
541    try:
542        osx_version = subprocess.check_output(
543            ["sw_vers", "-productVersion"], universal_newlines=True
544        )
545        osx_version = tuple(int(x) for x in osx_version.split("."))
546        if len(osx_version) == 2:
547            osx_version = (osx_version[0], osx_version[1], 0)
548        if osx_version >= (10, 11):
549            config.available_features.add("osx-autointerception")
550            config.available_features.add("osx-ld64-live_support")
551        if osx_version >= (13, 1):
552            config.available_features.add("jit-compatible-osx-swift-runtime")
553    except subprocess.CalledProcessError:
554        pass
555
556    config.darwin_osx_version = osx_version
557
558    # Detect x86_64h
559    try:
560        output = subprocess.check_output(["sysctl", "hw.cpusubtype"])
561        output_re = re.match("^hw.cpusubtype: ([0-9]+)$", output)
562        if output_re:
563            cpu_subtype = int(output_re.group(1))
564            if cpu_subtype == 8:  # x86_64h
565                config.available_features.add("x86_64h")
566    except:
567        pass
568
569    # 32-bit iOS simulator is deprecated and removed in latest Xcode.
570    if config.apple_platform == "iossim":
571        if config.target_arch == "i386":
572            config.unsupported = True
573
574    def get_macos_aligned_version(macos_vers):
575        platform = config.apple_platform
576        if platform == "osx":
577            return macos_vers
578
579        macos_major, macos_minor = macos_vers
580        assert macos_major >= 10
581
582        if macos_major == 10:  # macOS 10.x
583            major = macos_minor
584            minor = 0
585        else:  # macOS 11+
586            major = macos_major + 5
587            minor = macos_minor
588
589        assert major >= 11
590
591        if platform.startswith("ios") or platform.startswith("tvos"):
592            major -= 2
593        elif platform.startswith("watch"):
594            major -= 9
595        else:
596            lit_config.fatal("Unsupported apple platform '{}'".format(platform))
597
598        return (major, minor)
599
600    for vers in min_macos_deployment_target_substitutions:
601        flag = config.apple_platform_min_deployment_target_flag
602        major, minor = get_macos_aligned_version(vers)
603        apple_device = ""
604        sim = ""
605        if "target" in flag:
606            apple_device = config.apple_platform.split("sim")[0]
607            sim = "-simulator" if "sim" in config.apple_platform else ""
608
609        config.substitutions.append(
610            (
611                "%%min_macos_deployment_target=%s.%s" % vers,
612                "{}={}{}.{}{}".format(flag, apple_device, major, minor, sim),
613            )
614        )
615else:
616    for vers in min_macos_deployment_target_substitutions:
617        config.substitutions.append(("%%min_macos_deployment_target=%s.%s" % vers, ""))
618
619if config.android:
620    env = os.environ.copy()
621    if config.android_serial:
622        env["ANDROID_SERIAL"] = config.android_serial
623        config.environment["ANDROID_SERIAL"] = config.android_serial
624
625    adb = os.environ.get("ADB", "adb")
626
627    # These are needed for tests to upload/download temp files, such as
628    # suppression-files, to device.
629    config.substitutions.append(("%device_rundir/", "/data/local/tmp/Output/"))
630    config.substitutions.append(
631        ("%push_to_device", "%s -s '%s' push " % (adb, env["ANDROID_SERIAL"]))
632    )
633    config.substitutions.append(
634        ("%adb_shell ", "%s -s '%s' shell " % (adb, env["ANDROID_SERIAL"]))
635    )
636    config.substitutions.append(
637        ("%device_rm", "%s -s '%s' shell 'rm ' " % (adb, env["ANDROID_SERIAL"]))
638    )
639
640    try:
641        android_api_level_str = subprocess.check_output(
642            [adb, "shell", "getprop", "ro.build.version.sdk"], env=env
643        ).rstrip()
644        android_api_codename = (
645            subprocess.check_output(
646                [adb, "shell", "getprop", "ro.build.version.codename"], env=env
647            )
648            .rstrip()
649            .decode("utf-8")
650        )
651    except (subprocess.CalledProcessError, OSError):
652        lit_config.fatal(
653            "Failed to read ro.build.version.sdk (using '%s' as adb)" % adb
654        )
655    try:
656        android_api_level = int(android_api_level_str)
657    except ValueError:
658        lit_config.fatal(
659            "Failed to read ro.build.version.sdk (using '%s' as adb): got '%s'"
660            % (adb, android_api_level_str)
661        )
662    android_api_level = min(android_api_level, int(config.android_api_level))
663    for required in [26, 28, 29, 30]:
664        if android_api_level >= required:
665            config.available_features.add("android-%s" % required)
666    # FIXME: Replace with appropriate version when availible.
667    if android_api_level > 30 or (
668        android_api_level == 30 and android_api_codename == "S"
669    ):
670        config.available_features.add("android-thread-properties-api")
671
672    # Prepare the device.
673    android_tmpdir = "/data/local/tmp/Output"
674    subprocess.check_call([adb, "shell", "mkdir", "-p", android_tmpdir], env=env)
675    for file in config.android_files_to_push:
676        subprocess.check_call([adb, "push", file, android_tmpdir], env=env)
677else:
678    config.substitutions.append(("%device_rundir/", ""))
679    config.substitutions.append(("%push_to_device", "echo "))
680    config.substitutions.append(("%adb_shell", "echo "))
681
682if config.host_os == "Linux":
683    def add_glibc_versions(ver_string):
684        if config.android:
685            return
686
687        from distutils.version import LooseVersion
688
689        ver = LooseVersion(ver_string)
690        any_glibc = False
691        for required in [
692            "2.19",
693            "2.27",
694            "2.30",
695            "2.33",
696            "2.34",
697            "2.37",
698            "2.38",
699            "2.40",
700        ]:
701            if ver >= LooseVersion(required):
702                config.available_features.add("glibc-" + required)
703                any_glibc = True
704            if any_glibc:
705                config.available_features.add("glibc")
706
707    # detect whether we are using glibc, and which version
708    cmd_args = [
709        config.clang.strip(),
710        f"--target={config.target_triple}",
711        "-xc",
712        "-",
713        "-o",
714        "-",
715        "-dM",
716        "-E",
717    ] + shlex.split(config.target_cflags)
718    cmd = subprocess.Popen(
719        cmd_args,
720        stdout=subprocess.PIPE,
721        stdin=subprocess.PIPE,
722        stderr=subprocess.DEVNULL,
723        env={"LANG": "C"},
724    )
725    try:
726        sout, _ = cmd.communicate(b"#include <features.h>")
727        m = dict(re.findall(r"#define (__GLIBC__|__GLIBC_MINOR__) (\d+)", str(sout)))
728        add_glibc_versions(f"{m['__GLIBC__']}.{m['__GLIBC_MINOR__']}")
729    except:
730        pass
731
732sancovcc_path = os.path.join(config.llvm_tools_dir, "sancov")
733if os.path.exists(sancovcc_path):
734    config.available_features.add("has_sancovcc")
735    config.substitutions.append(("%sancovcc ", sancovcc_path))
736
737
738def liblto_path():
739    return os.path.join(config.llvm_shlib_dir, "libLTO.dylib")
740
741
742def is_darwin_lto_supported():
743    return os.path.exists(liblto_path())
744
745
746def is_binutils_lto_supported():
747    if not os.path.exists(os.path.join(config.llvm_shlib_dir, "LLVMgold.so")):
748        return False
749
750    # We require both ld.bfd and ld.gold exist and support plugins. They are in
751    # the same repository 'binutils-gdb' and usually built together.
752    for exe in (config.gnu_ld_executable, config.gold_executable):
753        try:
754            ld_cmd = subprocess.Popen(
755                [exe, "--help"], stdout=subprocess.PIPE, env={"LANG": "C"}
756            )
757            ld_out = ld_cmd.stdout.read().decode()
758            ld_cmd.wait()
759        except OSError:
760            return False
761        if not "-plugin" in ld_out:
762            return False
763
764    return True
765
766
767def is_lld_lto_supported():
768    # LLD does support LTO, but we require it to be built with the latest
769    # changes to claim support. Otherwise older copies of LLD may not
770    # understand new bitcode versions.
771    return os.path.exists(os.path.join(config.llvm_tools_dir, "lld"))
772
773
774def is_windows_lto_supported():
775    if not target_is_msvc:
776        return True
777    return os.path.exists(os.path.join(config.llvm_tools_dir, "lld-link.exe"))
778
779
780if config.host_os == "Darwin" and is_darwin_lto_supported():
781    config.lto_supported = True
782    config.lto_flags = ["-Wl,-lto_library," + liblto_path()]
783elif config.host_os in ["Linux", "FreeBSD", "NetBSD"]:
784    config.lto_supported = False
785    if config.use_lld and is_lld_lto_supported():
786        config.lto_supported = True
787    if is_binutils_lto_supported():
788        config.available_features.add("binutils_lto")
789        config.lto_supported = True
790
791    if config.lto_supported:
792        if config.use_lld:
793            config.lto_flags = ["-fuse-ld=lld"]
794        else:
795            config.lto_flags = ["-fuse-ld=gold"]
796elif config.host_os == "Windows" and is_windows_lto_supported():
797    config.lto_supported = True
798    config.lto_flags = ["-fuse-ld=lld"]
799else:
800    config.lto_supported = False
801
802if config.lto_supported:
803    config.available_features.add("lto")
804    if config.use_thinlto:
805        config.available_features.add("thinlto")
806        config.lto_flags += ["-flto=thin"]
807    else:
808        config.lto_flags += ["-flto"]
809
810if config.have_rpc_xdr_h:
811    config.available_features.add("sunrpc")
812
813# Sanitizer tests tend to be flaky on Windows due to PR24554, so add some
814# retries. We don't do this on otther platforms because it's slower.
815if platform.system() == "Windows":
816    config.test_retry_attempts = 2
817
818# No throttling on non-Darwin platforms.
819lit_config.parallelism_groups["shadow-memory"] = None
820
821if platform.system() == "Darwin":
822    ios_device = config.apple_platform != "osx" and not config.apple_platform.endswith(
823        "sim"
824    )
825    # Force sequential execution when running tests on iOS devices.
826    if ios_device:
827        lit_config.warning("Forcing sequential execution for iOS device tests")
828        lit_config.parallelism_groups["ios-device"] = 1
829        config.parallelism_group = "ios-device"
830
831    # Only run up to 3 processes that require shadow memory simultaneously on
832    # 64-bit Darwin. Using more scales badly and hogs the system due to
833    # inefficient handling of large mmap'd regions (terabytes) by the kernel.
834    else:
835        lit_config.warning(
836            "Throttling sanitizer tests that require shadow memory on Darwin"
837        )
838        lit_config.parallelism_groups["shadow-memory"] = 3
839
840# Multiple substitutions are necessary to support multiple shared objects used
841# at once.
842# Note that substitutions with numbers have to be defined first to avoid
843# being subsumed by substitutions with smaller postfix.
844for postfix in ["2", "1", ""]:
845    if config.host_os == "Darwin":
846        config.substitutions.append(
847            (
848                "%ld_flags_rpath_exe" + postfix,
849                "-Wl,-rpath,@executable_path/ %dynamiclib" + postfix,
850            )
851        )
852        config.substitutions.append(
853            (
854                "%ld_flags_rpath_so" + postfix,
855                "-install_name @rpath/`basename %dynamiclib{}`".format(postfix),
856            )
857        )
858    elif config.host_os in ("FreeBSD", "NetBSD", "OpenBSD"):
859        config.substitutions.append(
860            (
861                "%ld_flags_rpath_exe" + postfix,
862                r"-Wl,-z,origin -Wl,-rpath,\$ORIGIN -L%T -l%xdynamiclib_namespec"
863                + postfix,
864            )
865        )
866        config.substitutions.append(("%ld_flags_rpath_so" + postfix, ""))
867    elif config.host_os == "Linux":
868        config.substitutions.append(
869            (
870                "%ld_flags_rpath_exe" + postfix,
871                r"-Wl,-rpath,\$ORIGIN -L%T -l%xdynamiclib_namespec" + postfix,
872            )
873        )
874        config.substitutions.append(("%ld_flags_rpath_so" + postfix, ""))
875    elif config.host_os == "SunOS":
876        config.substitutions.append(
877            (
878                "%ld_flags_rpath_exe" + postfix,
879                r"-Wl,-R\$ORIGIN -L%T -l%xdynamiclib_namespec" + postfix,
880            )
881        )
882        config.substitutions.append(("%ld_flags_rpath_so" + postfix, ""))
883
884    # Must be defined after the substitutions that use %dynamiclib.
885    config.substitutions.append(
886        ("%dynamiclib" + postfix, "%T/%xdynamiclib_filename" + postfix)
887    )
888    config.substitutions.append(
889        (
890            "%xdynamiclib_filename" + postfix,
891            "lib%xdynamiclib_namespec{}.so".format(postfix),
892        )
893    )
894    config.substitutions.append(("%xdynamiclib_namespec", "%basename_t.dynamic"))
895
896config.default_sanitizer_opts = []
897if config.host_os == "Darwin":
898    # On Darwin, we default to `abort_on_error=1`, which would make tests run
899    # much slower. Let's override this and run lit tests with 'abort_on_error=0'.
900    config.default_sanitizer_opts += ["abort_on_error=0"]
901    config.default_sanitizer_opts += ["log_to_syslog=0"]
902    if lit.util.which("log"):
903        # Querying the log can only done by a privileged user so
904        # so check if we can query the log.
905        exit_code = -1
906        with open("/dev/null", "r") as f:
907            # Run a `log show` command the should finish fairly quickly and produce very little output.
908            exit_code = subprocess.call(
909                ["log", "show", "--last", "1m", "--predicate", "1 == 0"],
910                stdout=f,
911                stderr=f,
912            )
913        if exit_code == 0:
914            config.available_features.add("darwin_log_cmd")
915        else:
916            lit_config.warning("log command found but cannot queried")
917    else:
918        lit_config.warning("log command not found. Some tests will be skipped.")
919elif config.android:
920    config.default_sanitizer_opts += ["abort_on_error=0"]
921
922# Allow tests to use REQUIRES=stable-runtime.  For use when you cannot use XFAIL
923# because the test hangs or fails on one configuration and not the other.
924if config.android or (config.target_arch not in ["arm", "armhf", "aarch64"]):
925    config.available_features.add("stable-runtime")
926
927if config.asan_shadow_scale:
928    config.available_features.add("shadow-scale-%s" % config.asan_shadow_scale)
929else:
930    config.available_features.add("shadow-scale-3")
931
932if config.memprof_shadow_scale:
933    config.available_features.add(
934        "memprof-shadow-scale-%s" % config.memprof_shadow_scale
935    )
936else:
937    config.available_features.add("memprof-shadow-scale-3")
938
939if config.expensive_checks:
940    config.available_features.add("expensive_checks")
941
942# Propagate the LLD/LTO into the clang config option, so nothing else is needed.
943run_wrapper = []
944target_cflags = [getattr(config, "target_cflags", None)]
945extra_cflags = []
946
947if config.use_lto and config.lto_supported:
948    extra_cflags += config.lto_flags
949elif config.use_lto and (not config.lto_supported):
950    config.unsupported = True
951
952if config.use_lld and config.has_lld and not config.use_lto:
953    extra_cflags += ["-fuse-ld=lld"]
954elif config.use_lld and (not config.has_lld):
955    config.unsupported = True
956
957if config.host_os == "Darwin":
958    if getattr(config, "darwin_linker_version", None):
959        extra_cflags += ["-mlinker-version=" + config.darwin_linker_version]
960
961# Append any extra flags passed in lit_config
962append_target_cflags = lit_config.params.get("append_target_cflags", None)
963if append_target_cflags:
964    lit_config.note('Appending to extra_cflags: "{}"'.format(append_target_cflags))
965    extra_cflags += [append_target_cflags]
966
967config.clang = (
968    " " + " ".join(run_wrapper + [config.compile_wrapper, config.clang]) + " "
969)
970config.target_cflags = " " + " ".join(target_cflags + extra_cflags) + " "
971
972if config.host_os == "Darwin":
973    config.substitutions.append(
974        (
975            "%get_pid_from_output",
976            "{} {}/get_pid_from_output.py".format(
977                shlex.quote(config.python_executable),
978                shlex.quote(get_ios_commands_dir()),
979            ),
980        )
981    )
982    config.substitutions.append(
983        (
984            "%print_crashreport_for_pid",
985            "{} {}/print_crashreport_for_pid.py".format(
986                shlex.quote(config.python_executable),
987                shlex.quote(get_ios_commands_dir()),
988            ),
989        )
990    )
991
992# It is not realistically possible to account for all options that could
993# possibly be present in system and user configuration files, so disable
994# default configs for the test runs. In particular, anything hardening
995# related is likely to cause issues with sanitizer tests, because it may
996# preempt something we're looking to trap (e.g. _FORTIFY_SOURCE vs our ASAN).
997#
998# Only set this if we know we can still build for the target while disabling
999# default configs.
1000if config.has_no_default_config_flag:
1001    config.environment["CLANG_NO_DEFAULT_CONFIG"] = "1"
1002
1003if config.has_compiler_rt_libatomic:
1004  base_lib = os.path.join(config.compiler_rt_libdir, "libclang_rt.atomic%s.so"
1005                          % config.target_suffix)
1006  if sys.platform in ['win32'] and execute_external:
1007    # Don't pass dosish path separator to msys bash.exe.
1008    base_lib = base_lib.replace('\\', '/')
1009  config.substitutions.append(("%libatomic", base_lib + f" -Wl,-rpath,{config.compiler_rt_libdir}"))
1010else:
1011  config.substitutions.append(("%libatomic", "-latomic"))
1012
1013# Set LD_LIBRARY_PATH to pick dynamic runtime up properly.
1014push_dynamic_library_lookup_path(config, config.compiler_rt_libdir)
1015
1016# GCC-ASan uses dynamic runtime by default.
1017if config.compiler_id == "GNU":
1018    gcc_dir = os.path.dirname(config.clang)
1019    libasan_dir = os.path.join(gcc_dir, "..", "lib" + config.bits)
1020    push_dynamic_library_lookup_path(config, libasan_dir)
1021
1022
1023# Help tests that make sure certain files are in-sync between compiler-rt and
1024# llvm.
1025config.substitutions.append(("%crt_src", config.compiler_rt_src_root))
1026config.substitutions.append(("%llvm_src", config.llvm_src_root))
1027