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.
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.
11 use std
::collections
::HashMap
;
13 use std
::path
::{Path, PathBuf}
;
14 use std
::process
::Command
;
16 use build_helper
::output
;
18 use build
::util
::{exe, staticlib, libdir, mtime, is_dylib}
;
19 use build
::{Build, Compiler}
;
21 /// Build the standard library.
23 /// This will build the standard library for a particular stage of the build
24 /// using the `compiler` targeting the `target` architecture. The artifacts
25 /// created will also be linked into the sysroot directory.
26 pub fn std
<'a
>(build
: &'a Build
, stage
: u32, target
: &str,
27 compiler
: &Compiler
<'a
>) {
28 let host
= compiler
.host
;
29 println
!("Building stage{} std artifacts ({} -> {})", stage
,
32 // Move compiler-rt into place as it'll be required by the compiler when
33 // building the standard library to link the dylib of libstd
34 let libdir
= build
.sysroot_libdir(stage
, &host
, target
);
35 let _
= fs
::remove_dir_all(&libdir
);
36 t
!(fs
::create_dir_all(&libdir
));
37 t
!(fs
::hard_link(&build
.compiler_rt_built
.borrow()[target
],
38 libdir
.join(staticlib("compiler-rt", target
))));
40 build_startup_objects(build
, target
, &libdir
);
42 let out_dir
= build
.cargo_out(stage
, &host
, true, target
);
43 build
.clear_if_dirty(&out_dir
, &build
.compiler_path(compiler
));
44 let mut cargo
= build
.cargo(stage
, compiler
, true, target
, "build");
45 cargo
.arg("--features").arg(build
.std_features())
46 .arg("--manifest-path")
47 .arg(build
.src
.join("src/rustc/std_shim/Cargo.toml"));
49 if let Some(target
) = build
.config
.target_config
.get(target
) {
50 if let Some(ref jemalloc
) = target
.jemalloc
{
51 cargo
.env("JEMALLOC_OVERRIDE", jemalloc
);
54 if let Some(ref p
) = build
.config
.musl_root
{
55 if target
.contains("musl") {
56 cargo
.env("MUSL_ROOT", p
);
60 build
.run(&mut cargo
);
61 std_link(build
, stage
, target
, compiler
, host
);
64 /// Link all libstd rlibs/dylibs into the sysroot location.
66 /// Links those artifacts generated in the given `stage` for `target` produced
67 /// by `compiler` into `host`'s sysroot.
68 pub fn std_link(build
: &Build
,
73 let libdir
= build
.sysroot_libdir(stage
, host
, target
);
74 let out_dir
= build
.cargo_out(stage
, compiler
.host
, true, target
);
76 // If we're linking one compiler host's output into another, then we weren't
77 // called from the `std` method above. In that case we clean out what's
78 // already there and then also link compiler-rt into place.
79 if host
!= compiler
.host
{
80 let _
= fs
::remove_dir_all(&libdir
);
81 t
!(fs
::create_dir_all(&libdir
));
82 t
!(fs
::hard_link(&build
.compiler_rt_built
.borrow()[target
],
83 libdir
.join(staticlib("compiler-rt", target
))));
85 add_to_sysroot(&out_dir
, &libdir
);
88 /// Build and prepare startup objects like rsbegin.o and rsend.o
90 /// These are primarily used on Windows right now for linking executables/dlls.
91 /// They don't require any library support as they're just plain old object
92 /// files, so we just use the nightly snapshot compiler to always build them (as
93 /// no other compilers are guaranteed to be available).
94 fn build_startup_objects(build
: &Build
, target
: &str, into
: &Path
) {
95 if !target
.contains("pc-windows-gnu") {
98 let compiler
= Compiler
::new(0, &build
.config
.build
);
99 let compiler
= build
.compiler_path(&compiler
);
101 for file
in t
!(fs
::read_dir(build
.src
.join("src/rtstartup"))) {
103 build
.run(Command
::new(&compiler
)
105 .arg("--out-dir").arg(into
)
109 for obj
in ["crt2.o", "dllcrt2.o"].iter() {
110 t
!(fs
::copy(compiler_file(build
.cc(target
), obj
), into
.join(obj
)));
114 /// Build the compiler.
116 /// This will build the compiler for a particular stage of the build using
117 /// the `compiler` targeting the `target` architecture. The artifacts
118 /// created will also be linked into the sysroot directory.
119 pub fn rustc
<'a
>(build
: &'a Build
, stage
: u32, target
: &str,
120 compiler
: &Compiler
<'a
>) {
121 let host
= compiler
.host
;
122 println
!("Building stage{} compiler artifacts ({} -> {})", stage
,
125 let out_dir
= build
.cargo_out(stage
, &host
, false, target
);
126 build
.clear_if_dirty(&out_dir
, &libstd_shim(build
, stage
, &host
, target
));
128 let mut cargo
= build
.cargo(stage
, compiler
, false, target
, "build");
129 cargo
.arg("--features").arg(build
.rustc_features(stage
))
130 .arg("--manifest-path")
131 .arg(build
.src
.join("src/rustc/Cargo.toml"));
133 // In stage0 we may not need to build as many executables
135 cargo
.arg("--bin").arg("rustc");
138 // Set some configuration variables picked up by build scripts and
139 // the compiler alike
140 cargo
.env("CFG_RELEASE", &build
.release
)
141 .env("CFG_RELEASE_CHANNEL", &build
.config
.channel
)
142 .env("CFG_VERSION", &build
.version
)
143 .env("CFG_BOOTSTRAP_KEY", &build
.bootstrap_key
)
144 .env("CFG_PREFIX", build
.config
.prefix
.clone().unwrap_or(String
::new()))
145 .env("RUSTC_BOOTSTRAP_KEY", &build
.bootstrap_key
)
146 .env("CFG_LIBDIR_RELATIVE", "lib");
148 if let Some(ref ver_date
) = build
.ver_date
{
149 cargo
.env("CFG_VER_DATE", ver_date
);
151 if let Some(ref ver_hash
) = build
.ver_hash
{
152 cargo
.env("CFG_VER_HASH", ver_hash
);
154 if !build
.unstable_features
{
155 cargo
.env("CFG_DISABLE_UNSTABLE_FEATURES", "1");
157 let target_config
= build
.config
.target_config
.get(target
);
158 if let Some(ref s
) = target_config
.and_then(|c
| c
.llvm_config
.as_ref()) {
159 cargo
.env("LLVM_CONFIG", s
);
161 let llvm_config
= build
.llvm_out(&build
.config
.build
).join("bin")
162 .join(exe("llvm-config", target
));
163 cargo
.env("LLVM_CONFIG", llvm_config
);
165 if build
.config
.llvm_static_stdcpp
{
166 cargo
.env("LLVM_STATIC_STDCPP",
167 compiler_file(build
.cxx(target
), "libstdc++.a"));
169 if let Some(ref s
) = build
.config
.rustc_default_linker
{
170 cargo
.env("CFG_DEFAULT_LINKER", s
);
172 if let Some(ref s
) = build
.config
.rustc_default_ar
{
173 cargo
.env("CFG_DEFAULT_AR", s
);
175 build
.run(&mut cargo
);
177 rustc_link(build
, stage
, target
, compiler
, compiler
.host
);
180 /// Link all librustc rlibs/dylibs into the sysroot location.
182 /// Links those artifacts generated in the given `stage` for `target` produced
183 /// by `compiler` into `host`'s sysroot.
184 pub fn rustc_link(build
: &Build
,
189 let libdir
= build
.sysroot_libdir(stage
, host
, target
);
190 let out_dir
= build
.cargo_out(stage
, compiler
.host
, false, target
);
191 add_to_sysroot(&out_dir
, &libdir
);
194 /// Cargo's output path for the standard library in a given stage, compiled
195 /// by a particular compiler for the specified target.
196 fn libstd_shim(build
: &Build
, stage
: u32, host
: &str, target
: &str) -> PathBuf
{
197 build
.cargo_out(stage
, host
, true, target
).join("libstd_shim.rlib")
200 fn compiler_file(compiler
: &Path
, file
: &str) -> String
{
201 output(Command
::new(compiler
)
202 .arg(format
!("-print-file-name={}", file
))).trim().to_string()
205 /// Prepare a new compiler from the artifacts in `stage`
207 /// This will assemble a compiler in `build/$host/stage$stage`. The compiler
208 /// must have been previously produced by the `stage - 1` build.config.build
210 pub fn assemble_rustc(build
: &Build
, stage
: u32, host
: &str) {
211 assert
!(stage
> 0, "the stage0 compiler isn't assembled, it's downloaded");
213 // Clear out old files
214 let sysroot
= build
.sysroot(stage
, host
);
215 let _
= fs
::remove_dir_all(&sysroot
);
216 t
!(fs
::create_dir_all(&sysroot
));
218 // Link in all dylibs to the libdir
219 let sysroot_libdir
= sysroot
.join(libdir(host
));
220 t
!(fs
::create_dir_all(&sysroot_libdir
));
221 let src_libdir
= build
.sysroot_libdir(stage
- 1, &build
.config
.build
, host
);
222 for f
in t
!(fs
::read_dir(&src_libdir
)).map(|f
| t
!(f
)) {
223 let filename
= f
.file_name().into_string().unwrap();
224 if is_dylib(&filename
) {
225 t
!(fs
::hard_link(&f
.path(), sysroot_libdir
.join(&filename
)));
229 let out_dir
= build
.cargo_out(stage
- 1, &build
.config
.build
, false, host
);
231 // Link the compiler binary itself into place
232 let rustc
= out_dir
.join(exe("rustc", host
));
233 let bindir
= sysroot
.join("bin");
234 t
!(fs
::create_dir_all(&bindir
));
235 let compiler
= build
.compiler_path(&Compiler
::new(stage
, host
));
236 let _
= fs
::remove_file(&compiler
);
237 t
!(fs
::hard_link(rustc
, compiler
));
239 // See if rustdoc exists to link it into place
240 let rustdoc
= exe("rustdoc", host
);
241 let rustdoc_src
= out_dir
.join(&rustdoc
);
242 let rustdoc_dst
= bindir
.join(&rustdoc
);
243 if fs
::metadata(&rustdoc_src
).is_ok() {
244 let _
= fs
::remove_file(&rustdoc_dst
);
245 t
!(fs
::hard_link(&rustdoc_src
, &rustdoc_dst
));
249 /// Link some files into a rustc sysroot.
251 /// For a particular stage this will link all of the contents of `out_dir`
252 /// into the sysroot of the `host` compiler, assuming the artifacts are
253 /// compiled for the specified `target`.
254 fn add_to_sysroot(out_dir
: &Path
, sysroot_dst
: &Path
) {
255 // Collect the set of all files in the dependencies directory, keyed
256 // off the name of the library. We assume everything is of the form
257 // `foo-<hash>.{rlib,so,...}`, and there could be multiple different
258 // `<hash>` values for the same name (of old builds).
259 let mut map
= HashMap
::new();
260 for file
in t
!(fs
::read_dir(out_dir
.join("deps"))).map(|f
| t
!(f
)) {
261 let filename
= file
.file_name().into_string().unwrap();
263 // We're only interested in linking rlibs + dylibs, other things like
264 // unit tests don't get linked in
265 if !filename
.ends_with(".rlib") &&
266 !filename
.ends_with(".lib") &&
267 !is_dylib(&filename
) {
270 let file
= file
.path();
271 let dash
= filename
.find("-").unwrap();
272 let key
= (filename
[..dash
].to_string(),
273 file
.extension().unwrap().to_owned());
274 map
.entry(key
).or_insert(Vec
::new())
278 // For all hash values found, pick the most recent one to move into the
279 // sysroot, that should be the one we just built.
280 for (_
, paths
) in map
{
281 let (_
, path
) = paths
.iter().map(|path
| {
282 (mtime(&path
).seconds(), path
)
284 t
!(fs
::hard_link(&path
,
285 sysroot_dst
.join(path
.file_name().unwrap())));