]>
Commit | Line | Data |
---|---|---|
7453a54e SL |
1 | // Copyright 2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | extern crate gcc; | |
12 | extern crate build_helper; | |
13 | ||
14 | use std::process::Command; | |
15 | use std::env; | |
5bcae85e | 16 | use std::path::{PathBuf, Path}; |
7453a54e SL |
17 | |
18 | use build_helper::output; | |
19 | ||
7cac9316 XL |
20 | fn detect_llvm_link(major: u32, minor: u32, llvm_config: &Path) |
21 | -> (&'static str, Option<&'static str>) { | |
22 | if major > 3 || (major == 3 && minor >= 9) { | |
23 | // Force the link mode we want, preferring static by default, but | |
24 | // possibly overridden by `configure --enable-llvm-link-shared`. | |
25 | if env::var_os("LLVM_LINK_SHARED").is_some() { | |
26 | return ("dylib", Some("--link-shared")); | |
27 | } else { | |
28 | return ("static", Some("--link-static")); | |
29 | } | |
30 | } else if major == 3 && minor == 8 { | |
31 | // Find out LLVM's default linking mode. | |
32 | let mut mode_cmd = Command::new(llvm_config); | |
33 | mode_cmd.arg("--shared-mode"); | |
34 | if output(&mut mode_cmd).trim() == "shared" { | |
35 | return ("dylib", None); | |
36 | } else { | |
37 | return ("static", None); | |
476ff2be SL |
38 | } |
39 | } | |
40 | ("static", None) | |
41 | } | |
42 | ||
7453a54e | 43 | fn main() { |
9e0c209e | 44 | let target = env::var("TARGET").expect("TARGET was not set"); |
3157f602 | 45 | let llvm_config = env::var_os("LLVM_CONFIG") |
c30ab7b3 SL |
46 | .map(PathBuf::from) |
47 | .unwrap_or_else(|| { | |
48 | if let Some(dir) = env::var_os("CARGO_TARGET_DIR").map(PathBuf::from) { | |
49 | let to_test = dir.parent() | |
50 | .unwrap() | |
51 | .parent() | |
52 | .unwrap() | |
53 | .join(&target) | |
54 | .join("llvm/bin/llvm-config"); | |
55 | if Command::new(&to_test).output().is_ok() { | |
56 | return to_test; | |
57 | } | |
58 | } | |
59 | PathBuf::from("llvm-config") | |
60 | }); | |
7453a54e SL |
61 | |
62 | println!("cargo:rerun-if-changed={}", llvm_config.display()); | |
63 | ||
64 | // Test whether we're cross-compiling LLVM. This is a pretty rare case | |
65 | // currently where we're producing an LLVM for a different platform than | |
66 | // what this build script is currently running on. | |
67 | // | |
68 | // In that case, there's no guarantee that we can actually run the target, | |
69 | // so the build system works around this by giving us the LLVM_CONFIG for | |
70 | // the host platform. This only really works if the host LLVM and target | |
71 | // LLVM are compiled the same way, but for us that's typically the case. | |
72 | // | |
54a0048b SL |
73 | // We *want* detect this cross compiling situation by asking llvm-config |
74 | // what it's host-target is. If that's not the TARGET, then we're cross | |
75 | // compiling. Unfortunately `llvm-config` seems either be buggy, or we're | |
76 | // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will | |
77 | // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This | |
78 | // tricks us into thinking we're doing a cross build when we aren't, so | |
79 | // havoc ensues. | |
80 | // | |
81 | // In any case, if we're cross compiling, this generally just means that we | |
82 | // can't trust all the output of llvm-config becaues it might be targeted | |
83 | // for the host rather than the target. As a result a bunch of blocks below | |
84 | // are gated on `if !is_crossed` | |
9e0c209e SL |
85 | let target = env::var("TARGET").expect("TARGET was not set"); |
86 | let host = env::var("HOST").expect("HOST was not set"); | |
7453a54e SL |
87 | let is_crossed = target != host; |
88 | ||
7cac9316 XL |
89 | let mut optional_components = |
90 | vec!["x86", "arm", "aarch64", "mips", "powerpc", "pnacl", | |
91 | "systemz", "jsbackend", "msp430", "sparc", "nvptx"]; | |
92 | ||
93 | let mut version_cmd = Command::new(&llvm_config); | |
94 | version_cmd.arg("--version"); | |
95 | let version_output = output(&mut version_cmd); | |
96 | let mut parts = version_output.split('.').take(2) | |
97 | .filter_map(|s| s.parse::<u32>().ok()); | |
98 | let (major, minor) = | |
99 | if let (Some(major), Some(minor)) = (parts.next(), parts.next()) { | |
100 | (major, minor) | |
101 | } else { | |
102 | (3, 7) | |
103 | }; | |
104 | ||
105 | if major > 3 { | |
106 | optional_components.push("hexagon"); | |
107 | } | |
7453a54e SL |
108 | |
109 | // FIXME: surely we don't need all these components, right? Stuff like mcjit | |
110 | // or interpreter the compiler itself never uses. | |
3157f602 XL |
111 | let required_components = &["ipo", |
112 | "bitreader", | |
113 | "bitwriter", | |
114 | "linker", | |
115 | "asmparser", | |
116 | "mcjit", | |
117 | "interpreter", | |
7453a54e SL |
118 | "instrumentation"]; |
119 | ||
120 | let components = output(Command::new(&llvm_config).arg("--components")); | |
121 | let mut components = components.split_whitespace().collect::<Vec<_>>(); | |
3157f602 | 122 | components.retain(|c| optional_components.contains(c) || required_components.contains(c)); |
7453a54e SL |
123 | |
124 | for component in required_components { | |
125 | if !components.contains(component) { | |
126 | panic!("require llvm component {} but wasn't found", component); | |
127 | } | |
128 | } | |
129 | ||
130 | for component in components.iter() { | |
131 | println!("cargo:rustc-cfg=llvm_component=\"{}\"", component); | |
132 | } | |
133 | ||
134 | // Link in our own LLVM shims, compiled with the same flags as LLVM | |
135 | let mut cmd = Command::new(&llvm_config); | |
136 | cmd.arg("--cxxflags"); | |
137 | let cxxflags = output(&mut cmd); | |
138 | let mut cfg = gcc::Config::new(); | |
139 | for flag in cxxflags.split_whitespace() { | |
140 | // Ignore flags like `-m64` when we're doing a cross build | |
141 | if is_crossed && flag.starts_with("-m") { | |
3157f602 | 142 | continue; |
7453a54e | 143 | } |
cc61c64b XL |
144 | |
145 | // -Wdate-time is not supported by the netbsd cross compiler | |
146 | if is_crossed && target.contains("netbsd") && flag.contains("date-time") { | |
147 | continue; | |
148 | } | |
149 | ||
7453a54e SL |
150 | cfg.flag(flag); |
151 | } | |
a7813a04 | 152 | |
cc61c64b | 153 | for component in &components { |
a7813a04 XL |
154 | let mut flag = String::from("-DLLVM_COMPONENT_"); |
155 | flag.push_str(&component.to_uppercase()); | |
156 | cfg.flag(&flag); | |
157 | } | |
158 | ||
5bcae85e SL |
159 | if env::var_os("LLVM_RUSTLLVM").is_some() { |
160 | cfg.flag("-DLLVM_RUSTLLVM"); | |
161 | } | |
162 | ||
8bb4bdeb | 163 | build_helper::rerun_if_changed_anything_in_dir(Path::new("../rustllvm")); |
5bcae85e | 164 | cfg.file("../rustllvm/PassWrapper.cpp") |
7453a54e SL |
165 | .file("../rustllvm/RustWrapper.cpp") |
166 | .file("../rustllvm/ArchiveWrapper.cpp") | |
167 | .cpp(true) | |
168 | .cpp_link_stdlib(None) // we handle this below | |
169 | .compile("librustllvm.a"); | |
170 | ||
7cac9316 | 171 | let (llvm_kind, llvm_link_arg) = detect_llvm_link(major, minor, &llvm_config); |
476ff2be | 172 | |
7453a54e SL |
173 | // Link in all LLVM libraries, if we're uwring the "wrong" llvm-config then |
174 | // we don't pick up system libs because unfortunately they're for the host | |
175 | // of llvm-config, not the target that we're attempting to link. | |
176 | let mut cmd = Command::new(&llvm_config); | |
177 | cmd.arg("--libs"); | |
c30ab7b3 | 178 | |
476ff2be SL |
179 | if let Some(link_arg) = llvm_link_arg { |
180 | cmd.arg(link_arg); | |
c30ab7b3 SL |
181 | } |
182 | ||
7453a54e SL |
183 | if !is_crossed { |
184 | cmd.arg("--system-libs"); | |
185 | } | |
cc61c64b | 186 | cmd.args(&components); |
7453a54e SL |
187 | |
188 | for lib in output(&mut cmd).split_whitespace() { | |
189 | let name = if lib.starts_with("-l") { | |
190 | &lib[2..] | |
191 | } else if lib.starts_with("-") { | |
192 | &lib[1..] | |
5bcae85e SL |
193 | } else if Path::new(lib).exists() { |
194 | // On MSVC llvm-config will print the full name to libraries, but | |
195 | // we're only interested in the name part | |
196 | let name = Path::new(lib).file_name().unwrap().to_str().unwrap(); | |
197 | name.trim_right_matches(".lib") | |
198 | } else if lib.ends_with(".lib") { | |
199 | // Some MSVC libraries just come up with `.lib` tacked on, so chop | |
200 | // that off | |
201 | lib.trim_right_matches(".lib") | |
7453a54e | 202 | } else { |
c30ab7b3 | 203 | continue; |
7453a54e SL |
204 | }; |
205 | ||
206 | // Don't need or want this library, but LLVM's CMake build system | |
207 | // doesn't provide a way to disable it, so filter it here even though we | |
208 | // may or may not have built it. We don't reference anything from this | |
209 | // library and it otherwise may just pull in extra dependencies on | |
210 | // libedit which we don't want | |
211 | if name == "LLVMLineEditor" { | |
c30ab7b3 | 212 | continue; |
7453a54e SL |
213 | } |
214 | ||
3157f602 | 215 | let kind = if name.starts_with("LLVM") { |
476ff2be | 216 | llvm_kind |
3157f602 XL |
217 | } else { |
218 | "dylib" | |
219 | }; | |
7453a54e SL |
220 | println!("cargo:rustc-link-lib={}={}", kind, name); |
221 | } | |
222 | ||
223 | // LLVM ldflags | |
224 | // | |
225 | // If we're a cross-compile of LLVM then unfortunately we can't trust these | |
226 | // ldflags (largely where all the LLVM libs are located). Currently just | |
227 | // hack around this by replacing the host triple with the target and pray | |
228 | // that those -L directories are the same! | |
229 | let mut cmd = Command::new(&llvm_config); | |
cc61c64b XL |
230 | if let Some(link_arg) = llvm_link_arg { |
231 | cmd.arg(link_arg); | |
232 | } | |
7453a54e SL |
233 | cmd.arg("--ldflags"); |
234 | for lib in output(&mut cmd).split_whitespace() { | |
5bcae85e | 235 | if lib.starts_with("-LIBPATH:") { |
c30ab7b3 | 236 | println!("cargo:rustc-link-search=native={}", &lib[9..]); |
5bcae85e | 237 | } else if is_crossed { |
7453a54e SL |
238 | if lib.starts_with("-L") { |
239 | println!("cargo:rustc-link-search=native={}", | |
240 | lib[2..].replace(&host, &target)); | |
241 | } | |
242 | } else if lib.starts_with("-l") { | |
243 | println!("cargo:rustc-link-lib={}", &lib[2..]); | |
244 | } else if lib.starts_with("-L") { | |
245 | println!("cargo:rustc-link-search=native={}", &lib[2..]); | |
246 | } | |
247 | } | |
248 | ||
cc61c64b XL |
249 | let llvm_static_stdcpp = env::var_os("LLVM_STATIC_STDCPP"); |
250 | ||
476ff2be | 251 | let stdcppname = if target.contains("openbsd") { |
cc61c64b | 252 | // OpenBSD has a particular C++ runtime library name |
476ff2be | 253 | "estdc++" |
cc61c64b XL |
254 | } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() { |
255 | // NetBSD uses a separate library when relocation is required | |
256 | "stdc++_pic" | |
476ff2be SL |
257 | } else { |
258 | "stdc++" | |
259 | }; | |
260 | ||
7453a54e SL |
261 | // C++ runtime library |
262 | if !target.contains("msvc") { | |
cc61c64b | 263 | if let Some(s) = llvm_static_stdcpp { |
7453a54e SL |
264 | assert!(!cxxflags.contains("stdlib=libc++")); |
265 | let path = PathBuf::from(s); | |
266 | println!("cargo:rustc-link-search=native={}", | |
267 | path.parent().unwrap().display()); | |
476ff2be | 268 | println!("cargo:rustc-link-lib=static={}", stdcppname); |
7453a54e SL |
269 | } else if cxxflags.contains("stdlib=libc++") { |
270 | println!("cargo:rustc-link-lib=c++"); | |
271 | } else { | |
476ff2be | 272 | println!("cargo:rustc-link-lib={}", stdcppname); |
7453a54e SL |
273 | } |
274 | } | |
cc61c64b | 275 | |
7cac9316 | 276 | // LLVM requires symbols from this library, but apparently they're not printed |
cc61c64b | 277 | // during llvm-config? |
7cac9316 XL |
278 | if target.contains("windows-gnu") { |
279 | println!("cargo:rustc-link-lib=static-nobundle=gcc_s"); | |
280 | println!("cargo:rustc-link-lib=static-nobundle=pthread"); | |
cc61c64b | 281 | } |
7453a54e | 282 | } |