use std::env; use std::fs::{self, File}; use std::io::prelude::*; use std::path::PathBuf; use cargotest::{rustc_host, sleep_ms}; use cargotest::support::{cross_compile, execs, project}; use cargotest::support::paths::CargoPathExt; use cargotest::support::registry::Package; use hamcrest::{assert_that, existing_dir, existing_file}; #[test] fn custom_build_script_failed() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] build = "build.rs" "#, ) .file( "src/main.rs", r#" fn main() {} "#, ) .file( "build.rs", r#" fn main() { std::process::exit(101); } "#, ) .build(); assert_that( p.cargo("build").arg("-v"), execs().with_status(101).with_stderr(&format!( "\ [COMPILING] foo v0.5.0 ({url}) [RUNNING] `rustc --crate-name build_script_build build.rs --crate-type bin [..]` [RUNNING] `[..][/]build-script-build` [ERROR] failed to run custom build command for `foo v0.5.0 ({url})` process didn't exit successfully: `[..][/]build-script-build` (exit code: 101)", url = p.url() )), ); } #[test] fn custom_build_env_vars() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] [features] bar_feat = ["bar/foo"] [dependencies.bar] path = "bar" "#, ) .file( "src/main.rs", r#" fn main() {} "#, ) .file( "bar/Cargo.toml", r#" [project] name = "bar" version = "0.5.0" authors = ["wycats@example.com"] build = "build.rs" [features] foo = [] "#, ) .file( "bar/src/lib.rs", r#" pub fn hello() {} "#, ); let file_content = format!( r#" use std::env; use std::io::prelude::*; use std::path::Path; use std::fs; fn main() {{ let _target = env::var("TARGET").unwrap(); let _ncpus = env::var("NUM_JOBS").unwrap(); let _dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let opt = env::var("OPT_LEVEL").unwrap(); assert_eq!(opt, "0"); let opt = env::var("PROFILE").unwrap(); assert_eq!(opt, "debug"); let debug = env::var("DEBUG").unwrap(); assert_eq!(debug, "true"); let out = env::var("OUT_DIR").unwrap(); assert!(out.starts_with(r"{0}")); assert!(fs::metadata(&out).map(|m| m.is_dir()).unwrap_or(false)); let _host = env::var("HOST").unwrap(); let _feat = env::var("CARGO_FEATURE_FOO").unwrap(); let _cargo = env::var("CARGO").unwrap(); let rustc = env::var("RUSTC").unwrap(); assert_eq!(rustc, "rustc"); let rustdoc = env::var("RUSTDOC").unwrap(); assert_eq!(rustdoc, "rustdoc"); assert!(env::var("RUSTC_LINKER").is_err()); }} "#, p.root() .join("target") .join("debug") .join("build") .display() ); let p = p.file("bar/build.rs", &file_content).build(); assert_that( p.cargo("build").arg("--features").arg("bar_feat"), execs().with_status(0), ); } #[test] fn custom_build_env_var_rustc_linker() { if cross_compile::disabled() { return; } let target = cross_compile::alternate(); let p = project("foo") .file("Cargo.toml", r#" [project] name = "foo" version = "0.0.1" "# ) .file( ".cargo/config", &format!( r#" [target.{}] linker = "/path/to/linker" "#, target ) ) .file( "build.rs", r#" use std::env; fn main() { assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/linker")); } "# ) .file("src/lib.rs", "") .build(); // no crate type set => linker never called => build succeeds if and // only if build.rs succeeds, despite linker binary not existing. assert_that( p.cargo("build").arg("--target").arg(&target), execs().with_status(0), ); } #[test] fn custom_build_script_wrong_rustc_flags() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] build = "build.rs" "#, ) .file( "src/main.rs", r#" fn main() {} "#, ) .file( "build.rs", r#" fn main() { println!("cargo:rustc-flags=-aaa -bbb"); } "#, ) .build(); assert_that( p.cargo("build"), execs().with_status(101).with_stderr_contains(&format!( "\ [ERROR] Only `-l` and `-L` flags are allowed in build script of `foo v0.5.0 ({})`: \ `-aaa -bbb`", p.url() )), ); } /* #[test] fn custom_build_script_rustc_flags() { let p = project("foo") .file("Cargo.toml", r#" [project] name = "bar" version = "0.5.0" authors = ["wycats@example.com"] [dependencies.foo] path = "foo" "#) .file("src/main.rs", r#" fn main() {} "#) .file("foo/Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = ["wycats@example.com"] build = "build.rs" "#) .file("foo/src/lib.rs", r#" "#) .file("foo/build.rs", r#" fn main() { println!("cargo:rustc-flags=-l nonexistinglib -L /dummy/path1 -L /dummy/path2"); } "#) .build(); // TODO: TEST FAILS BECAUSE OF WRONG STDOUT (but otherwise, the build works) assert_that(p.cargo("build").arg("--verbose"), execs().with_status(101) .with_stderr(&format!("\ [COMPILING] bar v0.5.0 ({url}) [RUNNING] `rustc --crate-name test {dir}{sep}src{sep}lib.rs --crate-type lib -C debuginfo=2 \ -C metadata=[..] \ -C extra-filename=-[..] \ --out-dir {dir}{sep}target \ --emit=dep-info,link \ -L {dir}{sep}target \ -L {dir}{sep}target{sep}deps` ", sep = path::SEP, dir = p.root().display(), url = p.url(), ))); } */ #[test] fn links_no_build_cmd() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] links = "a" "#, ) .file("src/lib.rs", "") .build(); assert_that( p.cargo("build"), execs().with_status(101).with_stderr( "\ [ERROR] package `foo v0.5.0 (file://[..])` specifies that it links to `a` but does \ not have a custom build script ", ), ); } #[test] fn links_duplicates() { // this tests that the links_duplicates are caught at resolver time let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] links = "a" build = "build.rs" [dependencies.a-sys] path = "a-sys" "#, ) .file("src/lib.rs", "") .file("build.rs", "") .file( "a-sys/Cargo.toml", r#" [project] name = "a-sys" version = "0.5.0" authors = [] links = "a" build = "build.rs" "#, ) .file("a-sys/src/lib.rs", "") .file("a-sys/build.rs", "") .build(); assert_that(p.cargo("build"), execs().with_status(101) .with_stderr("\ error: failed to select a version for `a-sys`. ... required by package `foo v0.5.0 ([..])` versions that meet the requirements `*` are: 0.5.0 the package `a-sys` links to the native library `a`, but it conflicts with a previous package which links to `a` as well: package `foo v0.5.0 ([..])` failed to select a version for `a-sys` which could resolve this conflict ")); } #[test] fn links_duplicates_deep_dependency() { // this tests that the links_duplicates are caught at resolver time let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] links = "a" build = "build.rs" [dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file("build.rs", "") .file( "a/Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] build = "build.rs" [dependencies.a-sys] path = "a-sys" "#, ) .file("a/src/lib.rs", "") .file("a/build.rs", "") .file( "a/a-sys/Cargo.toml", r#" [project] name = "a-sys" version = "0.5.0" authors = [] links = "a" build = "build.rs" "#, ) .file("a/a-sys/src/lib.rs", "") .file("a/a-sys/build.rs", "") .build(); assert_that(p.cargo("build"), execs().with_status(101) .with_stderr("\ error: failed to select a version for `a-sys`. ... required by package `a v0.5.0 ([..])` ... which is depended on by `foo v0.5.0 ([..])` versions that meet the requirements `*` are: 0.5.0 the package `a-sys` links to the native library `a`, but it conflicts with a previous package which links to `a` as well: package `foo v0.5.0 ([..])` failed to select a version for `a-sys` which could resolve this conflict ")); } #[test] fn overrides_and_links() { let target = rustc_host(); let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" [dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; fn main() { assert_eq!(env::var("DEP_FOO_FOO").ok().expect("FOO missing"), "bar"); assert_eq!(env::var("DEP_FOO_BAR").ok().expect("BAR missing"), "baz"); } "#, ) .file( ".cargo/config", &format!( r#" [target.{}.foo] rustc-flags = "-L foo -L bar" foo = "bar" bar = "baz" "#, target ), ) .file( "a/Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] links = "foo" build = "build.rs" "#, ) .file("a/src/lib.rs", "") .file("a/build.rs", "not valid rust code") .build(); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [..] [..] [..] [..] [..] [RUNNING] `rustc --crate-name foo [..] -L foo -L bar` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); } #[test] fn unused_overrides() { let target = rustc_host(); let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .file( ".cargo/config", &format!( r#" [target.{}.foo] rustc-flags = "-L foo -L bar" foo = "bar" bar = "baz" "#, target ), ) .build(); assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); } #[test] fn links_passes_env_vars() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" [dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; fn main() { assert_eq!(env::var("DEP_FOO_FOO").unwrap(), "bar"); assert_eq!(env::var("DEP_FOO_BAR").unwrap(), "baz"); } "#, ) .file( "a/Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] links = "foo" build = "build.rs" "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", r#" use std::env; fn main() { let lib = env::var("CARGO_MANIFEST_LINKS").unwrap(); assert_eq!(lib, "foo"); println!("cargo:foo=bar"); println!("cargo:bar=baz"); } "#, ) .build(); assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); } #[test] fn only_rerun_build_script() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() {} "#, ) .build(); assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); p.root().move_into_the_past(); File::create(&p.root().join("some-new-file")).unwrap(); p.root().move_into_the_past(); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [COMPILING] foo v0.5.0 (file://[..]) [RUNNING] `[..][/]build-script-build` [RUNNING] `rustc --crate-name foo [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); } #[test] fn rebuild_continues_to_pass_env_vars() { let a = project("a") .file( "Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] links = "foo" build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::time::Duration; fn main() { println!("cargo:foo=bar"); println!("cargo:bar=baz"); std::thread::sleep(Duration::from_millis(500)); } "#, ) .build(); a.root().move_into_the_past(); let p = project("foo") .file( "Cargo.toml", &format!( r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" [dependencies.a] path = '{}' "#, a.root().display() ), ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; fn main() { assert_eq!(env::var("DEP_FOO_FOO").unwrap(), "bar"); assert_eq!(env::var("DEP_FOO_BAR").unwrap(), "baz"); } "#, ) .build(); assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); p.root().move_into_the_past(); File::create(&p.root().join("some-new-file")).unwrap(); p.root().move_into_the_past(); assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); } #[test] fn testing_and_such() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() {} "#, ) .build(); println!("build"); assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); p.root().move_into_the_past(); File::create(&p.root().join("src/lib.rs")).unwrap(); p.root().move_into_the_past(); println!("test"); assert_that( p.cargo("test").arg("-vj1"), execs() .with_status(0) .with_stderr( "\ [COMPILING] foo v0.5.0 (file://[..]) [RUNNING] `[..][/]build-script-build` [RUNNING] `rustc --crate-name foo [..]` [RUNNING] `rustc --crate-name foo [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] [RUNNING] `[..][/]foo-[..][EXE]` [DOCTEST] foo [RUNNING] `rustdoc --test [..]`", ) .with_stdout_contains_n("running 0 tests", 2), ); println!("doc"); assert_that( p.cargo("doc").arg("-v"), execs().with_status(0).with_stderr( "\ [DOCUMENTING] foo v0.5.0 (file://[..]) [RUNNING] `rustdoc [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); File::create(&p.root().join("src/main.rs")) .unwrap() .write_all(b"fn main() {}") .unwrap(); println!("run"); assert_that( p.cargo("run"), execs().with_status(0).with_stderr( "\ [COMPILING] foo v0.5.0 (file://[..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] [RUNNING] `target[/]debug[/]foo[EXE]` ", ), ); } #[test] fn propagation_of_l_flags() { let target = rustc_host(); let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] [dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] links = "bar" build = "build.rs" [dependencies.b] path = "../b" "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", r#" fn main() { println!("cargo:rustc-flags=-L bar"); } "#, ) .file( "b/Cargo.toml", r#" [project] name = "b" version = "0.5.0" authors = [] links = "foo" build = "build.rs" "#, ) .file("b/src/lib.rs", "") .file("b/build.rs", "bad file") .file( ".cargo/config", &format!( r#" [target.{}.foo] rustc-flags = "-L foo" "#, target ), ) .build(); assert_that( p.cargo("build").arg("-v").arg("-j1"), execs().with_status(0).with_stderr_contains( "\ [RUNNING] `rustc --crate-name a [..] -L bar[..]-L foo[..]` [COMPILING] foo v0.5.0 (file://[..]) [RUNNING] `rustc --crate-name foo [..] -L bar -L foo` ", ), ); } #[test] fn propagation_of_l_flags_new() { let target = rustc_host(); let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] [dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] links = "bar" build = "build.rs" [dependencies.b] path = "../b" "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", r#" fn main() { println!("cargo:rustc-link-search=bar"); } "#, ) .file( "b/Cargo.toml", r#" [project] name = "b" version = "0.5.0" authors = [] links = "foo" build = "build.rs" "#, ) .file("b/src/lib.rs", "") .file("b/build.rs", "bad file") .file( ".cargo/config", &format!( r#" [target.{}.foo] rustc-link-search = ["foo"] "#, target ), ) .build(); assert_that( p.cargo("build").arg("-v").arg("-j1"), execs().with_status(0).with_stderr_contains( "\ [RUNNING] `rustc --crate-name a [..] -L bar[..]-L foo[..]` [COMPILING] foo v0.5.0 (file://[..]) [RUNNING] `rustc --crate-name foo [..] -L bar -L foo` ", ), ); } #[test] fn build_deps_simple() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" [build-dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "build.rs", " #[allow(unused_extern_crates)] extern crate a; fn main() {} ", ) .file( "a/Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] "#, ) .file("a/src/lib.rs", "") .build(); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [COMPILING] a v0.5.0 (file://[..]) [RUNNING] `rustc --crate-name a [..]` [COMPILING] foo v0.5.0 (file://[..]) [RUNNING] `rustc [..] build.rs [..] --extern a=[..]` [RUNNING] `[..][/]foo-[..][/]build-script-build` [RUNNING] `rustc --crate-name foo [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); } #[test] fn build_deps_not_for_normal() { let target = rustc_host(); let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" [build-dependencies.aaaaa] path = "a" "#, ) .file( "src/lib.rs", "#[allow(unused_extern_crates)] extern crate aaaaa;", ) .file( "build.rs", " #[allow(unused_extern_crates)] extern crate aaaaa; fn main() {} ", ) .file( "a/Cargo.toml", r#" [project] name = "aaaaa" version = "0.5.0" authors = [] "#, ) .file("a/src/lib.rs", "") .build(); assert_that( p.cargo("build").arg("-v").arg("--target").arg(&target), execs() .with_status(101) .with_stderr_contains("[..]can't find crate for `aaaaa`[..]") .with_stderr_contains( "\ [ERROR] Could not compile `foo`. Caused by: process didn't exit successfully: [..] ", ), ); } #[test] fn build_cmd_with_a_build_cmd() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" [build-dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "build.rs", " #[allow(unused_extern_crates)] extern crate a; fn main() {} ", ) .file( "a/Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] build = "build.rs" [build-dependencies.b] path = "../b" "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", "#[allow(unused_extern_crates)] extern crate b; fn main() {}", ) .file( "b/Cargo.toml", r#" [project] name = "b" version = "0.5.0" authors = [] "#, ) .file("b/src/lib.rs", "") .build(); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [COMPILING] b v0.5.0 (file://[..]) [RUNNING] `rustc --crate-name b [..]` [COMPILING] a v0.5.0 (file://[..]) [RUNNING] `rustc [..] a[/]build.rs [..] --extern b=[..]` [RUNNING] `[..][/]a-[..][/]build-script-build` [RUNNING] `rustc --crate-name a [..]lib.rs --crate-type lib \ --emit=dep-info,link -C debuginfo=2 \ -C metadata=[..] \ --out-dir [..]target[/]debug[/]deps \ -L [..]target[/]debug[/]deps` [COMPILING] foo v0.5.0 (file://[..]) [RUNNING] `rustc --crate-name build_script_build build.rs --crate-type bin \ --emit=dep-info,link \ -C debuginfo=2 -C metadata=[..] --out-dir [..] \ -L [..]target[/]debug[/]deps \ --extern a=[..]liba[..].rlib` [RUNNING] `[..][/]foo-[..][/]build-script-build` [RUNNING] `rustc --crate-name foo [..]lib.rs --crate-type lib \ --emit=dep-info,link -C debuginfo=2 \ -C metadata=[..] \ --out-dir [..] \ -L [..]target[/]debug[/]deps` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); } #[test] fn out_dir_is_preserved() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; use std::fs::File; use std::path::Path; fn main() { let out = env::var("OUT_DIR").unwrap(); File::create(Path::new(&out).join("foo")).unwrap(); } "#, ) .build(); // Make the file assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); p.root().move_into_the_past(); // Change to asserting that it's there File::create(&p.root().join("build.rs")) .unwrap() .write_all( br#" use std::env; use std::old_io::File; fn main() { let out = env::var("OUT_DIR").unwrap(); File::open(&Path::new(&out).join("foo")).unwrap(); } "#, ) .unwrap(); p.root().move_into_the_past(); assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); // Run a fresh build where file should be preserved assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); // One last time to make sure it's still there. File::create(&p.root().join("foo")).unwrap(); assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); } #[test] fn output_separate_lines() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo:rustc-flags=-L foo"); println!("cargo:rustc-flags=-l static=foo"); } "#, ) .build(); assert_that( p.cargo("build").arg("-v"), execs().with_status(101).with_stderr_contains( "\ [COMPILING] foo v0.5.0 (file://[..]) [RUNNING] `rustc [..] build.rs [..]` [RUNNING] `[..][/]foo-[..][/]build-script-build` [RUNNING] `rustc --crate-name foo [..] -L foo -l static=foo` [ERROR] could not find native static library [..] ", ), ); } #[test] fn output_separate_lines_new() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo:rustc-link-search=foo"); println!("cargo:rustc-link-lib=static=foo"); } "#, ) .build(); assert_that( p.cargo("build").arg("-v"), execs().with_status(101).with_stderr_contains( "\ [COMPILING] foo v0.5.0 (file://[..]) [RUNNING] `rustc [..] build.rs [..]` [RUNNING] `[..][/]foo-[..][/]build-script-build` [RUNNING] `rustc --crate-name foo [..] -L foo -l static=foo` [ERROR] could not find native static library [..] ", ), ); } #[cfg(not(windows))] // FIXME(#867) #[test] fn code_generation() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file( "src/main.rs", r#" include!(concat!(env!("OUT_DIR"), "/hello.rs")); fn main() { println!("{}", message()); } "#, ) .file( "build.rs", r#" use std::env; use std::fs::File; use std::io::prelude::*; use std::path::PathBuf; fn main() { let dst = PathBuf::from(env::var("OUT_DIR").unwrap()); let mut f = File::create(&dst.join("hello.rs")).unwrap(); f.write_all(b" pub fn message() -> &'static str { \"Hello, World!\" } ").unwrap(); } "#, ) .build(); assert_that( p.cargo("run"), execs() .with_status(0) .with_stderr( "\ [COMPILING] foo v0.5.0 (file://[..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] [RUNNING] `target[/]debug[/]foo`", ) .with_stdout("Hello, World!"), ); assert_that(p.cargo("test"), execs().with_status(0)); } #[test] fn release_with_build_script() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() {} "#, ) .build(); assert_that( p.cargo("build").arg("-v").arg("--release"), execs().with_status(0), ); } #[test] fn build_script_only() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.0.0" authors = [] build = "build.rs" "#, ) .file("build.rs", r#"fn main() {}"#) .build(); assert_that( p.cargo("build").arg("-v"), execs().with_status(101).with_stderr( "\ [ERROR] failed to parse manifest at `[..]` Caused by: no targets specified in the manifest either src/lib.rs, src/main.rs, a [lib] section, or [[bin]] section must be present", ), ); } #[test] fn shared_dep_with_a_build_script() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" [dependencies.a] path = "a" [build-dependencies.b] path = "b" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file("a/build.rs", "fn main() {}") .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.5.0" authors = [] [dependencies.a] path = "../a" "#, ) .file("b/src/lib.rs", "") .build(); assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); } #[test] fn transitive_dep_host() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" [build-dependencies.b] path = "b" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.5.0" authors = [] links = "foo" build = "build.rs" "#, ) .file("a/build.rs", "fn main() {}") .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.5.0" authors = [] [lib] name = "b" plugin = true [dependencies.a] path = "../a" "#, ) .file("b/src/lib.rs", "") .build(); assert_that(p.cargo("build"), execs().with_status(0)); } #[test] fn test_a_lib_with_a_build_command() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file( "src/lib.rs", r#" include!(concat!(env!("OUT_DIR"), "/foo.rs")); /// ``` /// foo::bar(); /// ``` pub fn bar() { assert_eq!(foo(), 1); } "#, ) .file( "build.rs", r#" use std::env; use std::io::prelude::*; use std::fs::File; use std::path::PathBuf; fn main() { let out = PathBuf::from(env::var("OUT_DIR").unwrap()); File::create(out.join("foo.rs")).unwrap().write_all(b" fn foo() -> i32 { 1 } ").unwrap(); } "#, ) .build(); assert_that(p.cargo("test"), execs().with_status(0)); } #[test] fn test_dev_dep_build_script() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] [dev-dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file("a/build.rs", "fn main() {}") .file("a/src/lib.rs", "") .build(); assert_that(p.cargo("test"), execs().with_status(0)); } #[test] fn build_script_with_dynamic_native_dependency() { let _workspace = project("ws") .file( "Cargo.toml", r#" [workspace] members = ["builder", "foo"] "#, ) .build(); let build = project("ws/builder") .file( "Cargo.toml", r#" [package] name = "builder" version = "0.0.1" authors = [] [lib] name = "builder" crate-type = ["dylib"] plugin = true "#, ) .file( "src/lib.rs", r#" #[no_mangle] pub extern fn foo() {} "#, ) .build(); let foo = project("ws/foo") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] build = "build.rs" [build-dependencies.bar] path = "bar" "#, ) .file( "build.rs", r#" extern crate bar; fn main() { bar::bar() } "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] build = "build.rs" "#, ) .file( "bar/build.rs", r#" use std::env; use std::path::PathBuf; fn main() { let src = PathBuf::from(env::var("SRC").unwrap()); println!("cargo:rustc-link-search=native={}/target/debug/deps", src.display()); } "#, ) .file( "bar/src/lib.rs", r#" pub fn bar() { #[cfg_attr(not(target_env = "msvc"), link(name = "builder"))] #[cfg_attr(target_env = "msvc", link(name = "builder.dll"))] extern { fn foo(); } unsafe { foo() } } "#, ) .build(); assert_that( build .cargo("build") .arg("-v") .env("RUST_LOG", "cargo::ops::cargo_rustc"), execs().with_status(0), ); assert_that( foo.cargo("build") .arg("-v") .env("SRC", build.root()) .env("RUST_LOG", "cargo::ops::cargo_rustc"), execs().with_status(0), ); } #[test] fn profile_and_opt_level_set_correctly() { let build = project("builder") .file( "Cargo.toml", r#" [package] name = "builder" version = "0.0.1" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; fn main() { assert_eq!(env::var("OPT_LEVEL").unwrap(), "3"); assert_eq!(env::var("PROFILE").unwrap(), "release"); assert_eq!(env::var("DEBUG").unwrap(), "false"); } "#, ) .build(); assert_that(build.cargo("bench"), execs().with_status(0)); } #[test] fn profile_debug_0() { let p = project("foo") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" [profile.dev] debug = 0 "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; fn main() { assert_eq!(env::var("OPT_LEVEL").unwrap(), "0"); assert_eq!(env::var("PROFILE").unwrap(), "debug"); assert_eq!(env::var("DEBUG").unwrap(), "false"); } "#, ) .build(); assert_that(p.cargo("build"), execs().with_status(0)); } #[test] fn build_script_with_lto() { let build = project("builder") .file( "Cargo.toml", r#" [package] name = "builder" version = "0.0.1" authors = [] build = "build.rs" [profile.dev] lto = true "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { } "#, ) .build(); assert_that(build.cargo("build"), execs().with_status(0)); } #[test] fn test_duplicate_deps() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.1.0" authors = [] build = "build.rs" [dependencies.bar] path = "bar" [build-dependencies.bar] path = "bar" "#, ) .file( "src/main.rs", r#" extern crate bar; fn main() { bar::do_nothing() } "#, ) .file( "build.rs", r#" extern crate bar; fn main() { bar::do_nothing() } "#, ) .file( "bar/Cargo.toml", r#" [project] name = "bar" version = "0.1.0" authors = [] "#, ) .file("bar/src/lib.rs", "pub fn do_nothing() {}") .build(); assert_that(p.cargo("build"), execs().with_status(0)); } #[test] fn cfg_feedback() { let build = project("builder") .file( "Cargo.toml", r#" [package] name = "builder" version = "0.0.1" authors = [] build = "build.rs" "#, ) .file( "src/main.rs", " #[cfg(foo)] fn main() {} ", ) .file( "build.rs", r#" fn main() { println!("cargo:rustc-cfg=foo"); } "#, ) .build(); assert_that(build.cargo("build").arg("-v"), execs().with_status(0)); } #[test] fn cfg_override() { let target = rustc_host(); let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] links = "a" build = "build.rs" "#, ) .file( "src/main.rs", " #[cfg(foo)] fn main() {} ", ) .file("build.rs", "") .file( ".cargo/config", &format!( r#" [target.{}.a] rustc-cfg = ["foo"] "#, target ), ) .build(); assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); } #[test] fn cfg_test() { let p = project("foo") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] build = "build.rs" "#, ) .file( "build.rs", r#" fn main() { println!("cargo:rustc-cfg=foo"); } "#, ) .file( "src/lib.rs", r#" /// /// ``` /// extern crate foo; /// /// fn main() { /// foo::foo() /// } /// ``` /// #[cfg(foo)] pub fn foo() {} #[cfg(foo)] #[test] fn test_foo() { foo() } "#, ) .file( "tests/test.rs", r#" #[cfg(foo)] #[test] fn test_bar() {} "#, ) .build(); assert_that( p.cargo("test").arg("-v"), execs() .with_stderr(format!( "\ [COMPILING] foo v0.0.1 ({dir}) [RUNNING] [..] build.rs [..] [RUNNING] `[..][/]build-script-build` [RUNNING] [..] --cfg foo[..] [RUNNING] [..] --cfg foo[..] [RUNNING] [..] --cfg foo[..] [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] [RUNNING] `[..][/]foo-[..][EXE]` [RUNNING] `[..][/]test-[..][EXE]` [DOCTEST] foo [RUNNING] [..] --cfg foo[..]", dir = p.url() )) .with_stdout_contains("test test_foo ... ok") .with_stdout_contains("test test_bar ... ok") .with_stdout_contains_n("test [..] ... ok", 3), ); } #[test] fn cfg_doc() { let p = project("foo") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] build = "build.rs" [dependencies.bar] path = "bar" "#, ) .file( "build.rs", r#" fn main() { println!("cargo:rustc-cfg=foo"); } "#, ) .file( "src/lib.rs", r#" #[cfg(foo)] pub fn foo() {} "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] build = "build.rs" "#, ) .file( "bar/build.rs", r#" fn main() { println!("cargo:rustc-cfg=bar"); } "#, ) .file( "bar/src/lib.rs", r#" #[cfg(bar)] pub fn bar() {} "#, ) .build(); assert_that(p.cargo("doc"), execs().with_status(0)); assert_that(&p.root().join("target/doc"), existing_dir()); assert_that( &p.root().join("target/doc/foo/fn.foo.html"), existing_file(), ); assert_that( &p.root().join("target/doc/bar/fn.bar.html"), existing_file(), ); } #[test] fn cfg_override_test() { let p = project("foo") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] build = "build.rs" links = "a" "#, ) .file("build.rs", "") .file( ".cargo/config", &format!( r#" [target.{}.a] rustc-cfg = ["foo"] "#, rustc_host() ), ) .file( "src/lib.rs", r#" /// /// ``` /// extern crate foo; /// /// fn main() { /// foo::foo() /// } /// ``` /// #[cfg(foo)] pub fn foo() {} #[cfg(foo)] #[test] fn test_foo() { foo() } "#, ) .file( "tests/test.rs", r#" #[cfg(foo)] #[test] fn test_bar() {} "#, ) .build(); assert_that( p.cargo("test").arg("-v"), execs() .with_stderr(format!( "\ [COMPILING] foo v0.0.1 ({dir}) [RUNNING] `[..]` [RUNNING] `[..]` [RUNNING] `[..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] [RUNNING] `[..][/]foo-[..][EXE]` [RUNNING] `[..][/]test-[..][EXE]` [DOCTEST] foo [RUNNING] [..] --cfg foo[..]", dir = p.url() )) .with_stdout_contains("test test_foo ... ok") .with_stdout_contains("test test_bar ... ok") .with_stdout_contains_n("test [..] ... ok", 3), ); } #[test] fn cfg_override_doc() { let p = project("foo") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] build = "build.rs" links = "a" [dependencies.bar] path = "bar" "#, ) .file( ".cargo/config", &format!( r#" [target.{target}.a] rustc-cfg = ["foo"] [target.{target}.b] rustc-cfg = ["bar"] "#, target = rustc_host() ), ) .file("build.rs", "") .file( "src/lib.rs", r#" #[cfg(foo)] pub fn foo() {} "#, ) .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" authors = [] build = "build.rs" links = "b" "#, ) .file("bar/build.rs", "") .file( "bar/src/lib.rs", r#" #[cfg(bar)] pub fn bar() {} "#, ) .build(); assert_that(p.cargo("doc"), execs().with_status(0)); assert_that(&p.root().join("target/doc"), existing_dir()); assert_that( &p.root().join("target/doc/foo/fn.foo.html"), existing_file(), ); assert_that( &p.root().join("target/doc/bar/fn.bar.html"), existing_file(), ); } #[test] fn env_build() { let p = project("foo") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] build = "build.rs" "#, ) .file( "src/main.rs", r#" const FOO: &'static str = env!("FOO"); fn main() { println!("{}", FOO); } "#, ) .file( "build.rs", r#" fn main() { println!("cargo:rustc-env=FOO=foo"); } "#, ) .build(); assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); assert_that( p.cargo("run").arg("-v"), execs().with_status(0).with_stdout("foo\n"), ); } #[test] fn env_test() { let p = project("foo") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] build = "build.rs" "#, ) .file( "build.rs", r#" fn main() { println!("cargo:rustc-env=FOO=foo"); } "#, ) .file( "src/lib.rs", r#" pub const FOO: &'static str = env!("FOO"); "#, ) .file( "tests/test.rs", r#" extern crate foo; #[test] fn test_foo() { assert_eq!("foo", foo::FOO); } "#, ) .build(); assert_that( p.cargo("test").arg("-v"), execs() .with_stderr(format!( "\ [COMPILING] foo v0.0.1 ({dir}) [RUNNING] [..] build.rs [..] [RUNNING] `[..][/]build-script-build` [RUNNING] [..] --crate-name foo[..] [RUNNING] [..] --crate-name foo[..] [RUNNING] [..] --crate-name test[..] [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] [RUNNING] `[..][/]foo-[..][EXE]` [RUNNING] `[..][/]test-[..][EXE]` [DOCTEST] foo [RUNNING] [..] --crate-name foo[..]", dir = p.url() )) .with_stdout_contains_n("running 0 tests", 2) .with_stdout_contains("test test_foo ... ok"), ); } #[test] fn env_doc() { let p = project("foo") .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" authors = [] build = "build.rs" "#, ) .file( "src/main.rs", r#" const FOO: &'static str = env!("FOO"); fn main() {} "#, ) .file( "build.rs", r#" fn main() { println!("cargo:rustc-env=FOO=foo"); } "#, ) .build(); assert_that(p.cargo("doc").arg("-v"), execs().with_status(0)); } #[test] fn flags_go_into_tests() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] [dependencies] b = { path = "b" } "#, ) .file("src/lib.rs", "") .file("tests/foo.rs", "") .file( "b/Cargo.toml", r#" [project] name = "b" version = "0.5.0" authors = [] [dependencies] a = { path = "../a" } "#, ) .file("b/src/lib.rs", "") .file( "a/Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", r#" fn main() { println!("cargo:rustc-link-search=test"); } "#, ) .build(); assert_that( p.cargo("test").arg("-v").arg("--test=foo"), execs() .with_status(0) .with_stderr( "\ [COMPILING] a v0.5.0 ([..] [RUNNING] `rustc [..] a[/]build.rs [..]` [RUNNING] `[..][/]build-script-build` [RUNNING] `rustc [..] a[/]src[/]lib.rs [..] -L test[..]` [COMPILING] b v0.5.0 ([..] [RUNNING] `rustc [..] b[/]src[/]lib.rs [..] -L test[..]` [COMPILING] foo v0.5.0 ([..] [RUNNING] `rustc [..] src[/]lib.rs [..] -L test[..]` [RUNNING] `rustc [..] tests[/]foo.rs [..] -L test[..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] [RUNNING] `[..][/]foo-[..][EXE]`", ) .with_stdout_contains("running 0 tests"), ); assert_that( p.cargo("test").arg("-v").arg("-pb").arg("--lib"), execs() .with_status(0) .with_stderr( "\ [FRESH] a v0.5.0 ([..] [COMPILING] b v0.5.0 ([..] [RUNNING] `rustc [..] b[/]src[/]lib.rs [..] -L test[..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] [RUNNING] `[..][/]b-[..][EXE]`", ) .with_stdout_contains("running 0 tests"), ); } #[test] fn diamond_passes_args_only_once() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] [dependencies] a = { path = "a" } b = { path = "b" } "#, ) .file("src/lib.rs", "") .file("tests/foo.rs", "") .file( "a/Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] [dependencies] b = { path = "../b" } c = { path = "../c" } "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [project] name = "b" version = "0.5.0" authors = [] [dependencies] c = { path = "../c" } "#, ) .file("b/src/lib.rs", "") .file( "c/Cargo.toml", r#" [project] name = "c" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file( "c/build.rs", r#" fn main() { println!("cargo:rustc-link-search=native=test"); } "#, ) .file("c/src/lib.rs", "") .build(); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [COMPILING] c v0.5.0 ([..] [RUNNING] `rustc [..]` [RUNNING] `[..]` [RUNNING] `rustc [..]` [COMPILING] b v0.5.0 ([..] [RUNNING] `rustc [..]` [COMPILING] a v0.5.0 ([..] [RUNNING] `rustc [..]` [COMPILING] foo v0.5.0 ([..] [RUNNING] `[..]rlib -L native=test` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); } #[test] fn adding_an_override_invalidates() { let target = rustc_host(); let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] links = "foo" build = "build.rs" "#, ) .file("src/lib.rs", "") .file(".cargo/config", "") .file( "build.rs", r#" fn main() { println!("cargo:rustc-link-search=native=foo"); } "#, ) .build(); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [COMPILING] foo v0.5.0 ([..] [RUNNING] `rustc [..]` [RUNNING] `[..]` [RUNNING] `rustc [..] -L native=foo` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); File::create(p.root().join(".cargo/config")) .unwrap() .write_all( format!( " [target.{}.foo] rustc-link-search = [\"native=bar\"] ", target ).as_bytes(), ) .unwrap(); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [COMPILING] foo v0.5.0 ([..] [RUNNING] `rustc [..] -L native=bar` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); } #[test] fn changing_an_override_invalidates() { let target = rustc_host(); let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] links = "foo" build = "build.rs" "#, ) .file("src/lib.rs", "") .file( ".cargo/config", &format!( " [target.{}.foo] rustc-link-search = [\"native=foo\"] ", target ), ) .file("build.rs", "") .build(); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [COMPILING] foo v0.5.0 ([..] [RUNNING] `rustc [..] -L native=foo` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); File::create(p.root().join(".cargo/config")) .unwrap() .write_all( format!( " [target.{}.foo] rustc-link-search = [\"native=bar\"] ", target ).as_bytes(), ) .unwrap(); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [COMPILING] foo v0.5.0 ([..] [RUNNING] `rustc [..] -L native=bar` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); } #[test] fn fresh_builds_possible_with_link_libs() { // The bug is non-deterministic. Sometimes you can get a fresh build let target = rustc_host(); let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] links = "nativefoo" build = "build.rs" "#, ) .file("src/lib.rs", "") .file( ".cargo/config", &format!( " [target.{}.nativefoo] rustc-link-lib = [\"a\"] rustc-link-search = [\"./b\"] rustc-flags = \"-l z -L ./\" ", target ), ) .file("build.rs", "") .build(); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [COMPILING] foo v0.5.0 ([..] [RUNNING] `rustc [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); assert_that( p.cargo("build") .arg("-v") .env("RUST_LOG", "cargo::ops::cargo_rustc::fingerprint=info"), execs().with_status(0).with_stderr( "\ [FRESH] foo v0.5.0 ([..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); } #[test] fn fresh_builds_possible_with_multiple_metadata_overrides() { // The bug is non-deterministic. Sometimes you can get a fresh build let target = rustc_host(); let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] links = "foo" build = "build.rs" "#, ) .file("src/lib.rs", "") .file( ".cargo/config", &format!( " [target.{}.foo] a = \"\" b = \"\" c = \"\" d = \"\" e = \"\" ", target ), ) .file("build.rs", "") .build(); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [COMPILING] foo v0.5.0 ([..] [RUNNING] `rustc [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); assert_that( p.cargo("build") .arg("-v") .env("RUST_LOG", "cargo::ops::cargo_rustc::fingerprint=info"), execs().with_status(0).with_stderr( "\ [FRESH] foo v0.5.0 ([..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); } #[test] fn rebuild_only_on_explicit_paths() { let p = project("a") .file( "Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo:rerun-if-changed=foo"); println!("cargo:rerun-if-changed=bar"); } "#, ) .build(); assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); // files don't exist, so should always rerun if they don't exist println!("run without"); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [COMPILING] a v0.5.0 ([..]) [RUNNING] `[..][/]build-script-build` [RUNNING] `rustc [..] src[/]lib.rs [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); sleep_ms(1000); File::create(p.root().join("foo")).unwrap(); File::create(p.root().join("bar")).unwrap(); // now the exist, so run once, catch the mtime, then shouldn't run again println!("run with"); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [COMPILING] a v0.5.0 ([..]) [RUNNING] `[..][/]build-script-build` [RUNNING] `rustc [..] src[/]lib.rs [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); println!("run with2"); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [FRESH] a v0.5.0 ([..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); sleep_ms(1000); // random other files do not affect freshness println!("run baz"); File::create(p.root().join("baz")).unwrap(); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [FRESH] a v0.5.0 ([..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); // but changing dependent files does println!("run foo change"); File::create(p.root().join("foo")).unwrap(); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [COMPILING] a v0.5.0 ([..]) [RUNNING] `[..][/]build-script-build` [RUNNING] `rustc [..] src[/]lib.rs [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); // .. as does deleting a file println!("run foo delete"); fs::remove_file(p.root().join("bar")).unwrap(); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [COMPILING] a v0.5.0 ([..]) [RUNNING] `[..][/]build-script-build` [RUNNING] `rustc [..] src[/]lib.rs [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); } #[test] fn doctest_receives_build_link_args() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] [dependencies.a] path = "a" "#, ) .file("src/lib.rs", "") .file( "a/Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] links = "bar" build = "build.rs" "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", r#" fn main() { println!("cargo:rustc-link-search=native=bar"); } "#, ) .build(); assert_that( p.cargo("test").arg("-v"), execs().with_status(0).with_stderr_contains( "[RUNNING] `rustdoc --test [..] --crate-name foo [..]-L native=bar[..]`", ), ); } #[test] fn please_respect_the_dag() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" [dependencies] a = { path = 'a' } "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo:rustc-link-search=native=foo"); } "#, ) .file( "a/Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] links = "bar" build = "build.rs" "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", r#" fn main() { println!("cargo:rustc-link-search=native=bar"); } "#, ) .build(); assert_that( p.cargo("build").arg("-v"), execs() .with_status(0) .with_stderr_contains("[RUNNING] `rustc [..] -L native=foo -L native=bar[..]`"), ); } #[test] fn non_utf8_output() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file( "build.rs", r#" use std::io::prelude::*; fn main() { let mut out = std::io::stdout(); // print something that's not utf8 out.write_all(b"\xff\xff\n").unwrap(); // now print some cargo metadata that's utf8 println!("cargo:rustc-cfg=foo"); // now print more non-utf8 out.write_all(b"\xff\xff\n").unwrap(); } "#, ) .file( "src/main.rs", r#" #[cfg(foo)] fn main() {} "#, ) .build(); assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); } #[test] fn custom_target_dir() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] [dependencies] a = { path = "a" } "#, ) .file("src/lib.rs", "") .file( ".cargo/config", r#" [build] target-dir = 'test' "#, ) .file( "a/Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file("a/build.rs", "fn main() {}") .file("a/src/lib.rs", "") .build(); assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); } #[test] fn panic_abort_with_build_scripts() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] [profile.release] panic = 'abort' [dependencies] a = { path = "a" } "#, ) .file( "src/lib.rs", "#[allow(unused_extern_crates)] extern crate a;", ) .file( "a/Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] build = "build.rs" [build-dependencies] b = { path = "../b" } "#, ) .file("a/src/lib.rs", "") .file( "a/build.rs", "#[allow(unused_extern_crates)] extern crate b; fn main() {}", ) .file( "b/Cargo.toml", r#" [project] name = "b" version = "0.5.0" authors = [] "#, ) .file("b/src/lib.rs", "") .build(); assert_that( p.cargo("build").arg("-v").arg("--release"), execs().with_status(0), ); } #[test] fn warnings_emitted() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo:warning=foo"); println!("cargo:warning=bar"); } "#, ) .build(); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [COMPILING] foo v0.5.0 ([..]) [RUNNING] `rustc [..]` [RUNNING] `[..]` warning: foo warning: bar [RUNNING] `rustc [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); } #[test] fn warnings_hidden_for_upstream() { Package::new("bar", "0.1.0") .file( "build.rs", r#" fn main() { println!("cargo:warning=foo"); println!("cargo:warning=bar"); } "#, ) .file( "Cargo.toml", r#" [project] name = "bar" version = "0.1.0" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .publish(); let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] [dependencies] bar = "*" "#, ) .file("src/lib.rs", "") .build(); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr( "\ [UPDATING] registry `[..]` [DOWNLOADING] bar v0.1.0 ([..]) [COMPILING] bar v0.1.0 [RUNNING] `rustc [..]` [RUNNING] `[..]` [RUNNING] `rustc [..]` [COMPILING] foo v0.5.0 ([..]) [RUNNING] `rustc [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); } #[test] fn warnings_printed_on_vv() { Package::new("bar", "0.1.0") .file( "build.rs", r#" fn main() { println!("cargo:warning=foo"); println!("cargo:warning=bar"); } "#, ) .file( "Cargo.toml", r#" [project] name = "bar" version = "0.1.0" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .publish(); let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] [dependencies] bar = "*" "#, ) .file("src/lib.rs", "") .build(); assert_that( p.cargo("build").arg("-vv"), execs().with_status(0).with_stderr( "\ [UPDATING] registry `[..]` [DOWNLOADING] bar v0.1.0 ([..]) [COMPILING] bar v0.1.0 [RUNNING] `rustc [..]` [RUNNING] `[..]` warning: foo warning: bar [RUNNING] `rustc [..]` [COMPILING] foo v0.5.0 ([..]) [RUNNING] `rustc [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); } #[test] fn output_shows_on_vv() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::io::prelude::*; fn main() { std::io::stderr().write_all(b"stderr\n").unwrap(); std::io::stdout().write_all(b"stdout\n").unwrap(); } "#, ) .build(); assert_that( p.cargo("build").arg("-vv"), execs().with_status(0).with_stdout("stdout").with_stderr( "\ [COMPILING] foo v0.5.0 ([..]) [RUNNING] `rustc [..]` [RUNNING] `[..]` stderr [RUNNING] `rustc [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ), ); } #[test] fn links_with_dots() { let target = rustc_host(); let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] build = "build.rs" links = "a.b" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cargo:rustc-link-search=bar") } "#, ) .file( ".cargo/config", &format!( r#" [target.{}.'a.b'] rustc-link-search = ["foo"] "#, target ), ) .build(); assert_that( p.cargo("build").arg("-v"), execs() .with_status(0) .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..] [..] -L foo[..]`"), ); } #[test] fn rustc_and_rustdoc_set_correctly() { let p = project("builder") .file( "Cargo.toml", r#" [package] name = "builder" version = "0.0.1" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; fn main() { assert_eq!(env::var("RUSTC").unwrap(), "rustc"); assert_eq!(env::var("RUSTDOC").unwrap(), "rustdoc"); } "#, ) .build(); assert_that(p.cargo("bench"), execs().with_status(0)); } #[test] fn cfg_env_vars_available() { let p = project("builder") .file( "Cargo.toml", r#" [package] name = "builder" version = "0.0.1" authors = [] build = "build.rs" "#, ) .file("src/lib.rs", "") .file( "build.rs", r#" use std::env; fn main() { let fam = env::var("CARGO_CFG_TARGET_FAMILY").unwrap(); if cfg!(unix) { assert_eq!(fam, "unix"); } else { assert_eq!(fam, "windows"); } } "#, ) .build(); assert_that(p.cargo("bench"), execs().with_status(0)); } #[test] fn switch_features_rerun() { let p = project("builder") .file( "Cargo.toml", r#" [package] name = "builder" version = "0.0.1" authors = [] build = "build.rs" [features] foo = [] "#, ) .file( "src/main.rs", r#" fn main() { println!(include_str!(concat!(env!("OUT_DIR"), "/output"))); } "#, ) .file( "build.rs", r#" use std::env; use std::fs::File; use std::io::Write; use std::path::Path; fn main() { let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = Path::new(&out_dir).join("output"); let mut f = File::create(&out_dir).unwrap(); if env::var_os("CARGO_FEATURE_FOO").is_some() { f.write_all(b"foo").unwrap(); } else { f.write_all(b"bar").unwrap(); } } "#, ) .build(); assert_that( p.cargo("run").arg("-v").arg("--features=foo"), execs().with_status(0).with_stdout("foo\n"), ); assert_that( p.cargo("run").arg("-v"), execs().with_status(0).with_stdout("bar\n"), ); assert_that( p.cargo("run").arg("-v").arg("--features=foo"), execs().with_status(0).with_stdout("foo\n"), ); } #[test] fn assume_build_script_when_build_rs_present() { let p = project("builder") .file( "Cargo.toml", r#" [package] name = "builder" version = "0.0.1" authors = [] "#, ) .file( "src/main.rs", r#" fn main() { if ! cfg!(foo) { panic!("the build script was not run"); } } "#, ) .file( "build.rs", r#" fn main() { println!("cargo:rustc-cfg=foo"); } "#, ) .build(); assert_that(p.cargo("run").arg("-v"), execs().with_status(0)); } #[test] fn if_build_set_to_false_dont_treat_build_rs_as_build_script() { let p = project("builder") .file( "Cargo.toml", r#" [package] name = "builder" version = "0.0.1" authors = [] build = false "#, ) .file( "src/main.rs", r#" fn main() { if cfg!(foo) { panic!("the build script was run"); } } "#, ) .file( "build.rs", r#" fn main() { println!("cargo:rustc-cfg=foo"); } "#, ) .build(); assert_that(p.cargo("run").arg("-v"), execs().with_status(0)); } #[test] fn deterministic_rustc_dependency_flags() { // This bug is non-deterministic hence the large number of dependencies // in the hopes it will have a much higher chance of triggering it. Package::new("dep1", "0.1.0") .file( "Cargo.toml", r#" [project] name = "dep1" version = "0.1.0" authors = [] build = "build.rs" "#, ) .file( "build.rs", r#" fn main() { println!("cargo:rustc-flags=-L native=test1"); } "#, ) .file("src/lib.rs", "") .publish(); Package::new("dep2", "0.1.0") .file( "Cargo.toml", r#" [project] name = "dep2" version = "0.1.0" authors = [] build = "build.rs" "#, ) .file( "build.rs", r#" fn main() { println!("cargo:rustc-flags=-L native=test2"); } "#, ) .file("src/lib.rs", "") .publish(); Package::new("dep3", "0.1.0") .file( "Cargo.toml", r#" [project] name = "dep3" version = "0.1.0" authors = [] build = "build.rs" "#, ) .file( "build.rs", r#" fn main() { println!("cargo:rustc-flags=-L native=test3"); } "#, ) .file("src/lib.rs", "") .publish(); Package::new("dep4", "0.1.0") .file( "Cargo.toml", r#" [project] name = "dep4" version = "0.1.0" authors = [] build = "build.rs" "#, ) .file( "build.rs", r#" fn main() { println!("cargo:rustc-flags=-L native=test4"); } "#, ) .file("src/lib.rs", "") .publish(); let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.1.0" authors = [] [dependencies] dep1 = "*" dep2 = "*" dep3 = "*" dep4 = "*" "#, ) .file( "src/main.rs", r#" fn main() {} "#, ) .build(); assert_that( p.cargo("build").arg("-v"), execs().with_status(0).with_stderr_contains( "\ [RUNNING] `rustc --crate-name foo [..] -L native=test1 -L native=test2 \ -L native=test3 -L native=test4` ", ), ); } #[test] fn links_duplicates_with_cycle() { // this tests that the links_duplicates are caught at resolver time let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] links = "a" build = "build.rs" [dependencies.a] path = "a" [dev-dependencies] b = { path = "b" } "#, ) .file("src/lib.rs", "") .file("build.rs", "") .file( "a/Cargo.toml", r#" [project] name = "a" version = "0.5.0" authors = [] links = "a" build = "build.rs" "#, ) .file("a/src/lib.rs", "") .file("a/build.rs", "") .file( "b/Cargo.toml", r#" [project] name = "b" version = "0.5.0" authors = [] [dependencies] foo = { path = ".." } "#, ) .file("b/src/lib.rs", "") .build(); assert_that(p.cargo("build"), execs().with_status(101) .with_stderr("\ error: failed to select a version for `a`. ... required by package `foo v0.5.0 ([..])` versions that meet the requirements `*` are: 0.5.0 the package `a` links to the native library `a`, but it conflicts with a previous package which links to `a` as well: package `foo v0.5.0 ([..])` failed to select a version for `a` which could resolve this conflict ")); } #[test] fn rename_with_link_search_path() { let p = project("foo") .file( "Cargo.toml", r#" [project] name = "foo" version = "0.5.0" authors = [] [lib] crate-type = ["cdylib"] "#, ) .file( "src/lib.rs", " #[no_mangle] pub extern fn cargo_test_foo() {} ", ); let p = p.build(); assert_that(p.cargo("build"), execs().with_status(0)); let p2 = project("bar") .file( "Cargo.toml", r#" [project] name = "bar" version = "0.5.0" authors = [] [profile.dev] debug = false "#, ) .file( "build.rs", r#" use std::env; use std::fs; use std::path::PathBuf; fn main() { // Move the `libfoo.so` from the root of our project into the // build directory. This way Cargo should automatically manage // `LD_LIBRARY_PATH` and such. let root = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); let file = format!("{}foo{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX); let src = root.join(&file); let dst_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); let dst = dst_dir.join(&file); fs::copy(&src, &dst).unwrap(); // handle windows, like below drop(fs::copy(root.join("foo.dll.lib"), dst_dir.join("foo.dll.lib"))); println!("cargo:rerun-if-changed=build.rs"); if cfg!(target_env = "msvc") { println!("cargo:rustc-link-lib=foo.dll"); } else { println!("cargo:rustc-link-lib=foo"); } println!("cargo:rustc-link-search={}", dst.parent().unwrap().display()); } "#, ) .file( "src/main.rs", r#" extern { #[link_name = "cargo_test_foo"] fn foo(); } fn main() { unsafe { foo(); } } "#, ); let p2 = p2.build(); // Move the output `libfoo.so` into the directory of `p2`, and then delete // the `p` project. On OSX the `libfoo.dylib` artifact references the // original path in `p` so we want to make sure that it can't find it (hence // the deletion). let root = PathBuf::from(p.root()); let root = root.join("target").join("debug").join("deps"); let file = format!("{}foo{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX); let src = root.join(&file); let dst = p2.root().join(&file); fs::copy(&src, &dst).unwrap(); // copy the import library for windows, if it exists drop(fs::copy( &root.join("foo.dll.lib"), p2.root().join("foo.dll.lib"), )); fs::remove_dir_all(p.root()).unwrap(); // Everything should work the first time assert_that(p2.cargo("run"), execs().with_status(0)); // Now rename the root directory and rerun `cargo run`. Not only should we // not build anything but we also shouldn't crash. let mut new = p2.root(); new.pop(); new.push("bar2"); fs::rename(p2.root(), &new).unwrap(); assert_that( p2.cargo("run").cwd(&new), execs().with_status(0).with_stderr( "\ [FINISHED] [..] [RUNNING] [..] ", ), ); }