]>
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; | |
16 | use std::path::PathBuf; | |
17 | ||
18 | use build_helper::output; | |
19 | ||
20 | fn main() { | |
21 | println!("cargo:rustc-cfg=cargobuild"); | |
22 | ||
23 | let target = env::var("TARGET").unwrap(); | |
24 | let llvm_config = env::var_os("LLVM_CONFIG").map(PathBuf::from) | |
25 | .unwrap_or_else(|| { | |
26 | match env::var_os("CARGO_TARGET_DIR").map(PathBuf::from) { | |
27 | Some(dir) => { | |
28 | let to_test = dir.parent().unwrap().parent().unwrap() | |
29 | .join(&target).join("llvm/bin/llvm-config"); | |
30 | if Command::new(&to_test).output().is_ok() { | |
31 | return to_test | |
32 | } | |
33 | } | |
34 | None => {} | |
35 | } | |
36 | PathBuf::from("llvm-config") | |
37 | }); | |
38 | ||
39 | println!("cargo:rerun-if-changed={}", llvm_config.display()); | |
40 | ||
41 | // Test whether we're cross-compiling LLVM. This is a pretty rare case | |
42 | // currently where we're producing an LLVM for a different platform than | |
43 | // what this build script is currently running on. | |
44 | // | |
45 | // In that case, there's no guarantee that we can actually run the target, | |
46 | // so the build system works around this by giving us the LLVM_CONFIG for | |
47 | // the host platform. This only really works if the host LLVM and target | |
48 | // LLVM are compiled the same way, but for us that's typically the case. | |
49 | // | |
54a0048b SL |
50 | // We *want* detect this cross compiling situation by asking llvm-config |
51 | // what it's host-target is. If that's not the TARGET, then we're cross | |
52 | // compiling. Unfortunately `llvm-config` seems either be buggy, or we're | |
53 | // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will | |
54 | // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This | |
55 | // tricks us into thinking we're doing a cross build when we aren't, so | |
56 | // havoc ensues. | |
57 | // | |
58 | // In any case, if we're cross compiling, this generally just means that we | |
59 | // can't trust all the output of llvm-config becaues it might be targeted | |
60 | // for the host rather than the target. As a result a bunch of blocks below | |
61 | // are gated on `if !is_crossed` | |
7453a54e | 62 | let target = env::var("TARGET").unwrap(); |
54a0048b | 63 | let host = env::var("HOST").unwrap(); |
7453a54e SL |
64 | let is_crossed = target != host; |
65 | ||
66 | let optional_components = ["x86", "arm", "aarch64", "mips", "powerpc", | |
67 | "pnacl"]; | |
68 | ||
69 | // FIXME: surely we don't need all these components, right? Stuff like mcjit | |
70 | // or interpreter the compiler itself never uses. | |
71 | let required_components = &["ipo", "bitreader", "bitwriter", "linker", | |
72 | "asmparser", "mcjit", "interpreter", | |
73 | "instrumentation"]; | |
74 | ||
75 | let components = output(Command::new(&llvm_config).arg("--components")); | |
76 | let mut components = components.split_whitespace().collect::<Vec<_>>(); | |
77 | components.retain(|c| { | |
78 | optional_components.contains(c) || required_components.contains(c) | |
79 | }); | |
80 | ||
81 | for component in required_components { | |
82 | if !components.contains(component) { | |
83 | panic!("require llvm component {} but wasn't found", component); | |
84 | } | |
85 | } | |
86 | ||
87 | for component in components.iter() { | |
88 | println!("cargo:rustc-cfg=llvm_component=\"{}\"", component); | |
89 | } | |
90 | ||
91 | // Link in our own LLVM shims, compiled with the same flags as LLVM | |
92 | let mut cmd = Command::new(&llvm_config); | |
93 | cmd.arg("--cxxflags"); | |
94 | let cxxflags = output(&mut cmd); | |
95 | let mut cfg = gcc::Config::new(); | |
96 | for flag in cxxflags.split_whitespace() { | |
97 | // Ignore flags like `-m64` when we're doing a cross build | |
98 | if is_crossed && flag.starts_with("-m") { | |
99 | continue | |
100 | } | |
101 | cfg.flag(flag); | |
102 | } | |
103 | cfg.file("../rustllvm/ExecutionEngineWrapper.cpp") | |
104 | .file("../rustllvm/PassWrapper.cpp") | |
105 | .file("../rustllvm/RustWrapper.cpp") | |
106 | .file("../rustllvm/ArchiveWrapper.cpp") | |
107 | .cpp(true) | |
108 | .cpp_link_stdlib(None) // we handle this below | |
109 | .compile("librustllvm.a"); | |
110 | ||
111 | // Link in all LLVM libraries, if we're uwring the "wrong" llvm-config then | |
112 | // we don't pick up system libs because unfortunately they're for the host | |
113 | // of llvm-config, not the target that we're attempting to link. | |
114 | let mut cmd = Command::new(&llvm_config); | |
115 | cmd.arg("--libs"); | |
116 | if !is_crossed { | |
117 | cmd.arg("--system-libs"); | |
118 | } | |
119 | cmd.args(&components[..]); | |
120 | ||
121 | for lib in output(&mut cmd).split_whitespace() { | |
122 | let name = if lib.starts_with("-l") { | |
123 | &lib[2..] | |
124 | } else if lib.starts_with("-") { | |
125 | &lib[1..] | |
126 | } else { | |
127 | continue | |
128 | }; | |
129 | ||
130 | // Don't need or want this library, but LLVM's CMake build system | |
131 | // doesn't provide a way to disable it, so filter it here even though we | |
132 | // may or may not have built it. We don't reference anything from this | |
133 | // library and it otherwise may just pull in extra dependencies on | |
134 | // libedit which we don't want | |
135 | if name == "LLVMLineEditor" { | |
136 | continue | |
137 | } | |
138 | ||
139 | let kind = if name.starts_with("LLVM") {"static"} else {"dylib"}; | |
140 | println!("cargo:rustc-link-lib={}={}", kind, name); | |
141 | } | |
142 | ||
143 | // LLVM ldflags | |
144 | // | |
145 | // If we're a cross-compile of LLVM then unfortunately we can't trust these | |
146 | // ldflags (largely where all the LLVM libs are located). Currently just | |
147 | // hack around this by replacing the host triple with the target and pray | |
148 | // that those -L directories are the same! | |
149 | let mut cmd = Command::new(&llvm_config); | |
150 | cmd.arg("--ldflags"); | |
151 | for lib in output(&mut cmd).split_whitespace() { | |
152 | if is_crossed { | |
153 | if lib.starts_with("-L") { | |
154 | println!("cargo:rustc-link-search=native={}", | |
155 | lib[2..].replace(&host, &target)); | |
156 | } | |
157 | } else if lib.starts_with("-l") { | |
158 | println!("cargo:rustc-link-lib={}", &lib[2..]); | |
159 | } else if lib.starts_with("-L") { | |
160 | println!("cargo:rustc-link-search=native={}", &lib[2..]); | |
161 | } | |
162 | } | |
163 | ||
164 | // C++ runtime library | |
165 | if !target.contains("msvc") { | |
166 | if let Some(s) = env::var_os("LLVM_STATIC_STDCPP") { | |
167 | assert!(!cxxflags.contains("stdlib=libc++")); | |
168 | let path = PathBuf::from(s); | |
169 | println!("cargo:rustc-link-search=native={}", | |
170 | path.parent().unwrap().display()); | |
171 | println!("cargo:rustc-link-lib=static=stdc++"); | |
172 | } else if cxxflags.contains("stdlib=libc++") { | |
173 | println!("cargo:rustc-link-lib=c++"); | |
174 | } else { | |
175 | println!("cargo:rustc-link-lib=stdc++"); | |
176 | } | |
177 | } | |
178 | } |