]>
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 | ||
a7813a04 XL |
11 | //! Implementation of compiling various phases of the compiler and standard |
12 | //! library. | |
13 | //! | |
14 | //! This module contains some of the real meat in the rustbuild build system | |
15 | //! which is where Cargo is used to compiler the standard library, libtest, and | |
16 | //! compiler. This module is also responsible for assembling the sysroot as it | |
17 | //! goes along from the output of the previous stage. | |
18 | ||
7453a54e SL |
19 | use std::collections::HashMap; |
20 | use std::fs; | |
21 | use std::path::{Path, PathBuf}; | |
22 | use std::process::Command; | |
23 | ||
24 | use build_helper::output; | |
25 | ||
54a0048b SL |
26 | use build::util::{exe, staticlib, libdir, mtime, is_dylib, copy}; |
27 | use build::{Build, Compiler, Mode}; | |
7453a54e SL |
28 | |
29 | /// Build the standard library. | |
30 | /// | |
31 | /// This will build the standard library for a particular stage of the build | |
32 | /// using the `compiler` targeting the `target` architecture. The artifacts | |
33 | /// created will also be linked into the sysroot directory. | |
54a0048b SL |
34 | pub fn std<'a>(build: &'a Build, target: &str, compiler: &Compiler<'a>) { |
35 | println!("Building stage{} std artifacts ({} -> {})", compiler.stage, | |
36 | compiler.host, target); | |
7453a54e SL |
37 | |
38 | // Move compiler-rt into place as it'll be required by the compiler when | |
39 | // building the standard library to link the dylib of libstd | |
54a0048b | 40 | let libdir = build.sysroot_libdir(compiler, target); |
7453a54e SL |
41 | let _ = fs::remove_dir_all(&libdir); |
42 | t!(fs::create_dir_all(&libdir)); | |
54a0048b SL |
43 | copy(&build.compiler_rt_built.borrow()[target], |
44 | &libdir.join(staticlib("compiler-rt", target))); | |
7453a54e | 45 | |
a7813a04 XL |
46 | // Some platforms have startup objects that may be required to produce the |
47 | // libstd dynamic library, for example. | |
7453a54e SL |
48 | build_startup_objects(build, target, &libdir); |
49 | ||
54a0048b | 50 | let out_dir = build.cargo_out(compiler, Mode::Libstd, target); |
7453a54e | 51 | build.clear_if_dirty(&out_dir, &build.compiler_path(compiler)); |
54a0048b | 52 | let mut cargo = build.cargo(compiler, Mode::Libstd, target, "build"); |
7453a54e SL |
53 | cargo.arg("--features").arg(build.std_features()) |
54 | .arg("--manifest-path") | |
55 | .arg(build.src.join("src/rustc/std_shim/Cargo.toml")); | |
56 | ||
57 | if let Some(target) = build.config.target_config.get(target) { | |
58 | if let Some(ref jemalloc) = target.jemalloc { | |
59 | cargo.env("JEMALLOC_OVERRIDE", jemalloc); | |
60 | } | |
61 | } | |
62 | if let Some(ref p) = build.config.musl_root { | |
63 | if target.contains("musl") { | |
64 | cargo.env("MUSL_ROOT", p); | |
65 | } | |
66 | } | |
67 | ||
68 | build.run(&mut cargo); | |
54a0048b | 69 | std_link(build, target, compiler, compiler.host); |
7453a54e SL |
70 | } |
71 | ||
72 | /// Link all libstd rlibs/dylibs into the sysroot location. | |
73 | /// | |
74 | /// Links those artifacts generated in the given `stage` for `target` produced | |
75 | /// by `compiler` into `host`'s sysroot. | |
76 | pub fn std_link(build: &Build, | |
7453a54e SL |
77 | target: &str, |
78 | compiler: &Compiler, | |
79 | host: &str) { | |
54a0048b SL |
80 | let target_compiler = Compiler::new(compiler.stage, host); |
81 | let libdir = build.sysroot_libdir(&target_compiler, target); | |
82 | let out_dir = build.cargo_out(compiler, Mode::Libstd, target); | |
7453a54e SL |
83 | |
84 | // If we're linking one compiler host's output into another, then we weren't | |
85 | // called from the `std` method above. In that case we clean out what's | |
86 | // already there and then also link compiler-rt into place. | |
87 | if host != compiler.host { | |
88 | let _ = fs::remove_dir_all(&libdir); | |
89 | t!(fs::create_dir_all(&libdir)); | |
54a0048b SL |
90 | copy(&build.compiler_rt_built.borrow()[target], |
91 | &libdir.join(staticlib("compiler-rt", target))); | |
7453a54e SL |
92 | } |
93 | add_to_sysroot(&out_dir, &libdir); | |
54a0048b SL |
94 | |
95 | if target.contains("musl") && | |
96 | (target.contains("x86_64") || target.contains("i686")) { | |
97 | copy_third_party_objects(build, target, &libdir); | |
98 | } | |
99 | } | |
100 | ||
101 | /// Copies the crt(1,i,n).o startup objects | |
102 | /// | |
103 | /// Only required for musl targets that statically link to libc | |
104 | fn copy_third_party_objects(build: &Build, target: &str, into: &Path) { | |
105 | for &obj in &["crt1.o", "crti.o", "crtn.o"] { | |
106 | copy(&compiler_file(build.cc(target), obj), &into.join(obj)); | |
107 | } | |
7453a54e SL |
108 | } |
109 | ||
110 | /// Build and prepare startup objects like rsbegin.o and rsend.o | |
111 | /// | |
112 | /// These are primarily used on Windows right now for linking executables/dlls. | |
113 | /// They don't require any library support as they're just plain old object | |
114 | /// files, so we just use the nightly snapshot compiler to always build them (as | |
115 | /// no other compilers are guaranteed to be available). | |
116 | fn build_startup_objects(build: &Build, target: &str, into: &Path) { | |
117 | if !target.contains("pc-windows-gnu") { | |
118 | return | |
119 | } | |
120 | let compiler = Compiler::new(0, &build.config.build); | |
121 | let compiler = build.compiler_path(&compiler); | |
122 | ||
123 | for file in t!(fs::read_dir(build.src.join("src/rtstartup"))) { | |
124 | let file = t!(file); | |
125 | build.run(Command::new(&compiler) | |
126 | .arg("--emit=obj") | |
127 | .arg("--out-dir").arg(into) | |
128 | .arg(file.path())); | |
129 | } | |
130 | ||
131 | for obj in ["crt2.o", "dllcrt2.o"].iter() { | |
54a0048b | 132 | copy(&compiler_file(build.cc(target), obj), &into.join(obj)); |
7453a54e SL |
133 | } |
134 | } | |
135 | ||
54a0048b SL |
136 | /// Build libtest. |
137 | /// | |
138 | /// This will build libtest and supporting libraries for a particular stage of | |
139 | /// the build using the `compiler` targeting the `target` architecture. The | |
140 | /// artifacts created will also be linked into the sysroot directory. | |
141 | pub fn test<'a>(build: &'a Build, target: &str, compiler: &Compiler<'a>) { | |
142 | println!("Building stage{} test artifacts ({} -> {})", compiler.stage, | |
143 | compiler.host, target); | |
144 | let out_dir = build.cargo_out(compiler, Mode::Libtest, target); | |
145 | build.clear_if_dirty(&out_dir, &libstd_shim(build, compiler, target)); | |
146 | let mut cargo = build.cargo(compiler, Mode::Libtest, target, "build"); | |
147 | cargo.arg("--manifest-path") | |
148 | .arg(build.src.join("src/rustc/test_shim/Cargo.toml")); | |
149 | build.run(&mut cargo); | |
150 | test_link(build, target, compiler, compiler.host); | |
151 | } | |
152 | ||
153 | /// Link all libtest rlibs/dylibs into the sysroot location. | |
154 | /// | |
155 | /// Links those artifacts generated in the given `stage` for `target` produced | |
156 | /// by `compiler` into `host`'s sysroot. | |
157 | pub fn test_link(build: &Build, | |
158 | target: &str, | |
159 | compiler: &Compiler, | |
160 | host: &str) { | |
161 | let target_compiler = Compiler::new(compiler.stage, host); | |
162 | let libdir = build.sysroot_libdir(&target_compiler, target); | |
163 | let out_dir = build.cargo_out(compiler, Mode::Libtest, target); | |
164 | add_to_sysroot(&out_dir, &libdir); | |
165 | } | |
166 | ||
7453a54e SL |
167 | /// Build the compiler. |
168 | /// | |
169 | /// This will build the compiler for a particular stage of the build using | |
170 | /// the `compiler` targeting the `target` architecture. The artifacts | |
171 | /// created will also be linked into the sysroot directory. | |
54a0048b SL |
172 | pub fn rustc<'a>(build: &'a Build, target: &str, compiler: &Compiler<'a>) { |
173 | println!("Building stage{} compiler artifacts ({} -> {})", | |
174 | compiler.stage, compiler.host, target); | |
7453a54e | 175 | |
54a0048b SL |
176 | let out_dir = build.cargo_out(compiler, Mode::Librustc, target); |
177 | build.clear_if_dirty(&out_dir, &libtest_shim(build, compiler, target)); | |
7453a54e | 178 | |
54a0048b SL |
179 | let mut cargo = build.cargo(compiler, Mode::Librustc, target, "build"); |
180 | cargo.arg("--features").arg(build.rustc_features()) | |
7453a54e SL |
181 | .arg("--manifest-path") |
182 | .arg(build.src.join("src/rustc/Cargo.toml")); | |
183 | ||
7453a54e SL |
184 | // Set some configuration variables picked up by build scripts and |
185 | // the compiler alike | |
186 | cargo.env("CFG_RELEASE", &build.release) | |
187 | .env("CFG_RELEASE_CHANNEL", &build.config.channel) | |
188 | .env("CFG_VERSION", &build.version) | |
189 | .env("CFG_BOOTSTRAP_KEY", &build.bootstrap_key) | |
190 | .env("CFG_PREFIX", build.config.prefix.clone().unwrap_or(String::new())) | |
7453a54e SL |
191 | .env("CFG_LIBDIR_RELATIVE", "lib"); |
192 | ||
193 | if let Some(ref ver_date) = build.ver_date { | |
194 | cargo.env("CFG_VER_DATE", ver_date); | |
195 | } | |
196 | if let Some(ref ver_hash) = build.ver_hash { | |
197 | cargo.env("CFG_VER_HASH", ver_hash); | |
198 | } | |
199 | if !build.unstable_features { | |
200 | cargo.env("CFG_DISABLE_UNSTABLE_FEATURES", "1"); | |
201 | } | |
a7813a04 | 202 | cargo.env("LLVM_CONFIG", build.llvm_config(target)); |
7453a54e SL |
203 | if build.config.llvm_static_stdcpp { |
204 | cargo.env("LLVM_STATIC_STDCPP", | |
205 | compiler_file(build.cxx(target), "libstdc++.a")); | |
206 | } | |
207 | if let Some(ref s) = build.config.rustc_default_linker { | |
208 | cargo.env("CFG_DEFAULT_LINKER", s); | |
209 | } | |
210 | if let Some(ref s) = build.config.rustc_default_ar { | |
211 | cargo.env("CFG_DEFAULT_AR", s); | |
212 | } | |
213 | build.run(&mut cargo); | |
214 | ||
54a0048b | 215 | rustc_link(build, target, compiler, compiler.host); |
7453a54e SL |
216 | } |
217 | ||
218 | /// Link all librustc rlibs/dylibs into the sysroot location. | |
219 | /// | |
220 | /// Links those artifacts generated in the given `stage` for `target` produced | |
221 | /// by `compiler` into `host`'s sysroot. | |
222 | pub fn rustc_link(build: &Build, | |
7453a54e SL |
223 | target: &str, |
224 | compiler: &Compiler, | |
225 | host: &str) { | |
54a0048b SL |
226 | let target_compiler = Compiler::new(compiler.stage, host); |
227 | let libdir = build.sysroot_libdir(&target_compiler, target); | |
228 | let out_dir = build.cargo_out(compiler, Mode::Librustc, target); | |
7453a54e SL |
229 | add_to_sysroot(&out_dir, &libdir); |
230 | } | |
231 | ||
232 | /// Cargo's output path for the standard library in a given stage, compiled | |
233 | /// by a particular compiler for the specified target. | |
54a0048b SL |
234 | fn libstd_shim(build: &Build, compiler: &Compiler, target: &str) -> PathBuf { |
235 | build.cargo_out(compiler, Mode::Libstd, target).join("libstd_shim.rlib") | |
7453a54e SL |
236 | } |
237 | ||
54a0048b SL |
238 | /// Cargo's output path for libtest in a given stage, compiled by a particular |
239 | /// compiler for the specified target. | |
240 | fn libtest_shim(build: &Build, compiler: &Compiler, target: &str) -> PathBuf { | |
241 | build.cargo_out(compiler, Mode::Libtest, target).join("libtest_shim.rlib") | |
242 | } | |
243 | ||
244 | fn compiler_file(compiler: &Path, file: &str) -> PathBuf { | |
245 | let out = output(Command::new(compiler) | |
246 | .arg(format!("-print-file-name={}", file))); | |
247 | PathBuf::from(out.trim()) | |
7453a54e SL |
248 | } |
249 | ||
250 | /// Prepare a new compiler from the artifacts in `stage` | |
251 | /// | |
252 | /// This will assemble a compiler in `build/$host/stage$stage`. The compiler | |
253 | /// must have been previously produced by the `stage - 1` build.config.build | |
254 | /// compiler. | |
255 | pub fn assemble_rustc(build: &Build, stage: u32, host: &str) { | |
256 | assert!(stage > 0, "the stage0 compiler isn't assembled, it's downloaded"); | |
54a0048b SL |
257 | // The compiler that we're assembling |
258 | let target_compiler = Compiler::new(stage, host); | |
259 | ||
260 | // The compiler that compiled the compiler we're assembling | |
261 | let build_compiler = Compiler::new(stage - 1, &build.config.build); | |
7453a54e SL |
262 | |
263 | // Clear out old files | |
54a0048b | 264 | let sysroot = build.sysroot(&target_compiler); |
7453a54e SL |
265 | let _ = fs::remove_dir_all(&sysroot); |
266 | t!(fs::create_dir_all(&sysroot)); | |
267 | ||
268 | // Link in all dylibs to the libdir | |
269 | let sysroot_libdir = sysroot.join(libdir(host)); | |
270 | t!(fs::create_dir_all(&sysroot_libdir)); | |
54a0048b | 271 | let src_libdir = build.sysroot_libdir(&build_compiler, host); |
7453a54e SL |
272 | for f in t!(fs::read_dir(&src_libdir)).map(|f| t!(f)) { |
273 | let filename = f.file_name().into_string().unwrap(); | |
274 | if is_dylib(&filename) { | |
54a0048b | 275 | copy(&f.path(), &sysroot_libdir.join(&filename)); |
7453a54e SL |
276 | } |
277 | } | |
278 | ||
54a0048b | 279 | let out_dir = build.cargo_out(&build_compiler, Mode::Librustc, host); |
7453a54e SL |
280 | |
281 | // Link the compiler binary itself into place | |
282 | let rustc = out_dir.join(exe("rustc", host)); | |
283 | let bindir = sysroot.join("bin"); | |
284 | t!(fs::create_dir_all(&bindir)); | |
285 | let compiler = build.compiler_path(&Compiler::new(stage, host)); | |
286 | let _ = fs::remove_file(&compiler); | |
54a0048b | 287 | copy(&rustc, &compiler); |
7453a54e SL |
288 | |
289 | // See if rustdoc exists to link it into place | |
290 | let rustdoc = exe("rustdoc", host); | |
291 | let rustdoc_src = out_dir.join(&rustdoc); | |
292 | let rustdoc_dst = bindir.join(&rustdoc); | |
293 | if fs::metadata(&rustdoc_src).is_ok() { | |
294 | let _ = fs::remove_file(&rustdoc_dst); | |
54a0048b | 295 | copy(&rustdoc_src, &rustdoc_dst); |
7453a54e SL |
296 | } |
297 | } | |
298 | ||
299 | /// Link some files into a rustc sysroot. | |
300 | /// | |
301 | /// For a particular stage this will link all of the contents of `out_dir` | |
302 | /// into the sysroot of the `host` compiler, assuming the artifacts are | |
303 | /// compiled for the specified `target`. | |
304 | fn add_to_sysroot(out_dir: &Path, sysroot_dst: &Path) { | |
305 | // Collect the set of all files in the dependencies directory, keyed | |
306 | // off the name of the library. We assume everything is of the form | |
307 | // `foo-<hash>.{rlib,so,...}`, and there could be multiple different | |
308 | // `<hash>` values for the same name (of old builds). | |
309 | let mut map = HashMap::new(); | |
310 | for file in t!(fs::read_dir(out_dir.join("deps"))).map(|f| t!(f)) { | |
311 | let filename = file.file_name().into_string().unwrap(); | |
312 | ||
313 | // We're only interested in linking rlibs + dylibs, other things like | |
314 | // unit tests don't get linked in | |
315 | if !filename.ends_with(".rlib") && | |
316 | !filename.ends_with(".lib") && | |
317 | !is_dylib(&filename) { | |
318 | continue | |
319 | } | |
320 | let file = file.path(); | |
321 | let dash = filename.find("-").unwrap(); | |
322 | let key = (filename[..dash].to_string(), | |
323 | file.extension().unwrap().to_owned()); | |
324 | map.entry(key).or_insert(Vec::new()) | |
325 | .push(file.clone()); | |
326 | } | |
327 | ||
328 | // For all hash values found, pick the most recent one to move into the | |
329 | // sysroot, that should be the one we just built. | |
330 | for (_, paths) in map { | |
331 | let (_, path) = paths.iter().map(|path| { | |
332 | (mtime(&path).seconds(), path) | |
333 | }).max().unwrap(); | |
54a0048b | 334 | copy(&path, &sysroot_dst.join(path.file_name().unwrap())); |
7453a54e SL |
335 | } |
336 | } | |
54a0048b SL |
337 | |
338 | /// Build a tool in `src/tools` | |
339 | /// | |
340 | /// This will build the specified tool with the specified `host` compiler in | |
341 | /// `stage` into the normal cargo output directory. | |
342 | pub fn tool(build: &Build, stage: u32, host: &str, tool: &str) { | |
343 | println!("Building stage{} tool {} ({})", stage, tool, host); | |
344 | ||
345 | let compiler = Compiler::new(stage, host); | |
346 | ||
347 | // FIXME: need to clear out previous tool and ideally deps, may require | |
348 | // isolating output directories or require a pseudo shim step to | |
349 | // clear out all the info. | |
350 | // | |
351 | // Maybe when libstd is compiled it should clear out the rustc of the | |
352 | // corresponding stage? | |
353 | // let out_dir = build.cargo_out(stage, &host, Mode::Librustc, target); | |
354 | // build.clear_if_dirty(&out_dir, &libstd_shim(build, stage, &host, target)); | |
355 | ||
356 | let mut cargo = build.cargo(&compiler, Mode::Tool, host, "build"); | |
357 | cargo.arg("--manifest-path") | |
358 | .arg(build.src.join(format!("src/tools/{}/Cargo.toml", tool))); | |
359 | build.run(&mut cargo); | |
360 | } |