1
use std::error::Error;
2
use std::path::Path;
3

            
4
pub(crate) use duct::cmd;
5

            
6
/// Adds an explicit `--target <host triple>` so that `-Zbuild-std` has a concrete target.
7
///
8
/// The triple is derived from the host architecture and OS rather than hardcoded, so the
9
/// sanitizers also work on aarch64 hosts (e.g. Apple Silicon).
10
fn add_target_flag(arguments: &mut Vec<String>) {
11
    let arch = if cfg!(target_arch = "aarch64") {
12
        "aarch64"
13
    } else {
14
        "x86_64"
15
    };
16

            
17
    let triple = if cfg!(target_os = "linux") {
18
        Some(format!("{arch}-unknown-linux-gnu"))
19
    } else if cfg!(target_os = "macos") {
20
        Some(format!("{arch}-apple-darwin"))
21
    } else {
22
        None
23
    };
24

            
25
    if let Some(triple) = triple {
26
        arguments.push("--target".to_string());
27
        arguments.push(triple);
28
    }
29
}
30

            
31
///
32
/// Run the tests with the address sanitizer enabled to detect memory issues in unsafe code.
33
///
34
/// This only works under Linux and MacOS currently and requires the nightly toolchain.
35
///
36
pub(crate) fn address_sanitizer(mut arguments: Vec<String>) -> Result<(), Box<dyn Error>> {
37
    arguments.push("-Zbuild-std".to_string());
38

            
39
    add_target_flag(&mut arguments);
40

            
41
    let leak_sanitizer_suppress = Path::new(env!("CARGO_MANIFEST_DIR")).join("data/leak_sanitizer.suppress");
42

            
43
    cmd("cargo", arguments)
44
        .env(
45
            "LSAN_OPTIONS",
46
            format!("suppressions={}", leak_sanitizer_suppress.to_string_lossy()),
47
        )
48
        .env("RUSTFLAGS", "-Zsanitizer=address,leak")
49
        .env("RUSTDOCFLAGS", "-Zsanitizer=address,leak")
50
        .env("CFLAGS", "-fsanitize=address,leak")
51
        .env("CXXFLAGS", "-fsanitize=address,leak")
52
        .run()?;
53
    println!("ok.");
54

            
55
    Ok(())
56
}
57

            
58
///
59
/// Run the tests with the thread sanitizer enabled to detect data race conditions.
60
///
61
/// This only works under Linux and MacOS currently and requires the nightly toolchain.
62
///
63
pub(crate) fn thread_sanitizer(mut arguments: Vec<String>) -> Result<(), Box<dyn Error>> {
64
    arguments.push("-Zbuild-std".to_string());
65

            
66
    add_target_flag(&mut arguments);
67

            
68
    let thread_sanitizer_suppress = Path::new(env!("CARGO_MANIFEST_DIR")).join("data/thread_sanitizer.suppress");
69

            
70
    cmd("cargo", arguments)
71
        .env(
72
            "TSAN_OPTIONS",
73
            format!("suppressions={}", thread_sanitizer_suppress.to_string_lossy()),
74
        )
75
        .env("RUSTFLAGS", "-Zsanitizer=thread")
76
        .env("RUSTDOCFLAGS", "-Zsanitizer=thread")
77
        .env("CFLAGS", "-fsanitize=thread")
78
        .env("CXXFLAGS", "-fsanitize=thread")
79
        .run()?;
80
    println!("ok.");
81

            
82
    Ok(())
83
}