1 // Copyright 2016 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 //! Implementation of the various `check-*` targets of the build system.
13 //! This file implements the various regression test suites that we execute on
17 use std
::fs
::{self, File}
;
18 use std
::io
::prelude
::*;
19 use std
::path
::{PathBuf, Path}
;
20 use std
::process
::Command
;
22 use build_helper
::output
;
23 use bootstrap
::{dylib_path, dylib_path_var}
;
25 use build
::{Build, Compiler, Mode}
;
28 const ADB_TEST_DIR
: &'
static str = "/data/tmp";
30 /// Runs the `linkchecker` tool as compiled in `stage` by the `host` compiler.
32 /// This tool in `src/tools` will verify the validity of all our links in the
33 /// documentation to ensure we don't have a bunch of dead ones.
34 pub fn linkcheck(build
: &Build
, stage
: u32, host
: &str) {
35 println
!("Linkcheck stage{} ({})", stage
, host
);
36 let compiler
= Compiler
::new(stage
, host
);
37 build
.run(build
.tool_cmd(&compiler
, "linkchecker")
38 .arg(build
.out
.join(host
).join("doc")));
41 /// Runs the `cargotest` tool as compiled in `stage` by the `host` compiler.
43 /// This tool in `src/tools` will check out a few Rust projects and run `cargo
44 /// test` to ensure that we don't regress the test suites there.
45 pub fn cargotest(build
: &Build
, stage
: u32, host
: &str) {
46 let ref compiler
= Compiler
::new(stage
, host
);
48 // Configure PATH to find the right rustc. NB. we have to use PATH
49 // and not RUSTC because the Cargo test suite has tests that will
50 // fail if rustc is not spelled `rustc`.
51 let path
= build
.sysroot(compiler
).join("bin");
52 let old_path
= ::std
::env
::var("PATH").expect("");
53 let sep
= if cfg
!(windows
) { ";" }
else {":" }
;
54 let ref newpath
= format
!("{}{}{}", path
.display(), sep
, old_path
);
56 // Note that this is a short, cryptic, and not scoped directory name. This
57 // is currently to minimize the length of path on Windows where we otherwise
58 // quickly run into path name limit constraints.
59 let out_dir
= build
.out
.join("ct");
60 t
!(fs
::create_dir_all(&out_dir
));
62 build
.run(build
.tool_cmd(compiler
, "cargotest")
68 /// Runs the `tidy` tool as compiled in `stage` by the `host` compiler.
70 /// This tool in `src/tools` checks up on various bits and pieces of style and
71 /// otherwise just implements a few lint-like checks that are specific to the
73 pub fn tidy(build
: &Build
, stage
: u32, host
: &str) {
74 println
!("tidy check stage{} ({})", stage
, host
);
75 let compiler
= Compiler
::new(stage
, host
);
76 build
.run(build
.tool_cmd(&compiler
, "tidy")
77 .arg(build
.src
.join("src")));
80 fn testdir(build
: &Build
, host
: &str) -> PathBuf
{
81 build
.out
.join(host
).join("test")
84 /// Executes the `compiletest` tool to run a suite of tests.
86 /// Compiles all tests with `compiler` for `target` with the specified
87 /// compiletest `mode` and `suite` arguments. For example `mode` can be
88 /// "run-pass" or `suite` can be something like `debuginfo`.
89 pub fn compiletest(build
: &Build
,
94 println
!("Check compiletest {} ({} -> {})", suite
, compiler
.host
, target
);
95 let mut cmd
= build
.tool_cmd(compiler
, "compiletest");
97 // compiletest currently has... a lot of arguments, so let's just pass all
100 cmd
.arg("--compile-lib-path").arg(build
.rustc_libdir(compiler
));
101 cmd
.arg("--run-lib-path").arg(build
.sysroot_libdir(compiler
, target
));
102 cmd
.arg("--rustc-path").arg(build
.compiler_path(compiler
));
103 cmd
.arg("--rustdoc-path").arg(build
.rustdoc(compiler
));
104 cmd
.arg("--src-base").arg(build
.src
.join("src/test").join(suite
));
105 cmd
.arg("--build-base").arg(testdir(build
, compiler
.host
).join(suite
));
106 cmd
.arg("--stage-id").arg(format
!("stage{}-{}", compiler
.stage
, target
));
107 cmd
.arg("--mode").arg(mode
);
108 cmd
.arg("--target").arg(target
);
109 cmd
.arg("--host").arg(compiler
.host
);
110 cmd
.arg("--llvm-filecheck").arg(build
.llvm_filecheck(&build
.config
.build
));
112 let mut flags
= vec
!["-Crpath".to_string()];
113 if build
.config
.rust_optimize_tests
{
114 flags
.push("-O".to_string());
116 if build
.config
.rust_debuginfo_tests
{
117 flags
.push("-g".to_string());
120 let mut hostflags
= build
.rustc_flags(&compiler
.host
);
121 hostflags
.extend(flags
.clone());
122 cmd
.arg("--host-rustcflags").arg(hostflags
.join(" "));
124 let mut targetflags
= build
.rustc_flags(&target
);
125 targetflags
.extend(flags
);
126 targetflags
.push(format
!("-Lnative={}",
127 build
.test_helpers_out(target
).display()));
128 cmd
.arg("--target-rustcflags").arg(targetflags
.join(" "));
130 // FIXME: CFG_PYTHON should probably be detected more robustly elsewhere
131 let python_default
= "python";
132 cmd
.arg("--docck-python").arg(python_default
);
134 if build
.config
.build
.ends_with("apple-darwin") {
135 // Force /usr/bin/python on OSX for LLDB tests because we're loading the
136 // LLDB plugin's compiled module which only works with the system python
137 // (namely not Homebrew-installed python)
138 cmd
.arg("--lldb-python").arg("/usr/bin/python");
140 cmd
.arg("--lldb-python").arg(python_default
);
143 if let Some(ref vers
) = build
.gdb_version
{
144 cmd
.arg("--gdb-version").arg(vers
);
146 if let Some(ref vers
) = build
.lldb_version
{
147 cmd
.arg("--lldb-version").arg(vers
);
149 if let Some(ref dir
) = build
.lldb_python_dir
{
150 cmd
.arg("--lldb-python-dir").arg(dir
);
153 cmd
.args(&build
.flags
.args
);
155 if build
.config
.verbose
|| build
.flags
.verbose
{
156 cmd
.arg("--verbose");
159 // Only pass correct values for these flags for the `run-make` suite as it
160 // requires that a C++ compiler was configured which isn't always the case.
161 if suite
== "run-make" {
162 let llvm_config
= build
.llvm_config(target
);
163 let llvm_components
= output(Command
::new(&llvm_config
).arg("--components"));
164 let llvm_cxxflags
= output(Command
::new(&llvm_config
).arg("--cxxflags"));
165 cmd
.arg("--cc").arg(build
.cc(target
))
166 .arg("--cxx").arg(build
.cxx(target
))
167 .arg("--cflags").arg(build
.cflags(target
).join(" "))
168 .arg("--llvm-components").arg(llvm_components
.trim())
169 .arg("--llvm-cxxflags").arg(llvm_cxxflags
.trim());
171 cmd
.arg("--cc").arg("")
172 .arg("--cxx").arg("")
173 .arg("--cflags").arg("")
174 .arg("--llvm-components").arg("")
175 .arg("--llvm-cxxflags").arg("");
178 // Running a C compiler on MSVC requires a few env vars to be set, to be
179 // sure to set them here.
180 if target
.contains("msvc") {
181 for &(ref k
, ref v
) in build
.cc
[target
].0.env() {
187 build
.add_bootstrap_key(compiler
, &mut cmd
);
189 cmd
.arg("--adb-path").arg("adb");
190 cmd
.arg("--adb-test-dir").arg(ADB_TEST_DIR
);
191 if target
.contains("android") {
192 // Assume that cc for this target comes from the android sysroot
193 cmd
.arg("--android-cross-path")
194 .arg(build
.cc(target
).parent().unwrap().parent().unwrap());
196 cmd
.arg("--android-cross-path").arg("");
202 /// Run `rustdoc --test` for all documentation in `src/doc`.
204 /// This will run all tests in our markdown documentation (e.g. the book)
205 /// located in `src/doc`. The `rustdoc` that's run is the one that sits next to
207 pub fn docs(build
: &Build
, compiler
: &Compiler
) {
208 // Do a breadth-first traversal of the `src/doc` directory and just run
209 // tests for all files that end in `*.md`
210 let mut stack
= vec
![build
.src
.join("src/doc")];
212 while let Some(p
) = stack
.pop() {
214 stack
.extend(t
!(p
.read_dir()).map(|p
| t
!(p
).path()));
218 if p
.extension().and_then(|s
| s
.to_str()) != Some("md") {
222 println
!("doc tests for: {}", p
.display());
223 markdown_test(build
, compiler
, &p
);
227 /// Run the error index generator tool to execute the tests located in the error
230 /// The `error_index_generator` tool lives in `src/tools` and is used to
231 /// generate a markdown file from the error indexes of the code base which is
232 /// then passed to `rustdoc --test`.
233 pub fn error_index(build
: &Build
, compiler
: &Compiler
) {
234 println
!("Testing error-index stage{}", compiler
.stage
);
236 let output
= testdir(build
, compiler
.host
).join("error-index.md");
237 build
.run(build
.tool_cmd(compiler
, "error_index_generator")
240 .env("CFG_BUILD", &build
.config
.build
));
242 markdown_test(build
, compiler
, &output
);
245 fn markdown_test(build
: &Build
, compiler
: &Compiler
, markdown
: &Path
) {
246 let mut cmd
= Command
::new(build
.rustdoc(compiler
));
247 build
.add_rustc_lib_path(compiler
, &mut cmd
);
250 cmd
.arg("--test-args").arg(build
.flags
.args
.join(" "));
254 /// Run all unit tests plus documentation tests for an entire crate DAG defined
255 /// by a `Cargo.toml`
257 /// This is what runs tests for crates like the standard library, compiler, etc.
258 /// It essentially is the driver for running `cargo test`.
260 /// Currently this runs all tests for a DAG by passing a bunch of `-p foo`
261 /// arguments, and those arguments are discovered from `Cargo.lock`.
262 pub fn krate(build
: &Build
,
266 let (name
, path
, features
) = match mode
{
267 Mode
::Libstd
=> ("libstd", "src/rustc/std_shim", build
.std_features()),
268 Mode
::Libtest
=> ("libtest", "src/rustc/test_shim", String
::new()),
269 Mode
::Librustc
=> ("librustc", "src/rustc", build
.rustc_features()),
270 _
=> panic
!("can only test libraries"),
272 println
!("Testing {} stage{} ({} -> {})", name
, compiler
.stage
,
273 compiler
.host
, target
);
275 // Build up the base `cargo test` command.
276 let mut cargo
= build
.cargo(compiler
, mode
, target
, "test");
277 cargo
.arg("--manifest-path")
278 .arg(build
.src
.join(path
).join("Cargo.toml"))
279 .arg("--features").arg(features
);
281 // Generate a list of `-p` arguments to pass to the `cargo test` invocation
282 // by crawling the corresponding Cargo.lock file.
283 let lockfile
= build
.src
.join(path
).join("Cargo.lock");
284 let mut contents
= String
::new();
285 t
!(t
!(File
::open(&lockfile
)).read_to_string(&mut contents
));
286 let mut lines
= contents
.lines();
287 while let Some(line
) = lines
.next() {
288 let prefix
= "name = \"";
289 if !line
.starts_with(prefix
) {
292 lines
.next(); // skip `version = ...`
294 // skip crates.io or otherwise non-path crates
295 if let Some(line
) = lines
.next() {
296 if line
.starts_with("source") {
301 let crate_name
= &line
[prefix
.len()..line
.len() - 1];
303 // Right now jemalloc is our only target-specific crate in the sense
304 // that it's not present on all platforms. Custom skip it here for now,
305 // but if we add more this probably wants to get more generalized.
306 if crate_name
.contains("jemalloc") {
310 cargo
.arg("-p").arg(crate_name
);
313 // The tests are going to run with the *target* libraries, so we need to
314 // ensure that those libraries show up in the LD_LIBRARY_PATH equivalent.
316 // Note that to run the compiler we need to run with the *host* libraries,
317 // but our wrapper scripts arrange for that to be the case anyway.
318 let mut dylib_path
= dylib_path();
319 dylib_path
.insert(0, build
.sysroot_libdir(compiler
, target
));
320 cargo
.env(dylib_path_var(), env
::join_paths(&dylib_path
).unwrap());
322 if target
.contains("android") {
323 build
.run(cargo
.arg("--no-run"));
324 krate_android(build
, compiler
, target
, mode
);
326 cargo
.args(&build
.flags
.args
);
327 build
.run(&mut cargo
);
331 fn krate_android(build
: &Build
,
335 let mut tests
= Vec
::new();
336 let out_dir
= build
.cargo_out(compiler
, mode
, target
);
337 find_tests(&out_dir
, target
, &mut tests
);
338 find_tests(&out_dir
.join("deps"), target
, &mut tests
);
341 build
.run(Command
::new("adb").arg("push").arg(&test
).arg(ADB_TEST_DIR
));
343 let test_file_name
= test
.file_name().unwrap().to_string_lossy();
344 let log
= format
!("{}/check-stage{}-T-{}-H-{}-{}.log",
350 let program
= format
!("(cd {dir}; \
351 LD_LIBRARY_PATH=./{target} ./{test} \
356 test
= test_file_name
,
358 args
= build
.flags
.args
.join(" "));
360 let output
= output(Command
::new("adb").arg("shell").arg(&program
));
361 println
!("{}", output
);
362 build
.run(Command
::new("adb")
365 .arg(build
.out
.join("tmp")));
366 build
.run(Command
::new("adb").arg("shell").arg("rm").arg(&log
));
367 if !output
.contains("result: ok") {
368 panic
!("some tests failed");
373 fn find_tests(dir
: &Path
,
375 dst
: &mut Vec
<PathBuf
>) {
376 for e
in t
!(dir
.read_dir()).map(|e
| t
!(e
)) {
377 let file_type
= t
!(e
.file_type());
378 if !file_type
.is_file() {
381 let filename
= e
.file_name().into_string().unwrap();
382 if (target
.contains("windows") && filename
.ends_with(".exe")) ||
383 (!target
.contains("windows") && !filename
.contains(".")) {
389 pub fn android_copy_libs(build
: &Build
,
392 println
!("Android copy libs to emulator ({})", target
);
393 build
.run(Command
::new("adb").arg("remount"));
394 build
.run(Command
::new("adb").args(&["shell", "rm", "-r", ADB_TEST_DIR
]));
395 build
.run(Command
::new("adb").args(&["shell", "mkdir", ADB_TEST_DIR
]));
396 build
.run(Command
::new("adb")
398 .arg(build
.src
.join("src/etc/adb_run_wrapper.sh"))
401 let target_dir
= format
!("{}/{}", ADB_TEST_DIR
, target
);
402 build
.run(Command
::new("adb").args(&["shell", "mkdir", &target_dir
[..]]));
404 for f
in t
!(build
.sysroot_libdir(compiler
, target
).read_dir()) {
406 let name
= f
.file_name().into_string().unwrap();
407 if util
::is_dylib(&name
) {
408 build
.run(Command
::new("adb")