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
16 use std
::collections
::HashSet
;
19 use std
::path
::{PathBuf, Path}
;
20 use std
::process
::Command
;
22 use build_helper
::output
;
24 use {Build, Compiler, Mode}
;
25 use util
::{self, dylib_path, dylib_path_var}
;
27 const ADB_TEST_DIR
: &'
static str = "/data/tmp";
29 /// Runs the `linkchecker` tool as compiled in `stage` by the `host` compiler.
31 /// This tool in `src/tools` will verify the validity of all our links in the
32 /// documentation to ensure we don't have a bunch of dead ones.
33 pub fn linkcheck(build
: &Build
, stage
: u32, host
: &str) {
34 println
!("Linkcheck stage{} ({})", stage
, host
);
35 let compiler
= Compiler
::new(stage
, host
);
36 build
.run(build
.tool_cmd(&compiler
, "linkchecker")
37 .arg(build
.out
.join(host
).join("doc")));
40 /// Runs the `cargotest` tool as compiled in `stage` by the `host` compiler.
42 /// This tool in `src/tools` will check out a few Rust projects and run `cargo
43 /// test` to ensure that we don't regress the test suites there.
44 pub fn cargotest(build
: &Build
, stage
: u32, host
: &str) {
45 let ref compiler
= Compiler
::new(stage
, host
);
47 // Configure PATH to find the right rustc. NB. we have to use PATH
48 // and not RUSTC because the Cargo test suite has tests that will
49 // fail if rustc is not spelled `rustc`.
50 let path
= build
.sysroot(compiler
).join("bin");
51 let old_path
= ::std
::env
::var("PATH").expect("");
52 let sep
= if cfg
!(windows
) { ";" }
else {":" }
;
53 let ref newpath
= format
!("{}{}{}", path
.display(), sep
, old_path
);
55 // Note that this is a short, cryptic, and not scoped directory name. This
56 // is currently to minimize the length of path on Windows where we otherwise
57 // quickly run into path name limit constraints.
58 let out_dir
= build
.out
.join("ct");
59 t
!(fs
::create_dir_all(&out_dir
));
61 build
.run(build
.tool_cmd(compiler
, "cargotest")
67 /// Runs the `tidy` tool as compiled in `stage` by the `host` compiler.
69 /// This tool in `src/tools` checks up on various bits and pieces of style and
70 /// otherwise just implements a few lint-like checks that are specific to the
72 pub fn tidy(build
: &Build
, stage
: u32, host
: &str) {
73 println
!("tidy check stage{} ({})", stage
, host
);
74 let compiler
= Compiler
::new(stage
, host
);
75 build
.run(build
.tool_cmd(&compiler
, "tidy")
76 .arg(build
.src
.join("src")));
79 fn testdir(build
: &Build
, host
: &str) -> PathBuf
{
80 build
.out
.join(host
).join("test")
83 /// Executes the `compiletest` tool to run a suite of tests.
85 /// Compiles all tests with `compiler` for `target` with the specified
86 /// compiletest `mode` and `suite` arguments. For example `mode` can be
87 /// "run-pass" or `suite` can be something like `debuginfo`.
88 pub fn compiletest(build
: &Build
,
93 println
!("Check compiletest {} ({} -> {})", suite
, compiler
.host
, target
);
94 let mut cmd
= build
.tool_cmd(compiler
, "compiletest");
96 // compiletest currently has... a lot of arguments, so let's just pass all
99 cmd
.arg("--compile-lib-path").arg(build
.rustc_libdir(compiler
));
100 cmd
.arg("--run-lib-path").arg(build
.sysroot_libdir(compiler
, target
));
101 cmd
.arg("--rustc-path").arg(build
.compiler_path(compiler
));
102 cmd
.arg("--rustdoc-path").arg(build
.rustdoc(compiler
));
103 cmd
.arg("--src-base").arg(build
.src
.join("src/test").join(suite
));
104 cmd
.arg("--build-base").arg(testdir(build
, compiler
.host
).join(suite
));
105 cmd
.arg("--stage-id").arg(format
!("stage{}-{}", compiler
.stage
, target
));
106 cmd
.arg("--mode").arg(mode
);
107 cmd
.arg("--target").arg(target
);
108 cmd
.arg("--host").arg(compiler
.host
);
109 cmd
.arg("--llvm-filecheck").arg(build
.llvm_filecheck(&build
.config
.build
));
111 if let Some(nodejs
) = build
.config
.nodejs
.as_ref() {
112 cmd
.arg("--nodejs").arg(nodejs
);
115 let mut flags
= vec
!["-Crpath".to_string()];
116 if build
.config
.rust_optimize_tests
{
117 flags
.push("-O".to_string());
119 if build
.config
.rust_debuginfo_tests
{
120 flags
.push("-g".to_string());
123 let mut hostflags
= build
.rustc_flags(&compiler
.host
);
124 hostflags
.extend(flags
.clone());
125 cmd
.arg("--host-rustcflags").arg(hostflags
.join(" "));
127 let mut targetflags
= build
.rustc_flags(&target
);
128 targetflags
.extend(flags
);
129 targetflags
.push(format
!("-Lnative={}",
130 build
.test_helpers_out(target
).display()));
131 cmd
.arg("--target-rustcflags").arg(targetflags
.join(" "));
133 // FIXME: CFG_PYTHON should probably be detected more robustly elsewhere
134 let python_default
= "python";
135 cmd
.arg("--docck-python").arg(python_default
);
137 if build
.config
.build
.ends_with("apple-darwin") {
138 // Force /usr/bin/python on OSX for LLDB tests because we're loading the
139 // LLDB plugin's compiled module which only works with the system python
140 // (namely not Homebrew-installed python)
141 cmd
.arg("--lldb-python").arg("/usr/bin/python");
143 cmd
.arg("--lldb-python").arg(python_default
);
146 if let Some(ref gdb
) = build
.config
.gdb
{
147 cmd
.arg("--gdb").arg(gdb
);
149 if let Some(ref vers
) = build
.lldb_version
{
150 cmd
.arg("--lldb-version").arg(vers
);
152 if let Some(ref dir
) = build
.lldb_python_dir
{
153 cmd
.arg("--lldb-python-dir").arg(dir
);
155 let llvm_config
= build
.llvm_config(target
);
156 let llvm_version
= output(Command
::new(&llvm_config
).arg("--version"));
157 cmd
.arg("--llvm-version").arg(llvm_version
);
159 cmd
.args(&build
.flags
.cmd
.test_args());
161 if build
.config
.verbose
|| build
.flags
.verbose
{
162 cmd
.arg("--verbose");
165 if build
.config
.quiet_tests
{
169 // Only pass correct values for these flags for the `run-make` suite as it
170 // requires that a C++ compiler was configured which isn't always the case.
171 if suite
== "run-make" {
172 let llvm_components
= output(Command
::new(&llvm_config
).arg("--components"));
173 let llvm_cxxflags
= output(Command
::new(&llvm_config
).arg("--cxxflags"));
174 cmd
.arg("--cc").arg(build
.cc(target
))
175 .arg("--cxx").arg(build
.cxx(target
))
176 .arg("--cflags").arg(build
.cflags(target
).join(" "))
177 .arg("--llvm-components").arg(llvm_components
.trim())
178 .arg("--llvm-cxxflags").arg(llvm_cxxflags
.trim());
180 cmd
.arg("--cc").arg("")
181 .arg("--cxx").arg("")
182 .arg("--cflags").arg("")
183 .arg("--llvm-components").arg("")
184 .arg("--llvm-cxxflags").arg("");
187 // Running a C compiler on MSVC requires a few env vars to be set, to be
188 // sure to set them here.
189 if target
.contains("msvc") {
190 for &(ref k
, ref v
) in build
.cc
[target
].0.env() {
196 build
.add_bootstrap_key(&mut cmd
);
198 cmd
.arg("--adb-path").arg("adb");
199 cmd
.arg("--adb-test-dir").arg(ADB_TEST_DIR
);
200 if target
.contains("android") {
201 // Assume that cc for this target comes from the android sysroot
202 cmd
.arg("--android-cross-path")
203 .arg(build
.cc(target
).parent().unwrap().parent().unwrap());
205 cmd
.arg("--android-cross-path").arg("");
211 /// Run `rustdoc --test` for all documentation in `src/doc`.
213 /// This will run all tests in our markdown documentation (e.g. the book)
214 /// located in `src/doc`. The `rustdoc` that's run is the one that sits next to
216 pub fn docs(build
: &Build
, compiler
: &Compiler
) {
217 // Do a breadth-first traversal of the `src/doc` directory and just run
218 // tests for all files that end in `*.md`
219 let mut stack
= vec
![build
.src
.join("src/doc")];
221 while let Some(p
) = stack
.pop() {
223 stack
.extend(t
!(p
.read_dir()).map(|p
| t
!(p
).path()));
227 if p
.extension().and_then(|s
| s
.to_str()) != Some("md") {
231 println
!("doc tests for: {}", p
.display());
232 markdown_test(build
, compiler
, &p
);
236 /// Run the error index generator tool to execute the tests located in the error
239 /// The `error_index_generator` tool lives in `src/tools` and is used to
240 /// generate a markdown file from the error indexes of the code base which is
241 /// then passed to `rustdoc --test`.
242 pub fn error_index(build
: &Build
, compiler
: &Compiler
) {
243 println
!("Testing error-index stage{}", compiler
.stage
);
245 let output
= testdir(build
, compiler
.host
).join("error-index.md");
246 build
.run(build
.tool_cmd(compiler
, "error_index_generator")
249 .env("CFG_BUILD", &build
.config
.build
));
251 markdown_test(build
, compiler
, &output
);
254 fn markdown_test(build
: &Build
, compiler
: &Compiler
, markdown
: &Path
) {
255 let mut cmd
= Command
::new(build
.rustdoc(compiler
));
256 build
.add_rustc_lib_path(compiler
, &mut cmd
);
260 let mut test_args
= build
.flags
.cmd
.test_args().join(" ");
261 if build
.config
.quiet_tests
{
262 test_args
.push_str(" --quiet");
264 cmd
.arg("--test-args").arg(test_args
);
269 /// Run all unit tests plus documentation tests for an entire crate DAG defined
270 /// by a `Cargo.toml`
272 /// This is what runs tests for crates like the standard library, compiler, etc.
273 /// It essentially is the driver for running `cargo test`.
275 /// Currently this runs all tests for a DAG by passing a bunch of `-p foo`
276 /// arguments, and those arguments are discovered from `cargo metadata`.
277 pub fn krate(build
: &Build
,
281 krate
: Option
<&str>) {
282 let (name
, path
, features
, root
) = match mode
{
284 ("libstd", "src/rustc/std_shim", build
.std_features(), "std_shim")
287 ("libtest", "src/rustc/test_shim", String
::new(), "test_shim")
290 ("librustc", "src/rustc", build
.rustc_features(), "rustc-main")
292 _
=> panic
!("can only test libraries"),
294 println
!("Testing {} stage{} ({} -> {})", name
, compiler
.stage
,
295 compiler
.host
, target
);
297 // Build up the base `cargo test` command.
299 // Pass in some standard flags then iterate over the graph we've discovered
300 // in `cargo metadata` with the maps above and figure out what `-p`
301 // arguments need to get passed.
302 let mut cargo
= build
.cargo(compiler
, mode
, target
, "test");
303 cargo
.arg("--manifest-path")
304 .arg(build
.src
.join(path
).join("Cargo.toml"))
305 .arg("--features").arg(features
);
309 cargo
.arg("-p").arg(krate
);
312 let mut visited
= HashSet
::new();
313 let mut next
= vec
![root
];
314 while let Some(name
) = next
.pop() {
315 // Right now jemalloc is our only target-specific crate in the sense
316 // that it's not present on all platforms. Custom skip it here for now,
317 // but if we add more this probably wants to get more generalized.
318 if !name
.contains("jemalloc") {
319 cargo
.arg("-p").arg(name
);
321 for dep
in build
.crates
[name
].deps
.iter() {
322 if visited
.insert(dep
) {
330 // The tests are going to run with the *target* libraries, so we need to
331 // ensure that those libraries show up in the LD_LIBRARY_PATH equivalent.
333 // Note that to run the compiler we need to run with the *host* libraries,
334 // but our wrapper scripts arrange for that to be the case anyway.
335 let mut dylib_path
= dylib_path();
336 dylib_path
.insert(0, build
.sysroot_libdir(compiler
, target
));
337 cargo
.env(dylib_path_var(), env
::join_paths(&dylib_path
).unwrap());
339 if build
.config
.quiet_tests
{
341 cargo
.arg("--quiet");
344 if target
.contains("android") {
345 build
.run(cargo
.arg("--no-run"));
346 krate_android(build
, compiler
, target
, mode
);
347 } else if target
.contains("emscripten") {
348 build
.run(cargo
.arg("--no-run"));
349 krate_emscripten(build
, compiler
, target
, mode
);
351 cargo
.args(&build
.flags
.cmd
.test_args());
352 build
.run(&mut cargo
);
356 fn krate_android(build
: &Build
,
360 let mut tests
= Vec
::new();
361 let out_dir
= build
.cargo_out(compiler
, mode
, target
);
362 find_tests(&out_dir
, target
, &mut tests
);
363 find_tests(&out_dir
.join("deps"), target
, &mut tests
);
366 build
.run(Command
::new("adb").arg("push").arg(&test
).arg(ADB_TEST_DIR
));
368 let test_file_name
= test
.file_name().unwrap().to_string_lossy();
369 let log
= format
!("{}/check-stage{}-T-{}-H-{}-{}.log",
375 let program
= format
!("(cd {dir}; \
376 LD_LIBRARY_PATH=./{target} ./{test} \
381 test
= test_file_name
,
383 args
= build
.flags
.cmd
.test_args().join(" "));
385 let output
= output(Command
::new("adb").arg("shell").arg(&program
));
386 println
!("{}", output
);
387 build
.run(Command
::new("adb")
390 .arg(build
.out
.join("tmp")));
391 build
.run(Command
::new("adb").arg("shell").arg("rm").arg(&log
));
392 if !output
.contains("result: ok") {
393 panic
!("some tests failed");
398 fn krate_emscripten(build
: &Build
,
402 let mut tests
= Vec
::new();
403 let out_dir
= build
.cargo_out(compiler
, mode
, target
);
404 find_tests(&out_dir
, target
, &mut tests
);
405 find_tests(&out_dir
.join("deps"), target
, &mut tests
);
408 let test_file_name
= test
.to_string_lossy().into_owned();
409 println
!("running {}", test_file_name
);
410 let nodejs
= build
.config
.nodejs
.as_ref().expect("nodejs not configured");
411 let status
= Command
::new(nodejs
)
412 .arg(&test_file_name
)
413 .stderr(::std
::process
::Stdio
::inherit())
417 if !status
.success() {
418 panic
!("some tests failed");
421 Err(e
) => panic
!(format
!("failed to execute command: {}", e
)),
427 fn find_tests(dir
: &Path
,
429 dst
: &mut Vec
<PathBuf
>) {
430 for e
in t
!(dir
.read_dir()).map(|e
| t
!(e
)) {
431 let file_type
= t
!(e
.file_type());
432 if !file_type
.is_file() {
435 let filename
= e
.file_name().into_string().unwrap();
436 if (target
.contains("windows") && filename
.ends_with(".exe")) ||
437 (!target
.contains("windows") && !filename
.contains(".")) ||
438 (target
.contains("emscripten") && filename
.contains(".js")){
444 pub fn android_copy_libs(build
: &Build
,
447 println
!("Android copy libs to emulator ({})", target
);
448 build
.run(Command
::new("adb").arg("remount"));
449 build
.run(Command
::new("adb").args(&["shell", "rm", "-r", ADB_TEST_DIR
]));
450 build
.run(Command
::new("adb").args(&["shell", "mkdir", ADB_TEST_DIR
]));
451 build
.run(Command
::new("adb")
453 .arg(build
.src
.join("src/etc/adb_run_wrapper.sh"))
456 let target_dir
= format
!("{}/{}", ADB_TEST_DIR
, target
);
457 build
.run(Command
::new("adb").args(&["shell", "mkdir", &target_dir
[..]]));
459 for f
in t
!(build
.sysroot_libdir(compiler
, target
).read_dir()) {
461 let name
= f
.file_name().into_string().unwrap();
462 if util
::is_dylib(&name
) {
463 build
.run(Command
::new("adb")