1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use std::{
    env,
    ffi::OsStr,
    path::{Path, PathBuf},
    process::{Command, Stdio},
    str,
};

use print_bytes::println_lossy;

fn print_cargo_path(name: &str, path: &Path) {
    print!("cargo:{name}");
    println_lossy(path);
}

pub struct SysRoot {
    path: PathBuf,
}

impl SysRoot {
    pub fn resolve() -> Self {
        let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into());
        let output = Command::new(rustc)
            .args(&["--print", "sysroot"])
            .output()
            .expect("could not invoke `rustc` to find rust sysroot");
        // trim, but `str::trim` doesn't exist on `[u8]`
        let path = output
            .stdout
            .as_slice()
            .split(|c| c.is_ascii_whitespace())
            .next()
            .unwrap_or_default();
        let path = if cfg!(unix) {
            use std::os::unix::ffi::OsStrExt;

            OsStr::from_bytes(path)
        } else {
            // Windows is hard, so just require UTF-8
            let path = str::from_utf8(path).expect("`rustc --print sysroot` is not UTF-8");
            OsStr::new(path)
        };
        let path = Path::new(path).to_owned();
        Self { path }
    }

    pub fn sysroot(&self) -> &Path {
        self.path.as_path()
    }

    pub fn lib(&self) -> PathBuf {
        self.sysroot().join("lib")
    }

    pub fn rustlib(&self) -> PathBuf {
        let target = env::var_os("TARGET").expect("cargo should set $TARGET");
        [
            self.sysroot(),
            Path::new("lib"),
            Path::new("rustlib"),
            Path::new(&target),
            Path::new("lib"),
        ]
        .iter()
        .collect()
    }

    pub fn link_rustc_private(&self) {
        let lib = self.lib();
        print_cargo_path("rustc-link-search=native=", &lib);
        print_cargo_path("rustc-link-arg=-Wl,-rpath,", &lib);
    }
}

pub fn find_llvm_config() -> Option<PathBuf> {
    // Explicitly provided path in LLVM_CONFIG_PATH
    env::var_os("LLVM_CONFIG_PATH")
        .map(PathBuf::from)
        .or_else(|| {
            // Relative to LLVM_LIB_DIR
            env::var_os("LLVM_LIB_DIR")
                .map(PathBuf::from)
                .map(|mut lib_dir| {
                    lib_dir.push("../bin/llvm-config");
                    lib_dir.canonicalize().unwrap()
                })
        })
        .or_else(|| {
            // In PATH
            [
                "llvm-config-14",
                "llvm-config-13",
                "llvm-config-12",
                "llvm-config-11",
                "llvm-config-10",
                "llvm-config-9",
                "llvm-config-8",
                "llvm-config-7",
                "llvm-config-7.0",
                "llvm-config",
                // Homebrew install locations on Intel macOS
                "/usr/local/opt/llvm@13/bin/llvm-config",
                "/usr/local/opt/llvm@12/bin/llvm-config",
                "/usr/local/opt/llvm@11/bin/llvm-config",
                "/usr/local/opt/llvm@10/bin/llvm-config",
                "/usr/local/opt/llvm@9/bin/llvm-config",
                "/usr/local/opt/llvm@8/bin/llvm-config",
                "/usr/local/opt/llvm/bin/llvm-config",
                // Homebrew install locations on Apple Silicon macOS
                "/opt/homebrew/opt/llvm@13/bin/llvm-config",
                "/opt/homebrew/opt/llvm@12/bin/llvm-config",
                "/opt/homebrew/opt/llvm@11/bin/llvm-config",
                "/opt/homebrew/opt/llvm@10/bin/llvm-config",
                "/opt/homebrew/opt/llvm@9/bin/llvm-config",
                "/opt/homebrew/opt/llvm@8/bin/llvm-config",
                "/opt/homebrew/opt/llvm/bin/llvm-config",
            ]
            .iter()
            .map(Path::new)
            .filter(|c| {
                Command::new(c)
                    .stdout(Stdio::null())
                    .stderr(Stdio::null())
                    .spawn()
                    .is_ok()
            })
            .map(PathBuf::from)
            .next()
        })
}