]>
Commit | Line | Data |
---|---|---|
5bcae85e SL |
1 | //! Compilation of native dependencies like LLVM. |
2 | //! | |
3 | //! Native projects like LLVM unfortunately aren't suited just yet for | |
3b2f2976 | 4 | //! compilation in build scripts that Cargo has. This is because the |
5bcae85e SL |
5 | //! compilation takes a *very* long time but also because we don't want to |
6 | //! compile LLVM 3 times as part of a normal bootstrap (we want it cached). | |
7 | //! | |
8 | //! LLVM and compiler-rt are essentially just wired up to everything else to | |
9 | //! ensure that they're always in place if needed. | |
10 | ||
cc61c64b | 11 | use std::env; |
f035d41b | 12 | use std::env::consts::EXE_EXTENSION; |
94222f64 | 13 | use std::ffi::{OsStr, OsString}; |
c30ab7b3 | 14 | use std::fs::{self, File}; |
923072b8 | 15 | use std::io; |
2c00a5a8 | 16 | use std::path::{Path, PathBuf}; |
923072b8 | 17 | use std::process::Command; |
5bcae85e | 18 | |
0731742a | 19 | use crate::builder::{Builder, RunConfig, ShouldRun, Step}; |
3dfed10e | 20 | use crate::config::TargetSelection; |
923072b8 | 21 | use crate::util::{self, exe, output, program_out_of_date, t, up_to_date}; |
5099ac24 | 22 | use crate::{CLang, GitRepo}; |
5bcae85e | 23 | |
f9f354fc XL |
24 | pub struct Meta { |
25 | stamp: HashStamp, | |
26 | build_llvm_config: PathBuf, | |
27 | out_dir: PathBuf, | |
28 | root: String, | |
29 | } | |
30 | ||
5099ac24 FG |
31 | // Linker flags to pass to LLVM's CMake invocation. |
32 | #[derive(Debug, Clone, Default)] | |
33 | struct LdFlags { | |
34 | // CMAKE_EXE_LINKER_FLAGS | |
35 | exe: OsString, | |
36 | // CMAKE_SHARED_LINKER_FLAGS | |
37 | shared: OsString, | |
38 | // CMAKE_MODULE_LINKER_FLAGS | |
39 | module: OsString, | |
40 | } | |
41 | ||
42 | impl LdFlags { | |
43 | fn push_all(&mut self, s: impl AsRef<OsStr>) { | |
44 | let s = s.as_ref(); | |
45 | self.exe.push(" "); | |
46 | self.exe.push(s); | |
47 | self.shared.push(" "); | |
48 | self.shared.push(s); | |
49 | self.module.push(" "); | |
50 | self.module.push(s); | |
51 | } | |
52 | } | |
53 | ||
f9f354fc XL |
54 | // This returns whether we've already previously built LLVM. |
55 | // | |
56 | // It's used to avoid busting caches during x.py check -- if we've already built | |
57 | // LLVM, it's fine for us to not try to avoid doing so. | |
58 | // | |
59 | // This will return the llvm-config if it can get it (but it will not build it | |
60 | // if not). | |
61 | pub fn prebuilt_llvm_config( | |
62 | builder: &Builder<'_>, | |
3dfed10e | 63 | target: TargetSelection, |
f9f354fc | 64 | ) -> Result<PathBuf, Meta> { |
04454e1e FG |
65 | maybe_download_ci_llvm(builder); |
66 | ||
f9f354fc XL |
67 | // If we're using a custom LLVM bail out here, but we can only use a |
68 | // custom LLVM for the build triple. | |
69 | if let Some(config) = builder.config.target_config.get(&target) { | |
70 | if let Some(ref s) = config.llvm_config { | |
71 | check_llvm_version(builder, s); | |
72 | return Ok(s.to_path_buf()); | |
73 | } | |
74 | } | |
75 | ||
76 | let root = "src/llvm-project/llvm"; | |
77 | let out_dir = builder.llvm_out(target); | |
3dfed10e | 78 | |
f9f354fc | 79 | let mut llvm_config_ret_dir = builder.llvm_out(builder.config.build); |
1b1a35ee | 80 | if !builder.config.build.contains("msvc") || builder.ninja() { |
f9f354fc XL |
81 | llvm_config_ret_dir.push("build"); |
82 | } | |
83 | llvm_config_ret_dir.push("bin"); | |
84 | ||
3dfed10e | 85 | let build_llvm_config = llvm_config_ret_dir.join(exe("llvm-config", builder.config.build)); |
f9f354fc XL |
86 | |
87 | let stamp = out_dir.join("llvm-finished-building"); | |
88 | let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha()); | |
89 | ||
90 | if builder.config.llvm_skip_rebuild && stamp.path.exists() { | |
91 | builder.info( | |
92 | "Warning: \ | |
93 | Using a potentially stale build of LLVM; \ | |
94 | This may not behave well.", | |
95 | ); | |
96 | return Ok(build_llvm_config); | |
97 | } | |
98 | ||
99 | if stamp.is_done() { | |
100 | if stamp.hash.is_none() { | |
101 | builder.info( | |
102 | "Could not determine the LLVM submodule commit hash. \ | |
103 | Assuming that an LLVM rebuild is not necessary.", | |
104 | ); | |
105 | builder.info(&format!( | |
106 | "To force LLVM to rebuild, remove the file `{}`", | |
107 | stamp.path.display() | |
108 | )); | |
109 | } | |
110 | return Ok(build_llvm_config); | |
111 | } | |
112 | ||
113 | Err(Meta { stamp, build_llvm_config, out_dir, root: root.into() }) | |
114 | } | |
115 | ||
04454e1e FG |
116 | pub(crate) fn maybe_download_ci_llvm(builder: &Builder<'_>) { |
117 | let config = &builder.config; | |
118 | if !config.llvm_from_ci { | |
119 | return; | |
120 | } | |
121 | let mut rev_list = Command::new("git"); | |
122 | rev_list.args(&[ | |
123 | PathBuf::from("rev-list"), | |
923072b8 | 124 | format!("--author={}", builder.config.stage0_metadata.config.git_merge_commit_email).into(), |
04454e1e FG |
125 | "-n1".into(), |
126 | "--first-parent".into(), | |
127 | "HEAD".into(), | |
128 | "--".into(), | |
129 | builder.src.join("src/llvm-project"), | |
130 | builder.src.join("src/bootstrap/download-ci-llvm-stamp"), | |
131 | // the LLVM shared object file is named `LLVM-12-rust-{version}-nightly` | |
132 | builder.src.join("src/version"), | |
133 | ]); | |
134 | let llvm_sha = output(&mut rev_list); | |
135 | let llvm_sha = llvm_sha.trim(); | |
136 | ||
137 | if llvm_sha == "" { | |
923072b8 FG |
138 | eprintln!("error: could not find commit hash for downloading LLVM"); |
139 | eprintln!("help: maybe your repository history is too shallow?"); | |
140 | eprintln!("help: consider disabling `download-ci-llvm`"); | |
141 | eprintln!("help: or fetch enough history to include one upstream commit"); | |
04454e1e FG |
142 | panic!(); |
143 | } | |
144 | ||
145 | let llvm_root = config.ci_llvm_root(); | |
146 | let llvm_stamp = llvm_root.join(".llvm-stamp"); | |
147 | let key = format!("{}{}", llvm_sha, config.llvm_assertions); | |
148 | if program_out_of_date(&llvm_stamp, &key) && !config.dry_run { | |
149 | download_ci_llvm(builder, &llvm_sha); | |
150 | for binary in ["llvm-config", "FileCheck"] { | |
923072b8 | 151 | builder.fix_bin_or_dylib(&llvm_root.join("bin").join(binary)); |
04454e1e FG |
152 | } |
153 | let llvm_lib = llvm_root.join("lib"); | |
154 | for entry in t!(fs::read_dir(&llvm_lib)) { | |
155 | let lib = t!(entry).path(); | |
923072b8 FG |
156 | if lib.extension().map_or(false, |ext| ext == "so") { |
157 | builder.fix_bin_or_dylib(&lib); | |
04454e1e FG |
158 | } |
159 | } | |
160 | t!(fs::write(llvm_stamp, key)); | |
161 | } | |
162 | } | |
163 | ||
164 | fn download_ci_llvm(builder: &Builder<'_>, llvm_sha: &str) { | |
165 | let llvm_assertions = builder.config.llvm_assertions; | |
166 | ||
167 | let cache_prefix = format!("llvm-{}-{}", llvm_sha, llvm_assertions); | |
168 | let cache_dst = builder.out.join("cache"); | |
169 | let rustc_cache = cache_dst.join(cache_prefix); | |
170 | if !rustc_cache.exists() { | |
171 | t!(fs::create_dir_all(&rustc_cache)); | |
172 | } | |
923072b8 FG |
173 | let base = if llvm_assertions { |
174 | &builder.config.stage0_metadata.config.artifacts_with_llvm_assertions_server | |
04454e1e | 175 | } else { |
923072b8 | 176 | &builder.config.stage0_metadata.config.artifacts_server |
04454e1e FG |
177 | }; |
178 | let filename = format!("rust-dev-nightly-{}.tar.xz", builder.build.build.triple); | |
179 | let tarball = rustc_cache.join(&filename); | |
180 | if !tarball.exists() { | |
923072b8 FG |
181 | let help_on_error = "error: failed to download llvm from ci |
182 | ||
183 | help: old builds get deleted after a certain time | |
184 | help: if trying to compile an old commit of rustc, disable `download-ci-llvm` in config.toml: | |
185 | ||
186 | [llvm] | |
187 | download-ci-llvm = false | |
188 | "; | |
189 | builder.download_component( | |
190 | &format!("{base}/{llvm_sha}/{filename}"), | |
191 | &tarball, | |
192 | help_on_error, | |
193 | ); | |
04454e1e FG |
194 | } |
195 | let llvm_root = builder.config.ci_llvm_root(); | |
923072b8 | 196 | builder.unpack(&tarball, &llvm_root, "rust-dev"); |
04454e1e FG |
197 | } |
198 | ||
3b2f2976 XL |
199 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] |
200 | pub struct Llvm { | |
3dfed10e | 201 | pub target: TargetSelection, |
3b2f2976 XL |
202 | } |
203 | ||
204 | impl Step for Llvm { | |
2c00a5a8 XL |
205 | type Output = PathBuf; // path to llvm-config |
206 | ||
3b2f2976 XL |
207 | const ONLY_HOSTS: bool = true; |
208 | ||
9fa01778 | 209 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
04454e1e | 210 | run.path("src/llvm-project").path("src/llvm-project/llvm") |
5bcae85e SL |
211 | } |
212 | ||
9fa01778 | 213 | fn make_run(run: RunConfig<'_>) { |
dfeec247 | 214 | run.builder.ensure(Llvm { target: run.target }); |
3b2f2976 | 215 | } |
8bb4bdeb | 216 | |
3b2f2976 | 217 | /// Compile LLVM for `target`. |
9fa01778 | 218 | fn run(self, builder: &Builder<'_>) -> PathBuf { |
3b2f2976 | 219 | let target = self.target; |
3dfed10e XL |
220 | let target_native = if self.target.starts_with("riscv") { |
221 | // RISC-V target triples in Rust is not named the same as C compiler target triples. | |
222 | // This converts Rust RISC-V target triples to C compiler triples. | |
223 | let idx = target.triple.find('-').unwrap(); | |
224 | ||
225 | format!("riscv{}{}", &target.triple[5..7], &target.triple[idx..]) | |
94222f64 XL |
226 | } else if self.target.starts_with("powerpc") && self.target.ends_with("freebsd") { |
227 | // FreeBSD 13 had incompatible ABI changes on all PowerPC platforms. | |
228 | // Set the version suffix to 13.0 so the correct target details are used. | |
229 | format!("{}{}", self.target, "13.0") | |
3dfed10e XL |
230 | } else { |
231 | target.to_string() | |
232 | }; | |
3b2f2976 | 233 | |
f9f354fc XL |
234 | let Meta { stamp, build_llvm_config, out_dir, root } = |
235 | match prebuilt_llvm_config(builder, target) { | |
236 | Ok(p) => return p, | |
237 | Err(m) => m, | |
238 | }; | |
8bb4bdeb | 239 | |
136023e0 | 240 | builder.update_submodule(&Path::new("src").join("llvm-project")); |
04454e1e | 241 | if builder.llvm_link_shared() |
1b1a35ee XL |
242 | && (target.contains("windows") || target.contains("apple-darwin")) |
243 | { | |
244 | panic!("shared linking to LLVM is not currently supported on {}", target.triple); | |
245 | } | |
246 | ||
e74abb32 | 247 | builder.info(&format!("Building LLVM for {}", target)); |
ba9703b0 | 248 | t!(stamp.remove()); |
83c7162d | 249 | let _time = util::timeit(&builder); |
3b2f2976 | 250 | t!(fs::create_dir_all(&out_dir)); |
5bcae85e | 251 | |
136023e0 | 252 | // https://llvm.org/docs/CMake.html |
83c7162d | 253 | let mut cfg = cmake::Config::new(builder.src.join(root)); |
5099ac24 | 254 | let mut ldflags = LdFlags::default(); |
8bb4bdeb | 255 | |
83c7162d | 256 | let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) { |
3b2f2976 XL |
257 | (false, _) => "Debug", |
258 | (true, false) => "Release", | |
259 | (true, true) => "RelWithDebInfo", | |
260 | }; | |
261 | ||
2c00a5a8 XL |
262 | // NOTE: remember to also update `config.toml.example` when changing the |
263 | // defaults! | |
e74abb32 XL |
264 | let llvm_targets = match &builder.config.llvm_targets { |
265 | Some(s) => s, | |
dfeec247 | 266 | None => { |
17df50a5 | 267 | "AArch64;ARM;BPF;Hexagon;MSP430;Mips;NVPTX;PowerPC;RISCV;\ |
dfeec247 XL |
268 | Sparc;SystemZ;WebAssembly;X86" |
269 | } | |
3b2f2976 XL |
270 | }; |
271 | ||
e74abb32 XL |
272 | let llvm_exp_targets = match builder.config.llvm_experimental_targets { |
273 | Some(ref s) => s, | |
c295e0f8 | 274 | None => "AVR;M68k", |
2c00a5a8 | 275 | }; |
3b2f2976 | 276 | |
dfeec247 | 277 | let assertions = if builder.config.llvm_assertions { "ON" } else { "OFF" }; |
94222f64 | 278 | let plugins = if builder.config.llvm_plugins { "ON" } else { "OFF" }; |
3c0e092e | 279 | let enable_tests = if builder.config.llvm_tests { "ON" } else { "OFF" }; |
3b2f2976 | 280 | |
0531ce1d | 281 | cfg.out_dir(&out_dir) |
dfeec247 XL |
282 | .profile(profile) |
283 | .define("LLVM_ENABLE_ASSERTIONS", assertions) | |
94222f64 | 284 | .define("LLVM_ENABLE_PLUGINS", plugins) |
dfeec247 XL |
285 | .define("LLVM_TARGETS_TO_BUILD", llvm_targets) |
286 | .define("LLVM_EXPERIMENTAL_TARGETS_TO_BUILD", llvm_exp_targets) | |
287 | .define("LLVM_INCLUDE_EXAMPLES", "OFF") | |
dfeec247 XL |
288 | .define("LLVM_INCLUDE_DOCS", "OFF") |
289 | .define("LLVM_INCLUDE_BENCHMARKS", "OFF") | |
3c0e092e | 290 | .define("LLVM_INCLUDE_TESTS", enable_tests) |
dfeec247 XL |
291 | .define("LLVM_ENABLE_TERMINFO", "OFF") |
292 | .define("LLVM_ENABLE_LIBEDIT", "OFF") | |
293 | .define("LLVM_ENABLE_BINDINGS", "OFF") | |
294 | .define("LLVM_ENABLE_Z3_SOLVER", "OFF") | |
295 | .define("LLVM_PARALLEL_COMPILE_JOBS", builder.jobs().to_string()) | |
3dfed10e XL |
296 | .define("LLVM_TARGET_ARCH", target_native.split('-').next().unwrap()) |
297 | .define("LLVM_DEFAULT_TARGET_TRIPLE", target_native); | |
3b2f2976 | 298 | |
c295e0f8 XL |
299 | // Parts of our test suite rely on the `FileCheck` tool, which is built by default in |
300 | // `build/$TARGET/llvm/build/bin` is but *not* then installed to `build/$TARGET/llvm/bin`. | |
301 | // This flag makes sure `FileCheck` is copied in the final binaries directory. | |
302 | cfg.define("LLVM_INSTALL_UTILS", "ON"); | |
303 | ||
94222f64 XL |
304 | if builder.config.llvm_profile_generate { |
305 | cfg.define("LLVM_BUILD_INSTRUMENTED", "IR"); | |
923072b8 FG |
306 | if let Ok(llvm_profile_dir) = std::env::var("LLVM_PROFILE_DIR") { |
307 | cfg.define("LLVM_PROFILE_DATA_DIR", llvm_profile_dir); | |
308 | } | |
94222f64 XL |
309 | cfg.define("LLVM_BUILD_RUNTIME", "No"); |
310 | } | |
311 | if let Some(path) = builder.config.llvm_profile_use.as_ref() { | |
312 | cfg.define("LLVM_PROFDATA_FILE", &path); | |
313 | } | |
314 | ||
17df50a5 | 315 | if target != "aarch64-apple-darwin" && !target.contains("windows") { |
f035d41b XL |
316 | cfg.define("LLVM_ENABLE_ZLIB", "ON"); |
317 | } else { | |
f035d41b XL |
318 | cfg.define("LLVM_ENABLE_ZLIB", "OFF"); |
319 | } | |
320 | ||
923072b8 FG |
321 | // Are we compiling for iOS/tvOS/watchOS? |
322 | if target.contains("apple-ios") | |
323 | || target.contains("apple-tvos") | |
324 | || target.contains("apple-watchos") | |
325 | { | |
f035d41b XL |
326 | // These two defines prevent CMake from automatically trying to add a MacOSX sysroot, which leads to a compiler error. |
327 | cfg.define("CMAKE_OSX_SYSROOT", "/"); | |
328 | cfg.define("CMAKE_OSX_DEPLOYMENT_TARGET", ""); | |
329 | // Prevent cmake from adding -bundle to CFLAGS automatically, which leads to a compiler error because "-bitcode_bundle" also gets added. | |
330 | cfg.define("LLVM_ENABLE_PLUGINS", "OFF"); | |
331 | // Zlib fails to link properly, leading to a compiler error. | |
332 | cfg.define("LLVM_ENABLE_ZLIB", "OFF"); | |
333 | } | |
334 | ||
e74abb32 | 335 | if builder.config.llvm_thin_lto { |
48663c56 XL |
336 | cfg.define("LLVM_ENABLE_LTO", "Thin"); |
337 | if !target.contains("apple") { | |
dfeec247 | 338 | cfg.define("LLVM_ENABLE_LLD", "ON"); |
48663c56 | 339 | } |
b7449926 XL |
340 | } |
341 | ||
3b2f2976 XL |
342 | // This setting makes the LLVM tools link to the dynamic LLVM library, |
343 | // which saves both memory during parallel links and overall disk space | |
b7449926 XL |
344 | // for the tools. We don't do this on every platform as it doesn't work |
345 | // equally well everywhere. | |
1b1a35ee XL |
346 | // |
347 | // If we're not linking rustc to a dynamic LLVM, though, then don't link | |
348 | // tools to it. | |
04454e1e | 349 | if builder.llvm_link_tools_dynamically(target) && builder.llvm_link_shared() { |
b7449926 | 350 | cfg.define("LLVM_LINK_LLVM_DYLIB", "ON"); |
8faf50e0 XL |
351 | } |
352 | ||
5099ac24 | 353 | if target.starts_with("riscv") && !target.contains("freebsd") { |
a2a8927a XL |
354 | // RISC-V GCC erroneously requires linking against |
355 | // `libatomic` when using 1-byte and 2-byte C++ | |
356 | // atomics but the LLVM build system check cannot | |
357 | // detect this. Therefore it is set manually here. | |
358 | // FreeBSD uses Clang as its system compiler and | |
359 | // provides no libatomic in its base system so does | |
360 | // not want this. | |
5099ac24 FG |
361 | ldflags.exe.push(" -latomic"); |
362 | ldflags.shared.push(" -latomic"); | |
3dfed10e XL |
363 | } |
364 | ||
7cac9316 | 365 | if target.contains("msvc") { |
3b2f2976 XL |
366 | cfg.define("LLVM_USE_CRT_DEBUG", "MT"); |
367 | cfg.define("LLVM_USE_CRT_RELEASE", "MT"); | |
368 | cfg.define("LLVM_USE_CRT_RELWITHDEBINFO", "MT"); | |
369 | cfg.static_crt(true); | |
7cac9316 | 370 | } |
7cac9316 | 371 | |
3b2f2976 XL |
372 | if target.starts_with("i686") { |
373 | cfg.define("LLVM_BUILD_32_BITS", "ON"); | |
7cac9316 XL |
374 | } |
375 | ||
dc9dc135 XL |
376 | let mut enabled_llvm_projects = Vec::new(); |
377 | ||
378 | if util::forcing_clang_based_tests() { | |
379 | enabled_llvm_projects.push("clang"); | |
380 | enabled_llvm_projects.push("compiler-rt"); | |
381 | } | |
382 | ||
cdc7bbd5 | 383 | if builder.config.llvm_polly { |
29967ef6 XL |
384 | enabled_llvm_projects.push("polly"); |
385 | } | |
386 | ||
94222f64 XL |
387 | if builder.config.llvm_clang { |
388 | enabled_llvm_projects.push("clang"); | |
389 | } | |
390 | ||
f9f354fc XL |
391 | // We want libxml to be disabled. |
392 | // See https://github.com/rust-lang/rust/pull/50104 | |
393 | cfg.define("LLVM_ENABLE_LIBXML2", "OFF"); | |
b7449926 | 394 | |
74b04a01 | 395 | if !enabled_llvm_projects.is_empty() { |
dc9dc135 XL |
396 | enabled_llvm_projects.sort(); |
397 | enabled_llvm_projects.dedup(); | |
398 | cfg.define("LLVM_ENABLE_PROJECTS", enabled_llvm_projects.join(";")); | |
399 | } | |
400 | ||
83c7162d | 401 | if let Some(num_linkers) = builder.config.llvm_link_jobs { |
3b2f2976 XL |
402 | if num_linkers > 0 { |
403 | cfg.define("LLVM_PARALLEL_LINK_JOBS", num_linkers.to_string()); | |
404 | } | |
5bcae85e | 405 | } |
5bcae85e | 406 | |
94222f64 XL |
407 | // Workaround for ppc32 lld limitation |
408 | if target == "powerpc-unknown-freebsd" { | |
5099ac24 | 409 | ldflags.exe.push(" -fuse-ld=bfd"); |
94222f64 XL |
410 | } |
411 | ||
136023e0 | 412 | // https://llvm.org/docs/HowToCrossCompileLLVM.html |
e74abb32 | 413 | if target != builder.config.build { |
dfeec247 | 414 | builder.ensure(Llvm { target: builder.config.build }); |
3b2f2976 XL |
415 | // FIXME: if the llvm root for the build triple is overridden then we |
416 | // should use llvm-tblgen from there, also should verify that it | |
417 | // actually exists most of the time in normal installs of LLVM. | |
f035d41b XL |
418 | let host_bin = builder.llvm_out(builder.config.build).join("bin"); |
419 | cfg.define("CMAKE_CROSSCOMPILING", "True"); | |
420 | cfg.define("LLVM_TABLEGEN", host_bin.join("llvm-tblgen").with_extension(EXE_EXTENSION)); | |
421 | cfg.define("LLVM_NM", host_bin.join("llvm-nm").with_extension(EXE_EXTENSION)); | |
422 | cfg.define( | |
423 | "LLVM_CONFIG_PATH", | |
424 | host_bin.join("llvm-config").with_extension(EXE_EXTENSION), | |
425 | ); | |
3b2f2976 | 426 | } |
5bcae85e | 427 | |
b7449926 | 428 | if let Some(ref suffix) = builder.config.llvm_version_suffix { |
9fa01778 XL |
429 | // Allow version-suffix="" to not define a version suffix at all. |
430 | if !suffix.is_empty() { | |
431 | cfg.define("LLVM_VERSION_SUFFIX", suffix); | |
432 | } | |
ba9703b0 XL |
433 | } else if builder.config.channel == "dev" { |
434 | // Changes to a version suffix require a complete rebuild of the LLVM. | |
435 | // To avoid rebuilds during a time of version bump, don't include rustc | |
436 | // release number on the dev channel. | |
437 | cfg.define("LLVM_VERSION_SUFFIX", "-rust-dev"); | |
9fa01778 | 438 | } else { |
1b1a35ee | 439 | let suffix = format!("-rust-{}-{}", builder.version, builder.config.channel); |
ba9703b0 | 440 | cfg.define("LLVM_VERSION_SUFFIX", suffix); |
9fa01778 XL |
441 | } |
442 | ||
443 | if let Some(ref linker) = builder.config.llvm_use_linker { | |
444 | cfg.define("LLVM_USE_LINKER", linker); | |
b7449926 XL |
445 | } |
446 | ||
cdc7bbd5 | 447 | if builder.config.llvm_allow_old_toolchain { |
532ac7d7 XL |
448 | cfg.define("LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN", "YES"); |
449 | } | |
450 | ||
5099ac24 FG |
451 | configure_cmake(builder, target, &mut cfg, true, ldflags); |
452 | ||
453 | for (key, val) in &builder.config.llvm_build_config { | |
454 | cfg.define(key, val); | |
455 | } | |
3b2f2976 XL |
456 | |
457 | // FIXME: we don't actually need to build all LLVM tools and all LLVM | |
0731742a | 458 | // libraries here, e.g., we just want a few components and a few |
3b2f2976 XL |
459 | // tools. Figure out how to filter them down and only build the right |
460 | // tools and libs on all platforms. | |
83c7162d XL |
461 | |
462 | if builder.config.dry_run { | |
463 | return build_llvm_config; | |
464 | } | |
465 | ||
3b2f2976 XL |
466 | cfg.build(); |
467 | ||
ba9703b0 | 468 | t!(stamp.write()); |
2c00a5a8 XL |
469 | |
470 | build_llvm_config | |
3b2f2976 | 471 | } |
5bcae85e SL |
472 | } |
473 | ||
9fa01778 | 474 | fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) { |
83c7162d | 475 | if !builder.config.llvm_version_check { |
dfeec247 | 476 | return; |
5bcae85e SL |
477 | } |
478 | ||
83c7162d XL |
479 | if builder.config.dry_run { |
480 | return; | |
481 | } | |
482 | ||
5bcae85e SL |
483 | let mut cmd = Command::new(llvm_config); |
484 | let version = output(cmd.arg("--version")); | |
dfeec247 | 485 | let mut parts = version.split('.').take(2).filter_map(|s| s.parse::<u32>().ok()); |
8faf50e0 | 486 | if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) { |
3c0e092e | 487 | if major >= 12 { |
dfeec247 | 488 | return; |
abe05a73 | 489 | } |
5bcae85e | 490 | } |
3c0e092e | 491 | panic!("\n\nbad LLVM version: {}, need >=12.0\n\n", version) |
5bcae85e SL |
492 | } |
493 | ||
dfeec247 XL |
494 | fn configure_cmake( |
495 | builder: &Builder<'_>, | |
3dfed10e | 496 | target: TargetSelection, |
dfeec247 XL |
497 | cfg: &mut cmake::Config, |
498 | use_compiler_launcher: bool, | |
5099ac24 | 499 | mut ldflags: LdFlags, |
dfeec247 | 500 | ) { |
48663c56 XL |
501 | // Do not print installation messages for up-to-date files. |
502 | // LLVM and LLD builds can produce a lot of those and hit CI limits on log size. | |
503 | cfg.define("CMAKE_INSTALL_MESSAGE", "LAZY"); | |
504 | ||
3dfed10e XL |
505 | // Do not allow the user's value of DESTDIR to influence where |
506 | // LLVM will install itself. LLVM must always be installed in our | |
507 | // own build directories. | |
508 | cfg.env("DESTDIR", ""); | |
509 | ||
1b1a35ee | 510 | if builder.ninja() { |
0531ce1d XL |
511 | cfg.generator("Ninja"); |
512 | } | |
3dfed10e XL |
513 | cfg.target(&target.triple).host(&builder.config.build.triple); |
514 | ||
515 | if target != builder.config.build { | |
516 | if target.contains("netbsd") { | |
517 | cfg.define("CMAKE_SYSTEM_NAME", "NetBSD"); | |
518 | } else if target.contains("freebsd") { | |
519 | cfg.define("CMAKE_SYSTEM_NAME", "FreeBSD"); | |
520 | } else if target.contains("windows") { | |
521 | cfg.define("CMAKE_SYSTEM_NAME", "Windows"); | |
29967ef6 XL |
522 | } else if target.contains("haiku") { |
523 | cfg.define("CMAKE_SYSTEM_NAME", "Haiku"); | |
6a06907d XL |
524 | } else if target.contains("solaris") || target.contains("illumos") { |
525 | cfg.define("CMAKE_SYSTEM_NAME", "SunOS"); | |
3dfed10e XL |
526 | } |
527 | // When cross-compiling we should also set CMAKE_SYSTEM_VERSION, but in | |
528 | // that case like CMake we cannot easily determine system version either. | |
529 | // | |
530 | // Since, the LLVM itself makes rather limited use of version checks in | |
531 | // CMakeFiles (and then only in tests), and so far no issues have been | |
532 | // reported, the system version is currently left unset. | |
533 | } | |
0531ce1d XL |
534 | |
535 | let sanitize_cc = |cc: &Path| { | |
536 | if target.contains("msvc") { | |
537 | OsString::from(cc.to_str().unwrap().replace("\\", "/")) | |
538 | } else { | |
539 | cc.as_os_str().to_owned() | |
540 | } | |
541 | }; | |
542 | ||
543 | // MSVC with CMake uses msbuild by default which doesn't respect these | |
544 | // vars that we'd otherwise configure. In that case we just skip this | |
545 | // entirely. | |
1b1a35ee | 546 | if target.contains("msvc") && !builder.ninja() { |
dfeec247 | 547 | return; |
0531ce1d XL |
548 | } |
549 | ||
94b46f34 XL |
550 | let (cc, cxx) = match builder.config.llvm_clang_cl { |
551 | Some(ref cl) => (cl.as_ref(), cl.as_ref()), | |
552 | None => (builder.cc(target), builder.cxx(target).unwrap()), | |
553 | }; | |
0531ce1d XL |
554 | |
555 | // Handle msvc + ninja + ccache specially (this is what the bots use) | |
1b1a35ee | 556 | if target.contains("msvc") && builder.ninja() && builder.config.ccache.is_some() { |
dfeec247 XL |
557 | let mut wrap_cc = env::current_exe().expect("failed to get cwd"); |
558 | wrap_cc.set_file_name("sccache-plus-cl.exe"); | |
559 | ||
560 | cfg.define("CMAKE_C_COMPILER", sanitize_cc(&wrap_cc)) | |
561 | .define("CMAKE_CXX_COMPILER", sanitize_cc(&wrap_cc)); | |
562 | cfg.env("SCCACHE_PATH", builder.config.ccache.as_ref().unwrap()) | |
3dfed10e | 563 | .env("SCCACHE_TARGET", target.triple) |
dfeec247 XL |
564 | .env("SCCACHE_CC", &cc) |
565 | .env("SCCACHE_CXX", &cxx); | |
566 | ||
567 | // Building LLVM on MSVC can be a little ludicrous at times. We're so far | |
568 | // off the beaten path here that I'm not really sure this is even half | |
569 | // supported any more. Here we're trying to: | |
570 | // | |
571 | // * Build LLVM on MSVC | |
572 | // * Build LLVM with `clang-cl` instead of `cl.exe` | |
573 | // * Build a project with `sccache` | |
574 | // * Build for 32-bit as well | |
575 | // * Build with Ninja | |
576 | // | |
577 | // For `cl.exe` there are different binaries to compile 32/64 bit which | |
578 | // we use but for `clang-cl` there's only one which internally | |
579 | // multiplexes via flags. As a result it appears that CMake's detection | |
580 | // of a compiler's architecture and such on MSVC **doesn't** pass any | |
581 | // custom flags we pass in CMAKE_CXX_FLAGS below. This means that if we | |
582 | // use `clang-cl.exe` it's always diagnosed as a 64-bit compiler which | |
583 | // definitely causes problems since all the env vars are pointing to | |
584 | // 32-bit libraries. | |
585 | // | |
586 | // To hack around this... again... we pass an argument that's | |
587 | // unconditionally passed in the sccache shim. This'll get CMake to | |
588 | // correctly diagnose it's doing a 32-bit compilation and LLVM will | |
589 | // internally configure itself appropriately. | |
590 | if builder.config.llvm_clang_cl.is_some() && target.contains("i686") { | |
591 | cfg.env("SCCACHE_EXTRA_ARGS", "-m32"); | |
592 | } | |
0531ce1d | 593 | } else { |
dfeec247 XL |
594 | // If ccache is configured we inform the build a little differently how |
595 | // to invoke ccache while also invoking our compilers. | |
596 | if use_compiler_launcher { | |
597 | if let Some(ref ccache) = builder.config.ccache { | |
598 | cfg.define("CMAKE_C_COMPILER_LAUNCHER", ccache) | |
599 | .define("CMAKE_CXX_COMPILER_LAUNCHER", ccache); | |
600 | } | |
601 | } | |
602 | cfg.define("CMAKE_C_COMPILER", sanitize_cc(cc)) | |
3dfed10e XL |
603 | .define("CMAKE_CXX_COMPILER", sanitize_cc(cxx)) |
604 | .define("CMAKE_ASM_COMPILER", sanitize_cc(cc)); | |
0531ce1d XL |
605 | } |
606 | ||
83c7162d | 607 | cfg.build_arg("-j").build_arg(builder.jobs().to_string()); |
5099ac24 | 608 | let mut cflags: OsString = builder.cflags(target, GitRepo::Llvm, CLang::C).join(" ").into(); |
dc9dc135 | 609 | if let Some(ref s) = builder.config.llvm_cflags { |
5099ac24 FG |
610 | cflags.push(" "); |
611 | cflags.push(s); | |
9fa01778 | 612 | } |
f035d41b XL |
613 | // Some compiler features used by LLVM (such as thread locals) will not work on a min version below iOS 10. |
614 | if target.contains("apple-ios") { | |
615 | if target.contains("86-") { | |
5099ac24 | 616 | cflags.push(" -miphonesimulator-version-min=10.0"); |
f035d41b | 617 | } else { |
5099ac24 | 618 | cflags.push(" -miphoneos-version-min=10.0"); |
f035d41b XL |
619 | } |
620 | } | |
621 | if builder.config.llvm_clang_cl.is_some() { | |
5099ac24 | 622 | cflags.push(&format!(" --target={}", target)); |
f035d41b | 623 | } |
9fa01778 | 624 | cfg.define("CMAKE_C_FLAGS", cflags); |
5099ac24 | 625 | let mut cxxflags: OsString = builder.cflags(target, GitRepo::Llvm, CLang::Cxx).join(" ").into(); |
9fa01778 | 626 | if let Some(ref s) = builder.config.llvm_cxxflags { |
5099ac24 FG |
627 | cxxflags.push(" "); |
628 | cxxflags.push(s); | |
9fa01778 | 629 | } |
f035d41b | 630 | if builder.config.llvm_clang_cl.is_some() { |
5099ac24 | 631 | cxxflags.push(&format!(" --target={}", target)); |
f035d41b | 632 | } |
0531ce1d | 633 | cfg.define("CMAKE_CXX_FLAGS", cxxflags); |
83c7162d | 634 | if let Some(ar) = builder.ar(target) { |
0531ce1d XL |
635 | if ar.is_absolute() { |
636 | // LLVM build breaks if `CMAKE_AR` is a relative path, for some reason it | |
637 | // tries to resolve this path in the LLVM build directory. | |
638 | cfg.define("CMAKE_AR", sanitize_cc(ar)); | |
639 | } | |
640 | } | |
641 | ||
b7449926 XL |
642 | if let Some(ranlib) = builder.ranlib(target) { |
643 | if ranlib.is_absolute() { | |
644 | // LLVM build breaks if `CMAKE_RANLIB` is a relative path, for some reason it | |
645 | // tries to resolve this path in the LLVM build directory. | |
646 | cfg.define("CMAKE_RANLIB", sanitize_cc(ranlib)); | |
647 | } | |
648 | } | |
649 | ||
5099ac24 FG |
650 | if let Some(ref flags) = builder.config.llvm_ldflags { |
651 | ldflags.push_all(flags); | |
652 | } | |
653 | ||
654 | if let Some(flags) = get_var("LDFLAGS", &builder.config.build.triple, &target.triple) { | |
655 | ldflags.push_all(&flags); | |
9fa01778 XL |
656 | } |
657 | ||
5099ac24 FG |
658 | // For distribution we want the LLVM tools to be *statically* linked to libstdc++. |
659 | // We also do this if the user explicitly requested static libstdc++. | |
5e7ed085 | 660 | if builder.config.llvm_static_stdcpp { |
923072b8 | 661 | if !target.contains("msvc") && !target.contains("netbsd") && !target.contains("solaris") { |
5e7ed085 | 662 | if target.contains("apple") || target.contains("windows") { |
5099ac24 FG |
663 | ldflags.push_all("-static-libstdc++"); |
664 | } else { | |
665 | ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++"); | |
666 | } | |
667 | } | |
668 | } | |
669 | ||
670 | cfg.define("CMAKE_SHARED_LINKER_FLAGS", &ldflags.shared); | |
671 | cfg.define("CMAKE_MODULE_LINKER_FLAGS", &ldflags.module); | |
672 | cfg.define("CMAKE_EXE_LINKER_FLAGS", &ldflags.exe); | |
673 | ||
0531ce1d | 674 | if env::var_os("SCCACHE_ERROR_LOG").is_some() { |
48663c56 | 675 | cfg.env("RUSTC_LOG", "sccache=warn"); |
0531ce1d XL |
676 | } |
677 | } | |
678 | ||
5099ac24 FG |
679 | // Adapted from https://github.com/alexcrichton/cc-rs/blob/fba7feded71ee4f63cfe885673ead6d7b4f2f454/src/lib.rs#L2347-L2365 |
680 | fn get_var(var_base: &str, host: &str, target: &str) -> Option<OsString> { | |
681 | let kind = if host == target { "HOST" } else { "TARGET" }; | |
682 | let target_u = target.replace("-", "_"); | |
683 | env::var_os(&format!("{}_{}", var_base, target)) | |
684 | .or_else(|| env::var_os(&format!("{}_{}", var_base, target_u))) | |
685 | .or_else(|| env::var_os(&format!("{}_{}", kind, var_base))) | |
686 | .or_else(|| env::var_os(var_base)) | |
687 | } | |
688 | ||
0531ce1d XL |
689 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] |
690 | pub struct Lld { | |
3dfed10e | 691 | pub target: TargetSelection, |
0531ce1d XL |
692 | } |
693 | ||
694 | impl Step for Lld { | |
695 | type Output = PathBuf; | |
696 | const ONLY_HOSTS: bool = true; | |
697 | ||
9fa01778 | 698 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
04454e1e | 699 | run.path("src/llvm-project/lld") |
0531ce1d XL |
700 | } |
701 | ||
9fa01778 | 702 | fn make_run(run: RunConfig<'_>) { |
0531ce1d XL |
703 | run.builder.ensure(Lld { target: run.target }); |
704 | } | |
705 | ||
f035d41b | 706 | /// Compile LLD for `target`. |
9fa01778 | 707 | fn run(self, builder: &Builder<'_>) -> PathBuf { |
83c7162d XL |
708 | if builder.config.dry_run { |
709 | return PathBuf::from("lld-out-dir-test-gen"); | |
710 | } | |
0531ce1d | 711 | let target = self.target; |
0531ce1d | 712 | |
dfeec247 | 713 | let llvm_config = builder.ensure(Llvm { target: self.target }); |
0531ce1d | 714 | |
83c7162d | 715 | let out_dir = builder.lld_out(target); |
0531ce1d XL |
716 | let done_stamp = out_dir.join("lld-finished-building"); |
717 | if done_stamp.exists() { | |
dfeec247 | 718 | return out_dir; |
0531ce1d XL |
719 | } |
720 | ||
83c7162d XL |
721 | builder.info(&format!("Building LLD for {}", target)); |
722 | let _time = util::timeit(&builder); | |
0531ce1d XL |
723 | t!(fs::create_dir_all(&out_dir)); |
724 | ||
9fa01778 | 725 | let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/lld")); |
5099ac24 | 726 | configure_cmake(builder, target, &mut cfg, true, LdFlags::default()); |
0531ce1d | 727 | |
94b46f34 XL |
728 | // This is an awful, awful hack. Discovered when we migrated to using |
729 | // clang-cl to compile LLVM/LLD it turns out that LLD, when built out of | |
730 | // tree, will execute `llvm-config --cmakedir` and then tell CMake about | |
731 | // that directory for later processing. Unfortunately if this path has | |
732 | // forward slashes in it (which it basically always does on Windows) | |
733 | // then CMake will hit a syntax error later on as... something isn't | |
734 | // escaped it seems? | |
735 | // | |
736 | // Instead of attempting to fix this problem in upstream CMake and/or | |
737 | // LLVM/LLD we just hack around it here. This thin wrapper will take the | |
738 | // output from llvm-config and replace all instances of `\` with `/` to | |
739 | // ensure we don't hit the same bugs with escaping. It means that you | |
740 | // can't build on a system where your paths require `\` on Windows, but | |
741 | // there's probably a lot of reasons you can't do that other than this. | |
dfeec247 | 742 | let llvm_config_shim = env::current_exe().unwrap().with_file_name("llvm-config-wrapper"); |
f035d41b | 743 | |
5e7ed085 FG |
744 | // Re-use the same flags as llvm to control the level of debug information |
745 | // generated for lld. | |
746 | let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) { | |
747 | (false, _) => "Debug", | |
748 | (true, false) => "Release", | |
749 | (true, true) => "RelWithDebInfo", | |
750 | }; | |
751 | ||
0531ce1d | 752 | cfg.out_dir(&out_dir) |
5e7ed085 | 753 | .profile(profile) |
f9f354fc | 754 | .env("LLVM_CONFIG_REAL", &llvm_config) |
dfeec247 XL |
755 | .define("LLVM_CONFIG_PATH", llvm_config_shim) |
756 | .define("LLVM_INCLUDE_TESTS", "OFF"); | |
0531ce1d | 757 | |
923072b8 FG |
758 | if builder.config.llvm_allow_old_toolchain { |
759 | cfg.define("LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN", "YES"); | |
760 | } | |
761 | ||
f9f354fc XL |
762 | // While we're using this horrible workaround to shim the execution of |
763 | // llvm-config, let's just pile on more. I can't seem to figure out how | |
764 | // to build LLD as a standalone project and also cross-compile it at the | |
765 | // same time. It wants a natively executable `llvm-config` to learn | |
766 | // about LLVM, but then it learns about all the host configuration of | |
767 | // LLVM and tries to link to host LLVM libraries. | |
768 | // | |
769 | // To work around that we tell our shim to replace anything with the | |
770 | // build target with the actual target instead. This'll break parts of | |
771 | // LLD though which try to execute host tools, such as llvm-tblgen, so | |
772 | // we specifically tell it where to find those. This is likely super | |
773 | // brittle and will break over time. If anyone knows better how to | |
774 | // cross-compile LLD it would be much appreciated to fix this! | |
775 | if target != builder.config.build { | |
3dfed10e XL |
776 | cfg.env("LLVM_CONFIG_SHIM_REPLACE", &builder.config.build.triple) |
777 | .env("LLVM_CONFIG_SHIM_REPLACE_WITH", &target.triple) | |
f035d41b XL |
778 | .define( |
779 | "LLVM_TABLEGEN_EXE", | |
780 | llvm_config.with_file_name("llvm-tblgen").with_extension(EXE_EXTENSION), | |
781 | ); | |
f9f354fc XL |
782 | } |
783 | ||
784 | // Explicitly set C++ standard, because upstream doesn't do so | |
785 | // for standalone builds. | |
786 | cfg.define("CMAKE_CXX_STANDARD", "14"); | |
787 | ||
0531ce1d XL |
788 | cfg.build(); |
789 | ||
790 | t!(File::create(&done_stamp)); | |
791 | out_dir | |
792 | } | |
793 | } | |
794 | ||
3b2f2976 XL |
795 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
796 | pub struct TestHelpers { | |
3dfed10e | 797 | pub target: TargetSelection, |
3b2f2976 XL |
798 | } |
799 | ||
800 | impl Step for TestHelpers { | |
801 | type Output = (); | |
802 | ||
9fa01778 | 803 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
0531ce1d | 804 | run.path("src/test/auxiliary/rust_test_helpers.c") |
5bcae85e SL |
805 | } |
806 | ||
9fa01778 | 807 | fn make_run(run: RunConfig<'_>) { |
3b2f2976 | 808 | run.builder.ensure(TestHelpers { target: run.target }) |
476ff2be SL |
809 | } |
810 | ||
3b2f2976 | 811 | /// Compiles the `rust_test_helpers.c` library which we used in various |
416331ca | 812 | /// `run-pass` tests for ABI testing. |
9fa01778 | 813 | fn run(self, builder: &Builder<'_>) { |
83c7162d XL |
814 | if builder.config.dry_run { |
815 | return; | |
816 | } | |
1b1a35ee XL |
817 | // The x86_64-fortanix-unknown-sgx target doesn't have a working C |
818 | // toolchain. However, some x86_64 ELF objects can be linked | |
819 | // without issues. Use this hack to compile the test helpers. | |
820 | let target = if self.target == "x86_64-fortanix-unknown-sgx" { | |
821 | TargetSelection::from_user("x86_64-unknown-linux-gnu") | |
822 | } else { | |
823 | self.target | |
824 | }; | |
83c7162d XL |
825 | let dst = builder.test_helpers_out(target); |
826 | let src = builder.src.join("src/test/auxiliary/rust_test_helpers.c"); | |
3b2f2976 | 827 | if up_to_date(&src, &dst.join("librust_test_helpers.a")) { |
dfeec247 | 828 | return; |
3b2f2976 XL |
829 | } |
830 | ||
8faf50e0 | 831 | builder.info("Building test helpers"); |
3b2f2976 | 832 | t!(fs::create_dir_all(&dst)); |
ea8adc8c | 833 | let mut cfg = cc::Build::new(); |
e74abb32 XL |
834 | // FIXME: Workaround for https://github.com/emscripten-core/emscripten/issues/9013 |
835 | if target.contains("emscripten") { | |
836 | cfg.pic(false); | |
837 | } | |
3b2f2976 XL |
838 | |
839 | // We may have found various cross-compilers a little differently due to our | |
f035d41b XL |
840 | // extra configuration, so inform cc of these compilers. Note, though, that |
841 | // on MSVC we still need cc's detection of env vars (ugh). | |
3b2f2976 | 842 | if !target.contains("msvc") { |
83c7162d | 843 | if let Some(ar) = builder.ar(target) { |
3b2f2976 XL |
844 | cfg.archiver(ar); |
845 | } | |
83c7162d | 846 | cfg.compiler(builder.cc(target)); |
3b2f2976 | 847 | } |
3b2f2976 | 848 | cfg.cargo_metadata(false) |
dfeec247 | 849 | .out_dir(&dst) |
3dfed10e XL |
850 | .target(&target.triple) |
851 | .host(&builder.config.build.triple) | |
dfeec247 XL |
852 | .opt_level(0) |
853 | .warnings(false) | |
854 | .debug(false) | |
855 | .file(builder.src.join("src/test/auxiliary/rust_test_helpers.c")) | |
856 | .compile("rust_test_helpers"); | |
857 | } | |
858 | } | |
859 | ||
860 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | |
861 | pub struct Sanitizers { | |
3dfed10e | 862 | pub target: TargetSelection, |
dfeec247 XL |
863 | } |
864 | ||
865 | impl Step for Sanitizers { | |
866 | type Output = Vec<SanitizerRuntime>; | |
867 | ||
868 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { | |
04454e1e | 869 | run.alias("sanitizers") |
dfeec247 XL |
870 | } |
871 | ||
872 | fn make_run(run: RunConfig<'_>) { | |
873 | run.builder.ensure(Sanitizers { target: run.target }); | |
874 | } | |
875 | ||
876 | /// Builds sanitizer runtime libraries. | |
877 | fn run(self, builder: &Builder<'_>) -> Self::Output { | |
878 | let compiler_rt_dir = builder.src.join("src/llvm-project/compiler-rt"); | |
879 | if !compiler_rt_dir.exists() { | |
880 | return Vec::new(); | |
881 | } | |
882 | ||
883 | let out_dir = builder.native_dir(self.target).join("sanitizers"); | |
74b04a01 | 884 | let runtimes = supported_sanitizers(&out_dir, self.target, &builder.config.channel); |
dfeec247 XL |
885 | if runtimes.is_empty() { |
886 | return runtimes; | |
887 | } | |
888 | ||
889 | let llvm_config = builder.ensure(Llvm { target: builder.config.build }); | |
890 | if builder.config.dry_run { | |
891 | return runtimes; | |
892 | } | |
893 | ||
ba9703b0 XL |
894 | let stamp = out_dir.join("sanitizers-finished-building"); |
895 | let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha()); | |
896 | ||
897 | if stamp.is_done() { | |
898 | if stamp.hash.is_none() { | |
899 | builder.info(&format!( | |
900 | "Rebuild sanitizers by removing the file `{}`", | |
901 | stamp.path.display() | |
902 | )); | |
903 | } | |
dfeec247 XL |
904 | return runtimes; |
905 | } | |
906 | ||
907 | builder.info(&format!("Building sanitizers for {}", self.target)); | |
ba9703b0 | 908 | t!(stamp.remove()); |
dfeec247 XL |
909 | let _time = util::timeit(&builder); |
910 | ||
911 | let mut cfg = cmake::Config::new(&compiler_rt_dir); | |
912 | cfg.profile("Release"); | |
3dfed10e | 913 | cfg.define("CMAKE_C_COMPILER_TARGET", self.target.triple); |
dfeec247 XL |
914 | cfg.define("COMPILER_RT_BUILD_BUILTINS", "OFF"); |
915 | cfg.define("COMPILER_RT_BUILD_CRT", "OFF"); | |
916 | cfg.define("COMPILER_RT_BUILD_LIBFUZZER", "OFF"); | |
917 | cfg.define("COMPILER_RT_BUILD_PROFILE", "OFF"); | |
918 | cfg.define("COMPILER_RT_BUILD_SANITIZERS", "ON"); | |
919 | cfg.define("COMPILER_RT_BUILD_XRAY", "OFF"); | |
920 | cfg.define("COMPILER_RT_DEFAULT_TARGET_ONLY", "ON"); | |
921 | cfg.define("COMPILER_RT_USE_LIBCXX", "OFF"); | |
922 | cfg.define("LLVM_CONFIG_PATH", &llvm_config); | |
923 | ||
924 | // On Darwin targets the sanitizer runtimes are build as universal binaries. | |
925 | // Unfortunately sccache currently lacks support to build them successfully. | |
926 | // Disable compiler launcher on Darwin targets to avoid potential issues. | |
927 | let use_compiler_launcher = !self.target.contains("apple-darwin"); | |
5099ac24 | 928 | configure_cmake(builder, self.target, &mut cfg, use_compiler_launcher, LdFlags::default()); |
dfeec247 XL |
929 | |
930 | t!(fs::create_dir_all(&out_dir)); | |
931 | cfg.out_dir(out_dir); | |
932 | ||
933 | for runtime in &runtimes { | |
934 | cfg.build_target(&runtime.cmake_target); | |
935 | cfg.build(); | |
936 | } | |
ba9703b0 | 937 | t!(stamp.write()); |
dfeec247 XL |
938 | |
939 | runtimes | |
940 | } | |
941 | } | |
942 | ||
943 | #[derive(Clone, Debug)] | |
944 | pub struct SanitizerRuntime { | |
945 | /// CMake target used to build the runtime. | |
946 | pub cmake_target: String, | |
947 | /// Path to the built runtime library. | |
948 | pub path: PathBuf, | |
949 | /// Library filename that will be used rustc. | |
950 | pub name: String, | |
951 | } | |
952 | ||
953 | /// Returns sanitizers available on a given target. | |
74b04a01 XL |
954 | fn supported_sanitizers( |
955 | out_dir: &Path, | |
3dfed10e | 956 | target: TargetSelection, |
74b04a01 XL |
957 | channel: &str, |
958 | ) -> Vec<SanitizerRuntime> { | |
f035d41b XL |
959 | let darwin_libs = |os: &str, components: &[&str]| -> Vec<SanitizerRuntime> { |
960 | components | |
3dfed10e | 961 | .iter() |
f035d41b XL |
962 | .map(move |c| SanitizerRuntime { |
963 | cmake_target: format!("clang_rt.{}_{}_dynamic", c, os), | |
964 | path: out_dir | |
965 | .join(&format!("build/lib/darwin/libclang_rt.{}_{}_dynamic.dylib", c, os)), | |
966 | name: format!("librustc-{}_rt.{}.dylib", channel, c), | |
967 | }) | |
968 | .collect() | |
969 | }; | |
970 | ||
971 | let common_libs = |os: &str, arch: &str, components: &[&str]| -> Vec<SanitizerRuntime> { | |
972 | components | |
3dfed10e | 973 | .iter() |
f035d41b XL |
974 | .map(move |c| SanitizerRuntime { |
975 | cmake_target: format!("clang_rt.{}-{}", c, arch), | |
976 | path: out_dir.join(&format!("build/lib/{}/libclang_rt.{}-{}.a", os, c, arch)), | |
977 | name: format!("librustc-{}_rt.{}.a", channel, c), | |
978 | }) | |
979 | .collect() | |
980 | }; | |
981 | ||
3dfed10e | 982 | match &*target.triple { |
5869c6ff | 983 | "aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), |
f035d41b XL |
984 | "aarch64-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]), |
985 | "aarch64-unknown-linux-gnu" => { | |
6a06907d | 986 | common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"]) |
dfeec247 | 987 | } |
f035d41b XL |
988 | "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), |
989 | "x86_64-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]), | |
3dfed10e | 990 | "x86_64-unknown-freebsd" => common_libs("freebsd", "x86_64", &["asan", "msan", "tsan"]), |
94222f64 XL |
991 | "x86_64-unknown-netbsd" => { |
992 | common_libs("netbsd", "x86_64", &["asan", "lsan", "msan", "tsan"]) | |
993 | } | |
994 | "x86_64-unknown-illumos" => common_libs("illumos", "x86_64", &["asan"]), | |
995 | "x86_64-pc-solaris" => common_libs("solaris", "x86_64", &["asan"]), | |
dfeec247 | 996 | "x86_64-unknown-linux-gnu" => { |
f035d41b | 997 | common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"]) |
dfeec247 | 998 | } |
cdc7bbd5 XL |
999 | "x86_64-unknown-linux-musl" => { |
1000 | common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"]) | |
1001 | } | |
f035d41b | 1002 | _ => Vec::new(), |
3b2f2976 | 1003 | } |
5bcae85e | 1004 | } |
ba9703b0 XL |
1005 | |
1006 | struct HashStamp { | |
1007 | path: PathBuf, | |
1008 | hash: Option<Vec<u8>>, | |
1009 | } | |
1010 | ||
1011 | impl HashStamp { | |
1012 | fn new(path: PathBuf, hash: Option<&str>) -> Self { | |
1013 | HashStamp { path, hash: hash.map(|s| s.as_bytes().to_owned()) } | |
1014 | } | |
1015 | ||
1016 | fn is_done(&self) -> bool { | |
1017 | match fs::read(&self.path) { | |
1018 | Ok(h) => self.hash.as_deref().unwrap_or(b"") == h.as_slice(), | |
1019 | Err(e) if e.kind() == io::ErrorKind::NotFound => false, | |
1020 | Err(e) => { | |
1021 | panic!("failed to read stamp file `{}`: {}", self.path.display(), e); | |
1022 | } | |
1023 | } | |
1024 | } | |
1025 | ||
1026 | fn remove(&self) -> io::Result<()> { | |
1027 | match fs::remove_file(&self.path) { | |
1028 | Ok(()) => Ok(()), | |
1029 | Err(e) => { | |
1030 | if e.kind() == io::ErrorKind::NotFound { | |
1031 | Ok(()) | |
1032 | } else { | |
1033 | Err(e) | |
1034 | } | |
1035 | } | |
1036 | } | |
1037 | } | |
1038 | ||
1039 | fn write(&self) -> io::Result<()> { | |
1040 | fs::write(&self.path, self.hash.as_deref().unwrap_or(b"")) | |
1041 | } | |
1042 | } | |
cdc7bbd5 XL |
1043 | |
1044 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | |
1045 | pub struct CrtBeginEnd { | |
1046 | pub target: TargetSelection, | |
1047 | } | |
1048 | ||
1049 | impl Step for CrtBeginEnd { | |
1050 | type Output = PathBuf; | |
1051 | ||
1052 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { | |
1053 | run.path("src/llvm-project/compiler-rt/lib/crt") | |
1054 | } | |
1055 | ||
1056 | fn make_run(run: RunConfig<'_>) { | |
1057 | run.builder.ensure(CrtBeginEnd { target: run.target }); | |
1058 | } | |
1059 | ||
1060 | /// Build crtbegin.o/crtend.o for musl target. | |
1061 | fn run(self, builder: &Builder<'_>) -> Self::Output { | |
1062 | let out_dir = builder.native_dir(self.target).join("crt"); | |
1063 | ||
1064 | if builder.config.dry_run { | |
1065 | return out_dir; | |
1066 | } | |
1067 | ||
1068 | let crtbegin_src = builder.src.join("src/llvm-project/compiler-rt/lib/crt/crtbegin.c"); | |
1069 | let crtend_src = builder.src.join("src/llvm-project/compiler-rt/lib/crt/crtend.c"); | |
1070 | if up_to_date(&crtbegin_src, &out_dir.join("crtbegin.o")) | |
1071 | && up_to_date(&crtend_src, &out_dir.join("crtendS.o")) | |
1072 | { | |
1073 | return out_dir; | |
1074 | } | |
1075 | ||
1076 | builder.info("Building crtbegin.o and crtend.o"); | |
1077 | t!(fs::create_dir_all(&out_dir)); | |
1078 | ||
1079 | let mut cfg = cc::Build::new(); | |
1080 | ||
1081 | if let Some(ar) = builder.ar(self.target) { | |
1082 | cfg.archiver(ar); | |
1083 | } | |
1084 | cfg.compiler(builder.cc(self.target)); | |
1085 | cfg.cargo_metadata(false) | |
1086 | .out_dir(&out_dir) | |
1087 | .target(&self.target.triple) | |
1088 | .host(&builder.config.build.triple) | |
1089 | .warnings(false) | |
1090 | .debug(false) | |
1091 | .opt_level(3) | |
1092 | .file(crtbegin_src) | |
1093 | .file(crtend_src); | |
1094 | ||
1095 | // Those flags are defined in src/llvm-project/compiler-rt/lib/crt/CMakeLists.txt | |
1096 | // Currently only consumer of those objects is musl, which use .init_array/.fini_array | |
1097 | // instead of .ctors/.dtors | |
1098 | cfg.flag("-std=c11") | |
1099 | .define("CRT_HAS_INITFINI_ARRAY", None) | |
1100 | .define("EH_USE_FRAME_REGISTRY", None); | |
1101 | ||
1102 | cfg.compile("crt"); | |
1103 | ||
1104 | t!(fs::copy(out_dir.join("crtbegin.o"), out_dir.join("crtbeginS.o"))); | |
1105 | t!(fs::copy(out_dir.join("crtend.o"), out_dir.join("crtendS.o"))); | |
1106 | out_dir | |
1107 | } | |
1108 | } | |
94222f64 XL |
1109 | |
1110 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | |
1111 | pub struct Libunwind { | |
1112 | pub target: TargetSelection, | |
1113 | } | |
1114 | ||
1115 | impl Step for Libunwind { | |
1116 | type Output = PathBuf; | |
1117 | ||
1118 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { | |
1119 | run.path("src/llvm-project/libunwind") | |
1120 | } | |
1121 | ||
1122 | fn make_run(run: RunConfig<'_>) { | |
1123 | run.builder.ensure(Libunwind { target: run.target }); | |
1124 | } | |
1125 | ||
1126 | /// Build linunwind.a | |
1127 | fn run(self, builder: &Builder<'_>) -> Self::Output { | |
1128 | if builder.config.dry_run { | |
1129 | return PathBuf::new(); | |
1130 | } | |
1131 | ||
1132 | let out_dir = builder.native_dir(self.target).join("libunwind"); | |
1133 | let root = builder.src.join("src/llvm-project/libunwind"); | |
1134 | ||
1135 | if up_to_date(&root, &out_dir.join("libunwind.a")) { | |
1136 | return out_dir; | |
1137 | } | |
1138 | ||
1139 | builder.info(&format!("Building libunwind.a for {}", self.target.triple)); | |
1140 | t!(fs::create_dir_all(&out_dir)); | |
1141 | ||
1142 | let mut cc_cfg = cc::Build::new(); | |
1143 | let mut cpp_cfg = cc::Build::new(); | |
1144 | ||
1145 | cpp_cfg.cpp(true); | |
1146 | cpp_cfg.cpp_set_stdlib(None); | |
1147 | cpp_cfg.flag("-nostdinc++"); | |
1148 | cpp_cfg.flag("-fno-exceptions"); | |
1149 | cpp_cfg.flag("-fno-rtti"); | |
1150 | cpp_cfg.flag_if_supported("-fvisibility-global-new-delete-hidden"); | |
1151 | ||
1152 | for cfg in [&mut cc_cfg, &mut cpp_cfg].iter_mut() { | |
1153 | if let Some(ar) = builder.ar(self.target) { | |
1154 | cfg.archiver(ar); | |
1155 | } | |
1156 | cfg.target(&self.target.triple); | |
1157 | cfg.host(&builder.config.build.triple); | |
1158 | cfg.warnings(false); | |
1159 | cfg.debug(false); | |
1160 | // get_compiler() need set opt_level first. | |
1161 | cfg.opt_level(3); | |
1162 | cfg.flag("-fstrict-aliasing"); | |
1163 | cfg.flag("-funwind-tables"); | |
1164 | cfg.flag("-fvisibility=hidden"); | |
1165 | cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None); | |
1166 | cfg.include(root.join("include")); | |
1167 | cfg.cargo_metadata(false); | |
1168 | cfg.out_dir(&out_dir); | |
1169 | ||
1170 | if self.target.contains("x86_64-fortanix-unknown-sgx") { | |
1171 | cfg.static_flag(true); | |
1172 | cfg.flag("-fno-stack-protector"); | |
1173 | cfg.flag("-ffreestanding"); | |
1174 | cfg.flag("-fexceptions"); | |
1175 | ||
1176 | // easiest way to undefine since no API available in cc::Build to undefine | |
1177 | cfg.flag("-U_FORTIFY_SOURCE"); | |
1178 | cfg.define("_FORTIFY_SOURCE", "0"); | |
1179 | cfg.define("RUST_SGX", "1"); | |
1180 | cfg.define("__NO_STRING_INLINES", None); | |
1181 | cfg.define("__NO_MATH_INLINES", None); | |
1182 | cfg.define("_LIBUNWIND_IS_BAREMETAL", None); | |
1183 | cfg.define("__LIBUNWIND_IS_NATIVE_ONLY", None); | |
1184 | cfg.define("NDEBUG", None); | |
1185 | } | |
04454e1e FG |
1186 | if self.target.contains("windows") { |
1187 | cfg.define("_LIBUNWIND_HIDE_SYMBOLS", "1"); | |
1188 | cfg.define("_LIBUNWIND_IS_NATIVE_ONLY", "1"); | |
1189 | } | |
94222f64 XL |
1190 | } |
1191 | ||
1192 | cc_cfg.compiler(builder.cc(self.target)); | |
1193 | if let Ok(cxx) = builder.cxx(self.target) { | |
1194 | cpp_cfg.compiler(cxx); | |
1195 | } else { | |
1196 | cc_cfg.compiler(builder.cc(self.target)); | |
1197 | } | |
1198 | ||
1199 | // Don't set this for clang | |
1200 | // By default, Clang builds C code in GNU C17 mode. | |
1201 | // By default, Clang builds C++ code according to the C++98 standard, | |
1202 | // with many C++11 features accepted as extensions. | |
1203 | if cc_cfg.get_compiler().is_like_gnu() { | |
1204 | cc_cfg.flag("-std=c99"); | |
1205 | } | |
1206 | if cpp_cfg.get_compiler().is_like_gnu() { | |
1207 | cpp_cfg.flag("-std=c++11"); | |
1208 | } | |
1209 | ||
1210 | if self.target.contains("x86_64-fortanix-unknown-sgx") || self.target.contains("musl") { | |
1211 | // use the same GCC C compiler command to compile C++ code so we do not need to setup the | |
1212 | // C++ compiler env variables on the builders. | |
1213 | // Don't set this for clang++, as clang++ is able to compile this without libc++. | |
1214 | if cpp_cfg.get_compiler().is_like_gnu() { | |
1215 | cpp_cfg.cpp(false); | |
1216 | cpp_cfg.compiler(builder.cc(self.target)); | |
1217 | } | |
1218 | } | |
1219 | ||
1220 | let mut c_sources = vec![ | |
1221 | "Unwind-sjlj.c", | |
1222 | "UnwindLevel1-gcc-ext.c", | |
1223 | "UnwindLevel1.c", | |
1224 | "UnwindRegistersRestore.S", | |
1225 | "UnwindRegistersSave.S", | |
1226 | ]; | |
1227 | ||
1228 | let cpp_sources = vec!["Unwind-EHABI.cpp", "Unwind-seh.cpp", "libunwind.cpp"]; | |
1229 | let cpp_len = cpp_sources.len(); | |
1230 | ||
1231 | if self.target.contains("x86_64-fortanix-unknown-sgx") { | |
1232 | c_sources.push("UnwindRustSgx.c"); | |
1233 | } | |
1234 | ||
1235 | for src in c_sources { | |
1236 | cc_cfg.file(root.join("src").join(src).canonicalize().unwrap()); | |
1237 | } | |
1238 | ||
1239 | for src in &cpp_sources { | |
1240 | cpp_cfg.file(root.join("src").join(src).canonicalize().unwrap()); | |
1241 | } | |
1242 | ||
1243 | cpp_cfg.compile("unwind-cpp"); | |
1244 | ||
1245 | // FIXME: https://github.com/alexcrichton/cc-rs/issues/545#issuecomment-679242845 | |
1246 | let mut count = 0; | |
1247 | for entry in fs::read_dir(&out_dir).unwrap() { | |
1248 | let file = entry.unwrap().path().canonicalize().unwrap(); | |
1249 | if file.is_file() && file.extension() == Some(OsStr::new("o")) { | |
1250 | // file name starts with "Unwind-EHABI", "Unwind-seh" or "libunwind" | |
1251 | let file_name = file.file_name().unwrap().to_str().expect("UTF-8 file name"); | |
1252 | if cpp_sources.iter().any(|f| file_name.starts_with(&f[..f.len() - 4])) { | |
1253 | cc_cfg.object(&file); | |
1254 | count += 1; | |
1255 | } | |
1256 | } | |
1257 | } | |
1258 | assert_eq!(cpp_len, count, "Can't get object files from {:?}", &out_dir); | |
1259 | ||
1260 | cc_cfg.compile("unwind"); | |
1261 | out_dir | |
1262 | } | |
1263 | } |