]>
Commit | Line | Data |
---|---|---|
7453a54e | 1 | use std::env; |
dfeec247 XL |
2 | use std::path::{Path, PathBuf}; |
3 | use std::process::Command; | |
7453a54e | 4 | |
3dfed10e | 5 | use build_helper::{output, tracked_env_var_os}; |
7453a54e | 6 | |
ff7c6d11 XL |
7 | fn detect_llvm_link() -> (&'static str, &'static str) { |
8 | // Force the link mode we want, preferring static by default, but | |
9 | // possibly overridden by `configure --enable-llvm-link-shared`. | |
3dfed10e | 10 | if tracked_env_var_os("LLVM_LINK_SHARED").is_some() { |
ff7c6d11 XL |
11 | ("dylib", "--link-shared") |
12 | } else { | |
13 | ("static", "--link-static") | |
476ff2be | 14 | } |
476ff2be SL |
15 | } |
16 | ||
7453a54e | 17 | fn main() { |
3dfed10e | 18 | if tracked_env_var_os("RUST_CHECK").is_some() { |
83c7162d | 19 | // If we're just running `check`, there's no need for LLVM to be built. |
83c7162d XL |
20 | return; |
21 | } | |
22 | ||
9fa01778 XL |
23 | build_helper::restore_library_path(); |
24 | ||
9e0c209e | 25 | let target = env::var("TARGET").expect("TARGET was not set"); |
ba9703b0 | 26 | let llvm_config = |
3dfed10e XL |
27 | tracked_env_var_os("LLVM_CONFIG").map(|x| Some(PathBuf::from(x))).unwrap_or_else(|| { |
28 | if let Some(dir) = tracked_env_var_os("CARGO_TARGET_DIR").map(PathBuf::from) { | |
ba9703b0 XL |
29 | let to_test = dir |
30 | .parent() | |
31 | .unwrap() | |
32 | .parent() | |
33 | .unwrap() | |
34 | .join(&target) | |
35 | .join("llvm/bin/llvm-config"); | |
36 | if Command::new(&to_test).output().is_ok() { | |
37 | return Some(to_test); | |
38 | } | |
c30ab7b3 | 39 | } |
ba9703b0 XL |
40 | None |
41 | }); | |
42 | ||
43 | if let Some(llvm_config) = &llvm_config { | |
44 | println!("cargo:rerun-if-changed={}", llvm_config.display()); | |
45 | } | |
46 | let llvm_config = llvm_config.unwrap_or_else(|| PathBuf::from("llvm-config")); | |
7453a54e | 47 | |
7453a54e SL |
48 | // Test whether we're cross-compiling LLVM. This is a pretty rare case |
49 | // currently where we're producing an LLVM for a different platform than | |
50 | // what this build script is currently running on. | |
51 | // | |
52 | // In that case, there's no guarantee that we can actually run the target, | |
53 | // so the build system works around this by giving us the LLVM_CONFIG for | |
54 | // the host platform. This only really works if the host LLVM and target | |
55 | // LLVM are compiled the same way, but for us that's typically the case. | |
56 | // | |
54a0048b | 57 | // We *want* detect this cross compiling situation by asking llvm-config |
e1599b0c | 58 | // what its host-target is. If that's not the TARGET, then we're cross |
54a0048b SL |
59 | // compiling. Unfortunately `llvm-config` seems either be buggy, or we're |
60 | // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will | |
61 | // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This | |
62 | // tricks us into thinking we're doing a cross build when we aren't, so | |
63 | // havoc ensues. | |
64 | // | |
65 | // In any case, if we're cross compiling, this generally just means that we | |
e1599b0c | 66 | // can't trust all the output of llvm-config because it might be targeted |
54a0048b SL |
67 | // for the host rather than the target. As a result a bunch of blocks below |
68 | // are gated on `if !is_crossed` | |
9e0c209e SL |
69 | let target = env::var("TARGET").expect("TARGET was not set"); |
70 | let host = env::var("HOST").expect("HOST was not set"); | |
7453a54e SL |
71 | let is_crossed = target != host; |
72 | ||
29967ef6 | 73 | let optional_components = &[ |
dfeec247 XL |
74 | "x86", |
75 | "arm", | |
76 | "aarch64", | |
77 | "amdgpu", | |
f035d41b | 78 | "avr", |
c295e0f8 | 79 | "m68k", |
dfeec247 XL |
80 | "mips", |
81 | "powerpc", | |
82 | "systemz", | |
83 | "jsbackend", | |
84 | "webassembly", | |
85 | "msp430", | |
86 | "sparc", | |
87 | "nvptx", | |
88 | "hexagon", | |
29967ef6 | 89 | "riscv", |
17df50a5 | 90 | "bpf", |
dfeec247 | 91 | ]; |
7cac9316 | 92 | |
3dfed10e XL |
93 | let required_components = &[ |
94 | "ipo", | |
95 | "bitreader", | |
96 | "bitwriter", | |
97 | "linker", | |
98 | "asmparser", | |
99 | "lto", | |
100 | "coverage", | |
101 | "instrumentation", | |
102 | ]; | |
7453a54e SL |
103 | |
104 | let components = output(Command::new(&llvm_config).arg("--components")); | |
105 | let mut components = components.split_whitespace().collect::<Vec<_>>(); | |
3157f602 | 106 | components.retain(|c| optional_components.contains(c) || required_components.contains(c)); |
7453a54e SL |
107 | |
108 | for component in required_components { | |
109 | if !components.contains(component) { | |
110 | panic!("require llvm component {} but wasn't found", component); | |
111 | } | |
112 | } | |
113 | ||
114 | for component in components.iter() { | |
115 | println!("cargo:rustc-cfg=llvm_component=\"{}\"", component); | |
116 | } | |
117 | ||
118 | // Link in our own LLVM shims, compiled with the same flags as LLVM | |
119 | let mut cmd = Command::new(&llvm_config); | |
120 | cmd.arg("--cxxflags"); | |
121 | let cxxflags = output(&mut cmd); | |
ea8adc8c XL |
122 | let mut cfg = cc::Build::new(); |
123 | cfg.warnings(false); | |
7453a54e SL |
124 | for flag in cxxflags.split_whitespace() { |
125 | // Ignore flags like `-m64` when we're doing a cross build | |
126 | if is_crossed && flag.starts_with("-m") { | |
3157f602 | 127 | continue; |
7453a54e | 128 | } |
cc61c64b | 129 | |
0731742a XL |
130 | if flag.starts_with("-flto") { |
131 | continue; | |
132 | } | |
133 | ||
cc61c64b XL |
134 | // -Wdate-time is not supported by the netbsd cross compiler |
135 | if is_crossed && target.contains("netbsd") && flag.contains("date-time") { | |
136 | continue; | |
137 | } | |
138 | ||
3dfed10e XL |
139 | // Include path contains host directory, replace it with target |
140 | if is_crossed && flag.starts_with("-I") { | |
141 | cfg.flag(&flag.replace(&host, &target)); | |
142 | continue; | |
143 | } | |
144 | ||
7453a54e SL |
145 | cfg.flag(flag); |
146 | } | |
a7813a04 | 147 | |
cc61c64b | 148 | for component in &components { |
ff7c6d11 | 149 | let mut flag = String::from("LLVM_COMPONENT_"); |
a7813a04 | 150 | flag.push_str(&component.to_uppercase()); |
ff7c6d11 | 151 | cfg.define(&flag, None); |
a7813a04 XL |
152 | } |
153 | ||
3dfed10e | 154 | if tracked_env_var_os("LLVM_RUSTLLVM").is_some() { |
ff7c6d11 | 155 | cfg.define("LLVM_RUSTLLVM", None); |
5bcae85e SL |
156 | } |
157 | ||
3dfed10e | 158 | if tracked_env_var_os("LLVM_NDEBUG").is_some() { |
e1599b0c | 159 | cfg.define("NDEBUG", None); |
ba9703b0 | 160 | cfg.debug(false); |
e1599b0c XL |
161 | } |
162 | ||
1b1a35ee XL |
163 | build_helper::rerun_if_changed_anything_in_dir(Path::new("llvm-wrapper")); |
164 | cfg.file("llvm-wrapper/PassWrapper.cpp") | |
165 | .file("llvm-wrapper/RustWrapper.cpp") | |
166 | .file("llvm-wrapper/ArchiveWrapper.cpp") | |
167 | .file("llvm-wrapper/CoverageMappingWrapper.cpp") | |
168 | .file("llvm-wrapper/Linker.cpp") | |
dfeec247 XL |
169 | .cpp(true) |
170 | .cpp_link_stdlib(None) // we handle this below | |
1b1a35ee | 171 | .compile("llvm-wrapper"); |
7453a54e | 172 | |
ff7c6d11 | 173 | let (llvm_kind, llvm_link_arg) = detect_llvm_link(); |
476ff2be | 174 | |
e1599b0c | 175 | // Link in all LLVM libraries, if we're using the "wrong" llvm-config then |
7453a54e SL |
176 | // we don't pick up system libs because unfortunately they're for the host |
177 | // of llvm-config, not the target that we're attempting to link. | |
178 | let mut cmd = Command::new(&llvm_config); | |
ff7c6d11 | 179 | cmd.arg(llvm_link_arg).arg("--libs"); |
c30ab7b3 | 180 | |
7453a54e SL |
181 | if !is_crossed { |
182 | cmd.arg("--system-libs"); | |
3dfed10e XL |
183 | } else if target.contains("windows-gnu") { |
184 | println!("cargo:rustc-link-lib=shell32"); | |
185 | println!("cargo:rustc-link-lib=uuid"); | |
94222f64 | 186 | } else if target.contains("netbsd") || target.contains("haiku") || target.contains("darwin") { |
1b1a35ee | 187 | println!("cargo:rustc-link-lib=z"); |
7453a54e | 188 | } |
cc61c64b | 189 | cmd.args(&components); |
7453a54e SL |
190 | |
191 | for lib in output(&mut cmd).split_whitespace() { | |
fc512014 XL |
192 | let name = if let Some(stripped) = lib.strip_prefix("-l") { |
193 | stripped | |
194 | } else if let Some(stripped) = lib.strip_prefix('-') { | |
195 | stripped | |
5bcae85e SL |
196 | } else if Path::new(lib).exists() { |
197 | // On MSVC llvm-config will print the full name to libraries, but | |
198 | // we're only interested in the name part | |
199 | let name = Path::new(lib).file_name().unwrap().to_str().unwrap(); | |
0731742a | 200 | name.trim_end_matches(".lib") |
5bcae85e SL |
201 | } else if lib.ends_with(".lib") { |
202 | // Some MSVC libraries just come up with `.lib` tacked on, so chop | |
203 | // that off | |
0731742a | 204 | lib.trim_end_matches(".lib") |
7453a54e | 205 | } else { |
c30ab7b3 | 206 | continue; |
7453a54e SL |
207 | }; |
208 | ||
209 | // Don't need or want this library, but LLVM's CMake build system | |
210 | // doesn't provide a way to disable it, so filter it here even though we | |
211 | // may or may not have built it. We don't reference anything from this | |
212 | // library and it otherwise may just pull in extra dependencies on | |
213 | // libedit which we don't want | |
214 | if name == "LLVMLineEditor" { | |
c30ab7b3 | 215 | continue; |
7453a54e SL |
216 | } |
217 | ||
dfeec247 | 218 | let kind = if name.starts_with("LLVM") { llvm_kind } else { "dylib" }; |
7453a54e SL |
219 | println!("cargo:rustc-link-lib={}={}", kind, name); |
220 | } | |
221 | ||
222 | // LLVM ldflags | |
223 | // | |
224 | // If we're a cross-compile of LLVM then unfortunately we can't trust these | |
225 | // ldflags (largely where all the LLVM libs are located). Currently just | |
226 | // hack around this by replacing the host triple with the target and pray | |
227 | // that those -L directories are the same! | |
228 | let mut cmd = Command::new(&llvm_config); | |
ff7c6d11 | 229 | cmd.arg(llvm_link_arg).arg("--ldflags"); |
7453a54e | 230 | for lib in output(&mut cmd).split_whitespace() { |
dfeec247 | 231 | if is_crossed { |
fc512014 XL |
232 | if let Some(stripped) = lib.strip_prefix("-LIBPATH:") { |
233 | println!("cargo:rustc-link-search=native={}", stripped.replace(&host, &target)); | |
234 | } else if let Some(stripped) = lib.strip_prefix("-L") { | |
235 | println!("cargo:rustc-link-search=native={}", stripped.replace(&host, &target)); | |
7453a54e | 236 | } |
fc512014 XL |
237 | } else if let Some(stripped) = lib.strip_prefix("-LIBPATH:") { |
238 | println!("cargo:rustc-link-search=native={}", stripped); | |
239 | } else if let Some(stripped) = lib.strip_prefix("-l") { | |
240 | println!("cargo:rustc-link-lib={}", stripped); | |
241 | } else if let Some(stripped) = lib.strip_prefix("-L") { | |
242 | println!("cargo:rustc-link-search=native={}", stripped); | |
7453a54e SL |
243 | } |
244 | } | |
245 | ||
dc9dc135 | 246 | // Some LLVM linker flags (-L and -l) may be needed even when linking |
1b1a35ee | 247 | // rustc_llvm, for example when using static libc++, we may need to |
dc9dc135 XL |
248 | // manually specify the library search path and -ldl -lpthread as link |
249 | // dependencies. | |
3dfed10e | 250 | let llvm_linker_flags = tracked_env_var_os("LLVM_LINKER_FLAGS"); |
dc9dc135 XL |
251 | if let Some(s) = llvm_linker_flags { |
252 | for lib in s.into_string().unwrap().split_whitespace() { | |
fc512014 XL |
253 | if let Some(stripped) = lib.strip_prefix("-l") { |
254 | println!("cargo:rustc-link-lib={}", stripped); | |
255 | } else if let Some(stripped) = lib.strip_prefix("-L") { | |
256 | println!("cargo:rustc-link-search=native={}", stripped); | |
dc9dc135 XL |
257 | } |
258 | } | |
259 | } | |
260 | ||
3dfed10e XL |
261 | let llvm_static_stdcpp = tracked_env_var_os("LLVM_STATIC_STDCPP"); |
262 | let llvm_use_libcxx = tracked_env_var_os("LLVM_USE_LIBCXX"); | |
cc61c64b | 263 | |
476ff2be | 264 | let stdcppname = if target.contains("openbsd") { |
dfeec247 | 265 | if target.contains("sparc64") { "estdc++" } else { "c++" } |
ff7c6d11 XL |
266 | } else if target.contains("freebsd") { |
267 | "c++" | |
9fa01778 XL |
268 | } else if target.contains("darwin") { |
269 | "c++" | |
cc61c64b XL |
270 | } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() { |
271 | // NetBSD uses a separate library when relocation is required | |
272 | "stdc++_pic" | |
0731742a XL |
273 | } else if llvm_use_libcxx.is_some() { |
274 | "c++" | |
476ff2be SL |
275 | } else { |
276 | "stdc++" | |
277 | }; | |
278 | ||
3dfed10e XL |
279 | // RISC-V requires libatomic for sub-word atomic operations |
280 | if target.starts_with("riscv") { | |
281 | println!("cargo:rustc-link-lib=atomic"); | |
282 | } | |
283 | ||
7453a54e SL |
284 | // C++ runtime library |
285 | if !target.contains("msvc") { | |
cc61c64b | 286 | if let Some(s) = llvm_static_stdcpp { |
7453a54e SL |
287 | assert!(!cxxflags.contains("stdlib=libc++")); |
288 | let path = PathBuf::from(s); | |
dfeec247 | 289 | println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display()); |
e74abb32 | 290 | if target.contains("windows") { |
3c0e092e | 291 | println!("cargo:rustc-link-lib=static:-bundle={}", stdcppname); |
e74abb32 XL |
292 | } else { |
293 | println!("cargo:rustc-link-lib=static={}", stdcppname); | |
294 | } | |
7453a54e SL |
295 | } else if cxxflags.contains("stdlib=libc++") { |
296 | println!("cargo:rustc-link-lib=c++"); | |
297 | } else { | |
476ff2be | 298 | println!("cargo:rustc-link-lib={}", stdcppname); |
7453a54e SL |
299 | } |
300 | } | |
cc61c64b | 301 | |
f035d41b XL |
302 | // Libstdc++ depends on pthread which Rust doesn't link on MinGW |
303 | // since nothing else requires it. | |
7cac9316 | 304 | if target.contains("windows-gnu") { |
3c0e092e | 305 | println!("cargo:rustc-link-lib=static:-bundle=pthread"); |
cc61c64b | 306 | } |
7453a54e | 307 | } |