| //! Tests for fingerprinting (rebuild detection). |
| |
| use filetime::FileTime; |
| use std::fs::{self, OpenOptions}; |
| use std::io; |
| use std::io::prelude::*; |
| use std::net::TcpListener; |
| use std::path::{Path, PathBuf}; |
| use std::process::Stdio; |
| use std::thread; |
| use std::time::SystemTime; |
| |
| use super::death; |
| use cargo_test_support::paths::{self, CargoPathExt}; |
| use cargo_test_support::registry::Package; |
| use cargo_test_support::{ |
| basic_manifest, is_coarse_mtime, project, rustc_host, rustc_host_env, sleep_ms, |
| }; |
| |
| #[cargo_test] |
| fn modifying_and_moving() { |
| let p = project() |
| .file("src/main.rs", "mod a; fn main() {}") |
| .file("src/a.rs", "") |
| .build(); |
| |
| p.cargo("build") |
| .with_stderr( |
| "\ |
| [COMPILING] foo v0.0.1 ([CWD]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| |
| p.cargo("build").with_stdout("").run(); |
| p.root().move_into_the_past(); |
| p.root().join("target").move_into_the_past(); |
| |
| p.change_file("src/a.rs", "#[allow(unused)]fn main() {}"); |
| p.cargo("build -v") |
| .with_stderr( |
| "\ |
| [DIRTY] foo v0.0.1 ([CWD]): the file `src/a.rs` has changed ([..]) |
| [COMPILING] foo v0.0.1 ([CWD]) |
| [RUNNING] `rustc --crate-name foo [..] |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| |
| fs::rename(&p.root().join("src/a.rs"), &p.root().join("src/b.rs")).unwrap(); |
| p.cargo("build") |
| .with_status(101) |
| .with_stderr_contains("[..]file not found[..]") |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn modify_only_some_files() { |
| let p = project() |
| .file("src/lib.rs", "mod a;") |
| .file("src/a.rs", "") |
| .file("src/main.rs", "mod b; fn main() {}") |
| .file("src/b.rs", "") |
| .file("tests/test.rs", "") |
| .build(); |
| |
| p.cargo("build") |
| .with_stderr( |
| "\ |
| [COMPILING] foo v0.0.1 ([CWD]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| p.cargo("test").run(); |
| sleep_ms(1000); |
| |
| assert!(p.bin("foo").is_file()); |
| |
| let lib = p.root().join("src/lib.rs"); |
| p.change_file("src/lib.rs", "invalid rust code"); |
| p.change_file("src/b.rs", "#[allow(unused)]fn foo() {}"); |
| lib.move_into_the_past(); |
| |
| // Make sure the binary is rebuilt, not the lib |
| p.cargo("build -v") |
| .with_stderr( |
| "\ |
| [DIRTY] foo v0.0.1 ([CWD]): the file `src/b.rs` has changed ([..]) |
| [COMPILING] foo v0.0.1 ([CWD]) |
| [RUNNING] `rustc --crate-name foo [..] |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| assert!(p.bin("foo").is_file()); |
| } |
| |
| #[cargo_test] |
| fn rebuild_sub_package_then_while_package() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| authors = [] |
| version = "0.0.1" |
| |
| [dependencies.a] |
| path = "a" |
| [dependencies.b] |
| path = "b" |
| "#, |
| ) |
| .file("src/lib.rs", "extern crate a; extern crate b;") |
| .file( |
| "a/Cargo.toml", |
| r#" |
| [package] |
| name = "a" |
| authors = [] |
| version = "0.0.1" |
| [dependencies.b] |
| path = "../b" |
| "#, |
| ) |
| .file("a/src/lib.rs", "extern crate b;") |
| .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) |
| .file("b/src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build") |
| .with_stderr( |
| "\ |
| [COMPILING] b [..] |
| [COMPILING] a [..] |
| [COMPILING] foo [..] |
| [FINISHED] dev [..] |
| ", |
| ) |
| .run(); |
| |
| if is_coarse_mtime() { |
| sleep_ms(1000); |
| } |
| p.change_file("b/src/lib.rs", "pub fn b() {}"); |
| |
| p.cargo("build -pb -v") |
| .with_stderr( |
| "\ |
| [DIRTY] b v0.0.1 ([..]): the file `b/src/lib.rs` has changed ([..]) |
| [COMPILING] b [..] |
| [RUNNING] `rustc --crate-name b [..] |
| [FINISHED] dev [..] |
| ", |
| ) |
| .run(); |
| |
| p.change_file( |
| "src/lib.rs", |
| "extern crate a; extern crate b; pub fn toplevel() {}", |
| ); |
| |
| p.cargo("build -v") |
| .with_stderr( |
| "\ |
| [FRESH] b [..] |
| [DIRTY] a [..]: the dependency b was rebuilt ([..]) |
| [COMPILING] a [..] |
| [RUNNING] `rustc --crate-name a [..] |
| [DIRTY] foo [..]: the dependency b was rebuilt ([..]) |
| [COMPILING] foo [..] |
| [RUNNING] `rustc --crate-name foo [..] |
| [FINISHED] dev [..] |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn changing_lib_features_caches_targets() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| authors = [] |
| version = "0.0.1" |
| |
| [features] |
| foo = [] |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build") |
| .with_stderr( |
| "\ |
| [..]Compiling foo v0.0.1 ([..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| |
| p.cargo("build --features foo") |
| .with_stderr( |
| "\ |
| [..]Compiling foo v0.0.1 ([..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| |
| /* Targets should be cached from the first build */ |
| |
| p.cargo("build") |
| .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") |
| .run(); |
| |
| p.cargo("build").with_stdout("").run(); |
| |
| p.cargo("build --features foo") |
| .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn changing_profiles_caches_targets() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| authors = [] |
| version = "0.0.1" |
| |
| [profile.dev] |
| panic = "abort" |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build") |
| .with_stderr( |
| "\ |
| [..]Compiling foo v0.0.1 ([..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| |
| p.cargo("test") |
| .with_stderr( |
| "\ |
| [..]Compiling foo v0.0.1 ([..]) |
| [FINISHED] test [unoptimized + debuginfo] target(s) in [..] |
| [RUNNING] [..] (target[..]debug[..]deps[..]foo-[..][EXE]) |
| [DOCTEST] foo |
| ", |
| ) |
| .run(); |
| |
| /* Targets should be cached from the first build */ |
| |
| p.cargo("build") |
| .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") |
| .run(); |
| |
| p.cargo("test foo") |
| .with_stderr( |
| "\ |
| [FINISHED] test [unoptimized + debuginfo] target(s) in [..] |
| [RUNNING] [..] (target[..]debug[..]deps[..]foo-[..][EXE]) |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn changing_bin_paths_common_target_features_caches_targets() { |
| // Make sure dep_cache crate is built once per feature |
| let p = project() |
| .no_manifest() |
| .file( |
| ".cargo/config", |
| r#" |
| [build] |
| target-dir = "./target" |
| "#, |
| ) |
| .file( |
| "dep_crate/Cargo.toml", |
| r#" |
| [package] |
| name = "dep_crate" |
| version = "0.0.1" |
| authors = [] |
| |
| [features] |
| ftest = [] |
| "#, |
| ) |
| .file( |
| "dep_crate/src/lib.rs", |
| r#" |
| #[cfg(feature = "ftest")] |
| pub fn yo() { |
| println!("ftest on") |
| } |
| #[cfg(not(feature = "ftest"))] |
| pub fn yo() { |
| println!("ftest off") |
| } |
| "#, |
| ) |
| .file( |
| "a/Cargo.toml", |
| r#" |
| [package] |
| name = "a" |
| version = "0.0.1" |
| authors = [] |
| |
| [dependencies] |
| dep_crate = {path = "../dep_crate", features = []} |
| "#, |
| ) |
| .file("a/src/lib.rs", "") |
| .file( |
| "a/src/main.rs", |
| r#" |
| extern crate dep_crate; |
| use dep_crate::yo; |
| fn main() { |
| yo(); |
| } |
| "#, |
| ) |
| .file( |
| "b/Cargo.toml", |
| r#" |
| [package] |
| name = "b" |
| version = "0.0.1" |
| authors = [] |
| |
| [dependencies] |
| dep_crate = {path = "../dep_crate", features = ["ftest"]} |
| "#, |
| ) |
| .file("b/src/lib.rs", "") |
| .file( |
| "b/src/main.rs", |
| r#" |
| extern crate dep_crate; |
| use dep_crate::yo; |
| fn main() { |
| yo(); |
| } |
| "#, |
| ) |
| .build(); |
| |
| /* Build and rebuild a/. Ensure dep_crate only builds once */ |
| p.cargo("run") |
| .cwd("a") |
| .with_stdout("ftest off") |
| .with_stderr( |
| "\ |
| [..]Compiling dep_crate v0.0.1 ([..]) |
| [..]Compiling a v0.0.1 ([..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| [RUNNING] `[..]target/debug/a[EXE]` |
| ", |
| ) |
| .run(); |
| p.cargo("clean -p a").cwd("a").run(); |
| p.cargo("run") |
| .cwd("a") |
| .with_stdout("ftest off") |
| .with_stderr( |
| "\ |
| [..]Compiling a v0.0.1 ([..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| [RUNNING] `[..]target/debug/a[EXE]` |
| ", |
| ) |
| .run(); |
| |
| /* Build and rebuild b/. Ensure dep_crate only builds once */ |
| p.cargo("run") |
| .cwd("b") |
| .with_stdout("ftest on") |
| .with_stderr( |
| "\ |
| [..]Compiling dep_crate v0.0.1 ([..]) |
| [..]Compiling b v0.0.1 ([..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| [RUNNING] `[..]target/debug/b[EXE]` |
| ", |
| ) |
| .run(); |
| p.cargo("clean -p b").cwd("b").run(); |
| p.cargo("run") |
| .cwd("b") |
| .with_stdout("ftest on") |
| .with_stderr( |
| "\ |
| [..]Compiling b v0.0.1 ([..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| [RUNNING] `[..]target/debug/b[EXE]` |
| ", |
| ) |
| .run(); |
| |
| /* Build a/ package again. If we cache different feature dep builds correctly, |
| * this should not cause a rebuild of dep_crate */ |
| p.cargo("clean -p a").cwd("a").run(); |
| p.cargo("run") |
| .cwd("a") |
| .with_stdout("ftest off") |
| .with_stderr( |
| "\ |
| [..]Compiling a v0.0.1 ([..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| [RUNNING] `[..]target/debug/a[EXE]` |
| ", |
| ) |
| .run(); |
| |
| /* Build b/ package again. If we cache different feature dep builds correctly, |
| * this should not cause a rebuild */ |
| p.cargo("clean -p b").cwd("b").run(); |
| p.cargo("run") |
| .cwd("b") |
| .with_stdout("ftest on") |
| .with_stderr( |
| "\ |
| [..]Compiling b v0.0.1 ([..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| [RUNNING] `[..]target/debug/b[EXE]` |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn changing_bin_features_caches_targets() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| authors = [] |
| version = "0.0.1" |
| |
| [features] |
| foo = [] |
| "#, |
| ) |
| .file( |
| "src/main.rs", |
| r#" |
| fn main() { |
| let msg = if cfg!(feature = "foo") { "feature on" } else { "feature off" }; |
| println!("{}", msg); |
| } |
| "#, |
| ) |
| .build(); |
| |
| p.cargo("build") |
| .with_stderr( |
| "\ |
| [COMPILING] foo v0.0.1 ([..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| p.rename_run("foo", "off1").with_stdout("feature off").run(); |
| |
| p.cargo("build --features foo") |
| .with_stderr( |
| "\ |
| [COMPILING] foo v0.0.1 ([..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| p.rename_run("foo", "on1").with_stdout("feature on").run(); |
| |
| /* Targets should be cached from the first build */ |
| |
| let mut e = p.cargo("build -v"); |
| |
| // MSVC does not include hash in binary filename, so it gets recompiled. |
| if cfg!(target_env = "msvc") { |
| e.with_stderr( |
| "\ |
| [DIRTY] foo v0.0.1 ([..]): the list of features changed |
| [COMPILING] foo[..] |
| [RUNNING] `rustc [..] |
| [FINISHED] dev[..]", |
| ); |
| } else { |
| e.with_stderr("[FRESH] foo v0.0.1 ([..])\n[FINISHED] dev[..]"); |
| } |
| e.run(); |
| p.rename_run("foo", "off2").with_stdout("feature off").run(); |
| |
| let mut e = p.cargo("build --features foo -v"); |
| if cfg!(target_env = "msvc") { |
| e.with_stderr( |
| "\ |
| [DIRTY] foo v0.0.1 ([..]): the list of features changed |
| [COMPILING] foo[..] |
| [RUNNING] `rustc [..] |
| [FINISHED] dev[..]", |
| ); |
| } else { |
| e.with_stderr( |
| "\ |
| [FRESH] foo v0.0.1 ([..]) |
| [FINISHED] dev[..]", |
| ); |
| } |
| e.run(); |
| p.rename_run("foo", "on2").with_stdout("feature on").run(); |
| } |
| |
| #[cargo_test] |
| fn rebuild_tests_if_lib_changes() { |
| let p = project() |
| .file("src/lib.rs", "pub fn foo() {}") |
| .file( |
| "tests/foo.rs", |
| r#" |
| extern crate foo; |
| #[test] |
| fn test() { foo::foo(); } |
| "#, |
| ) |
| .build(); |
| |
| p.cargo("build").run(); |
| p.cargo("test").run(); |
| |
| sleep_ms(1000); |
| p.change_file("src/lib.rs", ""); |
| |
| p.cargo("build -v").run(); |
| p.cargo("test -v") |
| .with_status(101) |
| .with_stderr_contains("[..]cannot find function `foo`[..]") |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn no_rebuild_transitive_target_deps() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| |
| [dependencies] |
| a = { path = "a" } |
| [dev-dependencies] |
| b = { path = "b" } |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .file("tests/foo.rs", "") |
| .file( |
| "a/Cargo.toml", |
| r#" |
| [package] |
| name = "a" |
| version = "0.0.1" |
| authors = [] |
| |
| [target.foo.dependencies] |
| c = { path = "../c" } |
| "#, |
| ) |
| .file("a/src/lib.rs", "") |
| .file( |
| "b/Cargo.toml", |
| r#" |
| [package] |
| name = "b" |
| version = "0.0.1" |
| authors = [] |
| |
| [dependencies] |
| c = { path = "../c" } |
| "#, |
| ) |
| .file("b/src/lib.rs", "") |
| .file("c/Cargo.toml", &basic_manifest("c", "0.0.1")) |
| .file("c/src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build").run(); |
| p.cargo("test --no-run") |
| .with_stderr( |
| "\ |
| [COMPILING] c v0.0.1 ([..]) |
| [COMPILING] b v0.0.1 ([..]) |
| [COMPILING] foo v0.0.1 ([..]) |
| [FINISHED] test [unoptimized + debuginfo] target(s) in [..] |
| [EXECUTABLE] unittests src/lib.rs (target/debug/deps/foo-[..][EXE]) |
| [EXECUTABLE] tests/foo.rs (target/debug/deps/foo-[..][EXE]) |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn rerun_if_changed_in_dep() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| |
| [dependencies] |
| a = { path = "a" } |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .file( |
| "a/Cargo.toml", |
| r#" |
| [package] |
| name = "a" |
| version = "0.0.1" |
| authors = [] |
| build = "build.rs" |
| "#, |
| ) |
| .file( |
| "a/build.rs", |
| r#" |
| fn main() { |
| println!("cargo:rerun-if-changed=build.rs"); |
| } |
| "#, |
| ) |
| .file("a/src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build").run(); |
| p.cargo("build").with_stdout("").run(); |
| } |
| |
| #[cargo_test] |
| fn same_build_dir_cached_packages() { |
| let p = project() |
| .no_manifest() |
| .file( |
| "a1/Cargo.toml", |
| r#" |
| [package] |
| name = "a1" |
| version = "0.0.1" |
| authors = [] |
| [dependencies] |
| b = { path = "../b" } |
| "#, |
| ) |
| .file("a1/src/lib.rs", "") |
| .file( |
| "a2/Cargo.toml", |
| r#" |
| [package] |
| name = "a2" |
| version = "0.0.1" |
| authors = [] |
| [dependencies] |
| b = { path = "../b" } |
| "#, |
| ) |
| .file("a2/src/lib.rs", "") |
| .file( |
| "b/Cargo.toml", |
| r#" |
| [package] |
| name = "b" |
| version = "0.0.1" |
| authors = [] |
| [dependencies] |
| c = { path = "../c" } |
| "#, |
| ) |
| .file("b/src/lib.rs", "") |
| .file( |
| "c/Cargo.toml", |
| r#" |
| [package] |
| name = "c" |
| version = "0.0.1" |
| authors = [] |
| [dependencies] |
| d = { path = "../d" } |
| "#, |
| ) |
| .file("c/src/lib.rs", "") |
| .file("d/Cargo.toml", &basic_manifest("d", "0.0.1")) |
| .file("d/src/lib.rs", "") |
| .file( |
| ".cargo/config", |
| r#" |
| [build] |
| target-dir = "./target" |
| "#, |
| ) |
| .build(); |
| |
| p.cargo("build") |
| .cwd("a1") |
| .with_stderr(&format!( |
| "\ |
| [COMPILING] d v0.0.1 ({dir}/d) |
| [COMPILING] c v0.0.1 ({dir}/c) |
| [COMPILING] b v0.0.1 ({dir}/b) |
| [COMPILING] a1 v0.0.1 ([CWD]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| dir = p.url().to_file_path().unwrap().to_str().unwrap() |
| )) |
| .run(); |
| p.cargo("build") |
| .cwd("a2") |
| .with_stderr( |
| "\ |
| [COMPILING] a2 v0.0.1 ([CWD]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn no_rebuild_if_build_artifacts_move_backwards_in_time() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| |
| [dependencies] |
| a = { path = "a" } |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) |
| .file("a/src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build").run(); |
| |
| p.root().move_into_the_past(); |
| |
| p.cargo("build") |
| .with_stdout("") |
| .with_stderr("[FINISHED] [..]") |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn rebuild_if_build_artifacts_move_forward_in_time() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| |
| [dependencies] |
| a = { path = "a" } |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) |
| .file("a/src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build").run(); |
| |
| p.root().move_into_the_future(); |
| |
| p.cargo("build") |
| .env("CARGO_LOG", "") |
| .with_stdout("") |
| .with_stderr( |
| "\ |
| [COMPILING] a v0.0.1 ([..]) |
| [COMPILING] foo v0.0.1 ([..]) |
| [FINISHED] [..] |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn rebuild_if_environment_changes() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| description = "old desc" |
| version = "0.0.1" |
| authors = [] |
| "#, |
| ) |
| .file( |
| "src/main.rs", |
| r#" |
| fn main() { |
| println!("{}", env!("CARGO_PKG_DESCRIPTION")); |
| } |
| "#, |
| ) |
| .build(); |
| |
| p.cargo("run") |
| .with_stdout("old desc") |
| .with_stderr( |
| "\ |
| [COMPILING] foo v0.0.1 ([CWD]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| [RUNNING] `target/debug/foo[EXE]` |
| ", |
| ) |
| .run(); |
| |
| p.change_file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| description = "new desc" |
| version = "0.0.1" |
| authors = [] |
| "#, |
| ); |
| |
| p.cargo("run -v") |
| .with_stdout("new desc") |
| .with_stderr( |
| "\ |
| [DIRTY] foo v0.0.1 ([CWD]): the metadata changed |
| [COMPILING] foo v0.0.1 ([CWD]) |
| [RUNNING] `rustc [..] |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| [RUNNING] `target/debug/foo[EXE]` |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn no_rebuild_when_rename_dir() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "bar" |
| version = "0.0.1" |
| authors = [] |
| |
| [workspace] |
| |
| [dependencies] |
| foo = { path = "foo" } |
| "#, |
| ) |
| .file("src/_unused.rs", "") |
| .file("build.rs", "fn main() {}") |
| .file("foo/Cargo.toml", &basic_manifest("foo", "0.0.1")) |
| .file("foo/src/lib.rs", "") |
| .file("foo/build.rs", "fn main() {}") |
| .build(); |
| |
| // make sure the most recently modified file is `src/lib.rs`, not |
| // `Cargo.toml`, to expose a historical bug where we forgot to strip the |
| // `Cargo.toml` path from looking for the package root. |
| cargo_test_support::sleep_ms(100); |
| fs::write(p.root().join("src/lib.rs"), "").unwrap(); |
| |
| p.cargo("build").run(); |
| let mut new = p.root(); |
| new.pop(); |
| new.push("bar"); |
| fs::rename(p.root(), &new).unwrap(); |
| |
| p.cargo("build") |
| .cwd(&new) |
| .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn unused_optional_dep() { |
| Package::new("registry1", "0.1.0").publish(); |
| Package::new("registry2", "0.1.0").publish(); |
| Package::new("registry3", "0.1.0").publish(); |
| |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "p" |
| authors = [] |
| version = "0.1.0" |
| |
| [dependencies] |
| bar = { path = "bar" } |
| baz = { path = "baz" } |
| registry1 = "*" |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .file( |
| "bar/Cargo.toml", |
| r#" |
| [package] |
| name = "bar" |
| version = "0.1.1" |
| authors = [] |
| |
| [dev-dependencies] |
| registry2 = "*" |
| "#, |
| ) |
| .file("bar/src/lib.rs", "") |
| .file( |
| "baz/Cargo.toml", |
| r#" |
| [package] |
| name = "baz" |
| version = "0.1.1" |
| authors = [] |
| |
| [dependencies] |
| registry3 = { version = "*", optional = true } |
| "#, |
| ) |
| .file("baz/src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build").run(); |
| p.cargo("build").with_stderr("[FINISHED] [..]").run(); |
| } |
| |
| #[cargo_test] |
| fn path_dev_dep_registry_updates() { |
| Package::new("registry1", "0.1.0").publish(); |
| Package::new("registry2", "0.1.0").publish(); |
| |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "p" |
| authors = [] |
| version = "0.1.0" |
| |
| [dependencies] |
| bar = { path = "bar" } |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .file( |
| "bar/Cargo.toml", |
| r#" |
| [package] |
| name = "bar" |
| version = "0.1.1" |
| authors = [] |
| |
| [dependencies] |
| registry1 = "*" |
| |
| [dev-dependencies] |
| baz = { path = "../baz"} |
| "#, |
| ) |
| .file("bar/src/lib.rs", "") |
| .file( |
| "baz/Cargo.toml", |
| r#" |
| [package] |
| name = "baz" |
| version = "0.1.1" |
| authors = [] |
| |
| [dependencies] |
| registry2 = "*" |
| "#, |
| ) |
| .file("baz/src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build").run(); |
| p.cargo("build").with_stderr("[FINISHED] [..]").run(); |
| } |
| |
| #[cargo_test] |
| fn change_panic_mode() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [workspace] |
| members = ['bar', 'baz'] |
| [profile.dev] |
| panic = 'abort' |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) |
| .file("bar/src/lib.rs", "") |
| .file( |
| "baz/Cargo.toml", |
| r#" |
| [package] |
| name = "baz" |
| version = "0.1.1" |
| authors = [] |
| |
| [lib] |
| proc-macro = true |
| |
| [dependencies] |
| bar = { path = '../bar' } |
| "#, |
| ) |
| .file("baz/src/lib.rs", "extern crate bar;") |
| .build(); |
| |
| p.cargo("build -p bar").run(); |
| p.cargo("build -p baz").run(); |
| } |
| |
| #[cargo_test] |
| fn dont_rebuild_based_on_plugins() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "bar" |
| version = "0.1.1" |
| |
| [workspace] |
| members = ['baz'] |
| |
| [dependencies] |
| proc-macro-thing = { path = 'proc-macro-thing' } |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .file( |
| "proc-macro-thing/Cargo.toml", |
| r#" |
| [package] |
| name = "proc-macro-thing" |
| version = "0.1.1" |
| |
| [lib] |
| proc-macro = true |
| |
| [dependencies] |
| qux = { path = '../qux' } |
| "#, |
| ) |
| .file("proc-macro-thing/src/lib.rs", "") |
| .file( |
| "baz/Cargo.toml", |
| r#" |
| [package] |
| name = "baz" |
| version = "0.1.1" |
| |
| [dependencies] |
| qux = { path = '../qux' } |
| "#, |
| ) |
| .file("baz/src/main.rs", "fn main() {}") |
| .file("qux/Cargo.toml", &basic_manifest("qux", "0.1.1")) |
| .file("qux/src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build").run(); |
| p.cargo("build -p baz").run(); |
| p.cargo("build").with_stderr("[FINISHED] [..]\n").run(); |
| p.cargo("build -p bar") |
| .with_stderr("[FINISHED] [..]\n") |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn reuse_workspace_lib() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "bar" |
| version = "0.1.1" |
| |
| [workspace] |
| |
| [dependencies] |
| baz = { path = 'baz' } |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.1")) |
| .file("baz/src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build").run(); |
| p.cargo("test -p baz -v --no-run") |
| .with_stderr( |
| "\ |
| [COMPILING] baz v0.1.1 ([..]) |
| [RUNNING] `rustc[..] --test [..]` |
| [FINISHED] [..] |
| [EXECUTABLE] `[..]/target/debug/deps/baz-[..][EXE]` |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn reuse_shared_build_dep() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.0.1" |
| |
| [dependencies] |
| shared = {path = "shared"} |
| |
| [workspace] |
| members = ["shared", "bar"] |
| "#, |
| ) |
| .file("src/main.rs", "fn main() {}") |
| .file("shared/Cargo.toml", &basic_manifest("shared", "0.0.1")) |
| .file("shared/src/lib.rs", "") |
| .file( |
| "bar/Cargo.toml", |
| r#" |
| [package] |
| name = "bar" |
| version = "0.0.1" |
| |
| [build-dependencies] |
| shared = { path = "../shared" } |
| "#, |
| ) |
| .file("bar/src/lib.rs", "") |
| .file("bar/build.rs", "fn main() {}") |
| .build(); |
| |
| p.cargo("build --workspace").run(); |
| // This should not recompile! |
| p.cargo("build -p foo -v") |
| .with_stderr( |
| "\ |
| [FRESH] shared [..] |
| [FRESH] foo [..] |
| [FINISHED] [..] |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn changing_rustflags_is_cached() { |
| let p = project().file("src/lib.rs", "").build(); |
| |
| // This isn't ever cached, we always have to recompile |
| p.cargo("build") |
| .with_stderr( |
| "\ |
| [COMPILING] foo v0.0.1 ([..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", |
| ) |
| .run(); |
| p.cargo("build -v") |
| .env("RUSTFLAGS", "-C linker=cc") |
| .with_stderr( |
| "\ |
| [DIRTY] foo v0.0.1 ([..]): the rustflags changed |
| [COMPILING] foo v0.0.1 ([..]) |
| [RUNNING] `rustc [..] |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", |
| ) |
| .run(); |
| |
| p.cargo("build -v") |
| .with_stderr( |
| "\ |
| [DIRTY] foo v0.0.1 ([..]): the rustflags changed |
| [COMPILING] foo v0.0.1 ([..]) |
| [RUNNING] `rustc [..] |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", |
| ) |
| .run(); |
| p.cargo("build -v") |
| .env("RUSTFLAGS", "-C linker=cc") |
| .with_stderr( |
| "\ |
| [DIRTY] foo v0.0.1 ([..]): the rustflags changed |
| [COMPILING] foo v0.0.1 ([..]) |
| [RUNNING] `rustc [..] |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn update_dependency_mtime_does_not_rebuild() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.0.1" |
| |
| [dependencies] |
| bar = { path = "bar" } |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) |
| .file("bar/src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build -Z mtime-on-use") |
| .masquerade_as_nightly_cargo(&["mtime-on-use"]) |
| .env("RUSTFLAGS", "-C linker=cc") |
| .with_stderr( |
| "\ |
| [COMPILING] bar v0.0.1 ([..]) |
| [COMPILING] foo v0.0.1 ([..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", |
| ) |
| .run(); |
| // This does not make new files, but it does update the mtime of the dependency. |
| p.cargo("build -p bar -Z mtime-on-use") |
| .masquerade_as_nightly_cargo(&["mtime-on-use"]) |
| .env("RUSTFLAGS", "-C linker=cc") |
| .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") |
| .run(); |
| // This should not recompile! |
| p.cargo("build -Z mtime-on-use") |
| .masquerade_as_nightly_cargo(&["mtime-on-use"]) |
| .env("RUSTFLAGS", "-C linker=cc") |
| .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") |
| .run(); |
| } |
| |
| fn fingerprint_cleaner(mut dir: PathBuf, timestamp: filetime::FileTime) { |
| // Cargo is experimenting with letting outside projects develop some |
| // limited forms of GC for target_dir. This is one of the forms. |
| // Specifically, Cargo is updating the mtime of a file in |
| // target/profile/.fingerprint each time it uses the fingerprint. |
| // So a cleaner can remove files associated with a fingerprint |
| // if all the files in the fingerprint's folder are older then a time stamp without |
| // effecting any builds that happened since that time stamp. |
| let mut cleaned = false; |
| dir.push(".fingerprint"); |
| for fing in fs::read_dir(&dir).unwrap() { |
| let fing = fing.unwrap(); |
| |
| let outdated = |f: io::Result<fs::DirEntry>| { |
| filetime::FileTime::from_last_modification_time(&f.unwrap().metadata().unwrap()) |
| <= timestamp |
| }; |
| if fs::read_dir(fing.path()).unwrap().all(outdated) { |
| fs::remove_dir_all(fing.path()).unwrap(); |
| println!("remove: {:?}", fing.path()); |
| // a real cleaner would remove the big files in deps and build as well |
| // but fingerprint is sufficient for our tests |
| cleaned = true; |
| } else { |
| } |
| } |
| assert!( |
| cleaned, |
| "called fingerprint_cleaner, but there was nothing to remove" |
| ); |
| } |
| |
| #[cargo_test] |
| fn fingerprint_cleaner_does_not_rebuild() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.0.1" |
| |
| [dependencies] |
| bar = { path = "bar" } |
| |
| [features] |
| a = [] |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) |
| .file("bar/src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build -Z mtime-on-use") |
| .masquerade_as_nightly_cargo(&["mtime-on-use"]) |
| .run(); |
| p.cargo("build -Z mtime-on-use --features a") |
| .masquerade_as_nightly_cargo(&["mtime-on-use"]) |
| .with_stderr( |
| "\ |
| [COMPILING] foo v0.0.1 ([..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", |
| ) |
| .run(); |
| if is_coarse_mtime() { |
| sleep_ms(1000); |
| } |
| let timestamp = filetime::FileTime::from_system_time(SystemTime::now()); |
| if is_coarse_mtime() { |
| sleep_ms(1000); |
| } |
| // This does not make new files, but it does update the mtime. |
| p.cargo("build -Z mtime-on-use --features a") |
| .masquerade_as_nightly_cargo(&["mtime-on-use"]) |
| .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") |
| .run(); |
| fingerprint_cleaner(p.target_debug_dir(), timestamp); |
| // This should not recompile! |
| p.cargo("build -Z mtime-on-use --features a") |
| .masquerade_as_nightly_cargo(&["mtime-on-use"]) |
| .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") |
| .run(); |
| // But this should be cleaned and so need a rebuild |
| p.cargo("build -Z mtime-on-use") |
| .masquerade_as_nightly_cargo(&["mtime-on-use"]) |
| .with_stderr( |
| "\ |
| [COMPILING] foo v0.0.1 ([..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn reuse_panic_build_dep_test() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.0.1" |
| |
| [build-dependencies] |
| bar = { path = "bar" } |
| |
| [dev-dependencies] |
| bar = { path = "bar" } |
| |
| [profile.dev] |
| panic = "abort" |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .file("build.rs", "fn main() {}") |
| .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) |
| .file("bar/src/lib.rs", "") |
| .build(); |
| |
| // Check that `bar` is not built twice. It is only needed once (without `panic`). |
| p.cargo("test --lib --no-run -v") |
| .with_stderr( |
| "\ |
| [COMPILING] bar [..] |
| [RUNNING] `rustc --crate-name bar [..] |
| [COMPILING] foo [..] |
| [RUNNING] `rustc --crate-name build_script_build [..] |
| [RUNNING] [..]build-script-build` |
| [RUNNING] `rustc --crate-name foo src/lib.rs [..]--test[..] |
| [FINISHED] [..] |
| [EXECUTABLE] `[..]/target/debug/deps/foo-[..][EXE]` |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn reuse_panic_pm() { |
| // foo(panic) -> bar(panic) |
| // somepm(nopanic) -> bar(nopanic) |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.0.1" |
| |
| [dependencies] |
| bar = { path = "bar" } |
| somepm = { path = "somepm" } |
| |
| [profile.dev] |
| panic = "abort" |
| "#, |
| ) |
| .file("src/lib.rs", "extern crate bar;") |
| .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) |
| .file("bar/src/lib.rs", "") |
| .file( |
| "somepm/Cargo.toml", |
| r#" |
| [package] |
| name = "somepm" |
| version = "0.0.1" |
| |
| [lib] |
| proc-macro = true |
| |
| [dependencies] |
| bar = { path = "../bar" } |
| "#, |
| ) |
| .file("somepm/src/lib.rs", "extern crate bar;") |
| .build(); |
| |
| // bar is built once without panic (for proc-macro) and once with (for the |
| // normal dependency). |
| p.cargo("build -v") |
| .with_stderr_unordered( |
| "\ |
| [COMPILING] bar [..] |
| [RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..] |
| [RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C debuginfo=2 [..] |
| [COMPILING] somepm [..] |
| [RUNNING] `rustc --crate-name somepm [..] |
| [COMPILING] foo [..] |
| [RUNNING] `rustc --crate-name foo src/lib.rs [..]-C panic=abort[..] |
| [FINISHED] [..] |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn bust_patched_dep() { |
| Package::new("registry1", "0.1.0").publish(); |
| Package::new("registry2", "0.1.0") |
| .dep("registry1", "0.1.0") |
| .publish(); |
| |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.0.1" |
| |
| [dependencies] |
| registry2 = "0.1.0" |
| |
| [patch.crates-io] |
| registry1 = { path = "reg1new" } |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .file("reg1new/Cargo.toml", &basic_manifest("registry1", "0.1.0")) |
| .file("reg1new/src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build").run(); |
| if is_coarse_mtime() { |
| sleep_ms(1000); |
| } |
| |
| p.change_file("reg1new/src/lib.rs", ""); |
| if is_coarse_mtime() { |
| sleep_ms(1000); |
| } |
| |
| p.cargo("build -v") |
| .with_stderr( |
| "\ |
| [DIRTY] registry1 v0.1.0 ([..]): the file `reg1new/src/lib.rs` has changed ([..]) |
| [COMPILING] registry1 v0.1.0 ([..]) |
| [RUNNING] `rustc [..] |
| [DIRTY] registry2 v0.1.0: the dependency registry1 was rebuilt |
| [COMPILING] registry2 v0.1.0 |
| [RUNNING] `rustc [..] |
| [DIRTY] foo v0.0.1 ([..]): the dependency registry2 was rebuilt |
| [COMPILING] foo v0.0.1 ([..]) |
| [RUNNING] `rustc [..] |
| [FINISHED] [..] |
| ", |
| ) |
| .run(); |
| |
| p.cargo("build -v") |
| .with_stderr( |
| "\ |
| [FRESH] registry1 v0.1.0 ([..]) |
| [FRESH] registry2 v0.1.0 |
| [FRESH] foo v0.0.1 ([..]) |
| [FINISHED] [..] |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn rebuild_on_mid_build_file_modification() { |
| let server = TcpListener::bind("127.0.0.1:0").unwrap(); |
| let addr = server.local_addr().unwrap(); |
| |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [workspace] |
| members = ["root", "proc_macro_dep"] |
| "#, |
| ) |
| .file( |
| "root/Cargo.toml", |
| r#" |
| [package] |
| name = "root" |
| version = "0.1.0" |
| authors = [] |
| |
| [dependencies] |
| proc_macro_dep = { path = "../proc_macro_dep" } |
| "#, |
| ) |
| .file( |
| "root/src/lib.rs", |
| r#" |
| #[macro_use] |
| extern crate proc_macro_dep; |
| |
| #[derive(Noop)] |
| pub struct X; |
| "#, |
| ) |
| .file( |
| "proc_macro_dep/Cargo.toml", |
| r#" |
| [package] |
| name = "proc_macro_dep" |
| version = "0.1.0" |
| authors = [] |
| |
| [lib] |
| proc-macro = true |
| "#, |
| ) |
| .file( |
| "proc_macro_dep/src/lib.rs", |
| &format!( |
| r#" |
| extern crate proc_macro; |
| |
| use std::io::Read; |
| use std::net::TcpStream; |
| use proc_macro::TokenStream; |
| |
| #[proc_macro_derive(Noop)] |
| pub fn noop(_input: TokenStream) -> TokenStream {{ |
| let mut stream = TcpStream::connect("{}").unwrap(); |
| let mut v = Vec::new(); |
| stream.read_to_end(&mut v).unwrap(); |
| "".parse().unwrap() |
| }} |
| "#, |
| addr |
| ), |
| ) |
| .build(); |
| let root = p.root(); |
| |
| let t = thread::spawn(move || { |
| let socket = server.accept().unwrap().0; |
| sleep_ms(1000); |
| let mut file = OpenOptions::new() |
| .write(true) |
| .append(true) |
| .open(root.join("root/src/lib.rs")) |
| .unwrap(); |
| writeln!(file, "// modified").expect("Failed to append to root sources"); |
| drop(file); |
| drop(socket); |
| drop(server.accept().unwrap()); |
| }); |
| |
| p.cargo("build") |
| .with_stderr( |
| "\ |
| [COMPILING] proc_macro_dep v0.1.0 ([..]/proc_macro_dep) |
| [COMPILING] root v0.1.0 ([..]/root) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| |
| p.cargo("build -v") |
| .with_stderr( |
| "\ |
| [FRESH] proc_macro_dep v0.1.0 ([..]/proc_macro_dep) |
| [DIRTY] root v0.1.0 ([..]/root): the file `root/src/lib.rs` has changed ([..]) |
| [COMPILING] root v0.1.0 ([..]/root) |
| [RUNNING] `rustc [..] |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| |
| t.join().ok().unwrap(); |
| } |
| |
| #[cargo_test] |
| fn dirty_both_lib_and_test() { |
| // This tests that all artifacts that depend on the results of a build |
| // script will get rebuilt when the build script reruns, even for separate |
| // commands. It does the following: |
| // |
| // 1. Project "foo" has a build script which will compile a small |
| // staticlib to link against. Normally this would use the `cc` crate, |
| // but here we just use rustc to avoid the `cc` dependency. |
| // 2. Build the library. |
| // 3. Build the unit test. The staticlib intentionally has a bad value. |
| // 4. Rewrite the staticlib with the correct value. |
| // 5. Build the library again. |
| // 6. Build the unit test. This should recompile. |
| |
| let slib = |n| { |
| format!( |
| r#" |
| #[no_mangle] |
| pub extern "C" fn doit() -> i32 {{ |
| return {}; |
| }} |
| "#, |
| n |
| ) |
| }; |
| |
| let p = project() |
| .file( |
| "src/lib.rs", |
| r#" |
| extern "C" { |
| fn doit() -> i32; |
| } |
| |
| #[test] |
| fn t1() { |
| assert_eq!(unsafe { doit() }, 1, "doit assert failure"); |
| } |
| "#, |
| ) |
| .file( |
| "build.rs", |
| r#" |
| use std::env; |
| use std::path::PathBuf; |
| use std::process::Command; |
| |
| fn main() { |
| let rustc = env::var_os("RUSTC").unwrap(); |
| let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); |
| assert!( |
| Command::new(rustc) |
| .args(&[ |
| "--crate-type=staticlib", |
| "--out-dir", |
| out_dir.to_str().unwrap(), |
| "slib.rs" |
| ]) |
| .status() |
| .unwrap() |
| .success(), |
| "slib build failed" |
| ); |
| println!("cargo:rustc-link-lib=slib"); |
| println!("cargo:rustc-link-search={}", out_dir.display()); |
| } |
| "#, |
| ) |
| .file("slib.rs", &slib(2)) |
| .build(); |
| |
| p.cargo("build").run(); |
| |
| // 2 != 1 |
| p.cargo("test --lib") |
| .with_status(101) |
| .with_stdout_contains("[..]doit assert failure[..]") |
| .run(); |
| |
| if is_coarse_mtime() { |
| // #5918 |
| sleep_ms(1000); |
| } |
| // Fix the mistake. |
| p.change_file("slib.rs", &slib(1)); |
| |
| p.cargo("build").run(); |
| // This should recompile with the new static lib, and the test should pass. |
| p.cargo("test --lib").run(); |
| } |
| |
| #[cargo_test] |
| fn script_fails_stay_dirty() { |
| // Check if a script is aborted (such as hitting Ctrl-C) that it will re-run. |
| // Steps: |
| // 1. Build to establish fingerprints. |
| // 2. Make a change that triggers the build script to re-run. Abort the |
| // script while it is running. |
| // 3. Run the build again and make sure it re-runs the script. |
| let p = project() |
| .file( |
| "build.rs", |
| r#" |
| mod helper; |
| fn main() { |
| println!("cargo:rerun-if-changed=build.rs"); |
| helper::doit(); |
| } |
| "#, |
| ) |
| .file("helper.rs", "pub fn doit() {}") |
| .file("src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build").run(); |
| if is_coarse_mtime() { |
| sleep_ms(1000); |
| } |
| p.change_file("helper.rs", r#"pub fn doit() {panic!("Crash!");}"#); |
| p.cargo("build") |
| .with_stderr_contains("[..]Crash![..]") |
| .with_status(101) |
| .run(); |
| // There was a bug where this second call would be "fresh". |
| p.cargo("build") |
| .with_stderr_contains("[..]Crash![..]") |
| .with_status(101) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn simulated_docker_deps_stay_cached() { |
| // Test what happens in docker where the nanoseconds are zeroed out. |
| Package::new("regdep", "1.0.0").publish(); |
| Package::new("regdep_old_style", "1.0.0") |
| .file("build.rs", "fn main() {}") |
| .file("src/lib.rs", "") |
| .publish(); |
| Package::new("regdep_env", "1.0.0") |
| .file( |
| "build.rs", |
| r#" |
| fn main() { |
| println!("cargo:rerun-if-env-changed=SOMEVAR"); |
| } |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .publish(); |
| Package::new("regdep_rerun", "1.0.0") |
| .file( |
| "build.rs", |
| r#" |
| fn main() { |
| println!("cargo:rerun-if-changed=build.rs"); |
| } |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .publish(); |
| |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.1.0" |
| |
| [dependencies] |
| pathdep = { path = "pathdep" } |
| regdep = "1.0" |
| regdep_old_style = "1.0" |
| regdep_env = "1.0" |
| regdep_rerun = "1.0" |
| "#, |
| ) |
| .file( |
| "src/lib.rs", |
| " |
| extern crate pathdep; |
| extern crate regdep; |
| extern crate regdep_old_style; |
| extern crate regdep_env; |
| extern crate regdep_rerun; |
| ", |
| ) |
| .file("build.rs", "fn main() {}") |
| .file("pathdep/Cargo.toml", &basic_manifest("pathdep", "1.0.0")) |
| .file("pathdep/src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build").run(); |
| |
| let already_zero = { |
| // This happens on HFS with 1-second timestamp resolution, |
| // or other filesystems where it just so happens to write exactly on a |
| // 1-second boundary. |
| let metadata = fs::metadata(p.root().join("src/lib.rs")).unwrap(); |
| let mtime = FileTime::from_last_modification_time(&metadata); |
| mtime.nanoseconds() == 0 |
| }; |
| |
| // Recursively remove `nanoseconds` from every path. |
| fn zeropath(path: &Path) { |
| for entry in walkdir::WalkDir::new(path) |
| .into_iter() |
| .filter_map(|e| e.ok()) |
| { |
| let metadata = fs::metadata(entry.path()).unwrap(); |
| let mtime = metadata.modified().unwrap(); |
| let mtime_duration = mtime.duration_since(SystemTime::UNIX_EPOCH).unwrap(); |
| let trunc_mtime = FileTime::from_unix_time(mtime_duration.as_secs() as i64, 0); |
| let atime = metadata.accessed().unwrap(); |
| let atime_duration = atime.duration_since(SystemTime::UNIX_EPOCH).unwrap(); |
| let trunc_atime = FileTime::from_unix_time(atime_duration.as_secs() as i64, 0); |
| if let Err(e) = filetime::set_file_times(entry.path(), trunc_atime, trunc_mtime) { |
| // Windows doesn't allow changing filetimes on some things |
| // (directories, other random things I'm not sure why). Just |
| // ignore them. |
| if e.kind() == std::io::ErrorKind::PermissionDenied { |
| println!("PermissionDenied filetime on {:?}", entry.path()); |
| } else { |
| panic!("FileTime error on {:?}: {:?}", entry.path(), e); |
| } |
| } |
| } |
| } |
| zeropath(&p.root()); |
| zeropath(&paths::home()); |
| |
| if already_zero { |
| println!("already zero"); |
| // If it was already truncated, then everything stays fresh. |
| p.cargo("build -v") |
| .with_stderr_unordered( |
| "\ |
| [FRESH] pathdep [..] |
| [FRESH] regdep [..] |
| [FRESH] regdep_env [..] |
| [FRESH] regdep_old_style [..] |
| [FRESH] regdep_rerun [..] |
| [FRESH] foo [..] |
| [FINISHED] [..] |
| ", |
| ) |
| .run(); |
| } else { |
| println!("not already zero"); |
| // It is not ideal that `foo` gets recompiled, but that is the current |
| // behavior. Currently mtimes are ignored for registry deps. |
| // |
| // Note that this behavior is due to the fact that `foo` has a build |
| // script in "old" mode where it doesn't print `rerun-if-*`. In this |
| // mode we use `Precalculated` to fingerprint a path dependency, where |
| // `Precalculated` is an opaque string which has the most recent mtime |
| // in it. It differs between builds because one has nsec=0 and the other |
| // likely has a nonzero nsec. Hence, the rebuild. |
| p.cargo("build -v") |
| .with_stderr_unordered( |
| "\ |
| [FRESH] pathdep [..] |
| [FRESH] regdep [..] |
| [FRESH] regdep_env [..] |
| [FRESH] regdep_old_style [..] |
| [FRESH] regdep_rerun [..] |
| [DIRTY] foo [..]: the precalculated components changed |
| [COMPILING] foo [..] |
| [RUNNING] [..]/foo-[..]/build-script-build[..] |
| [RUNNING] `rustc --crate-name foo[..] |
| [FINISHED] [..] |
| ", |
| ) |
| .run(); |
| } |
| } |
| |
| #[cargo_test] |
| fn metadata_change_invalidates() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.1.0" |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build").run(); |
| |
| for attr in &[ |
| "authors = [\"foo\"]", |
| "description = \"desc\"", |
| "homepage = \"https://example.com\"", |
| "repository =\"https://example.com\"", |
| ] { |
| let mut file = OpenOptions::new() |
| .write(true) |
| .append(true) |
| .open(p.root().join("Cargo.toml")) |
| .unwrap(); |
| writeln!(file, "{}", attr).unwrap(); |
| p.cargo("build") |
| .with_stderr_contains("[COMPILING] foo [..]") |
| .run(); |
| } |
| p.cargo("build -v") |
| .with_stderr_contains("[FRESH] foo[..]") |
| .run(); |
| assert_eq!(p.glob("target/debug/deps/libfoo-*.rlib").count(), 1); |
| } |
| |
| #[cargo_test] |
| fn edition_change_invalidates() { |
| const MANIFEST: &str = r#" |
| [package] |
| name = "foo" |
| version = "0.1.0" |
| "#; |
| let p = project() |
| .file("Cargo.toml", MANIFEST) |
| .file("src/lib.rs", "") |
| .build(); |
| p.cargo("build").run(); |
| p.change_file("Cargo.toml", &format!("{}edition = \"2018\"", MANIFEST)); |
| p.cargo("build") |
| .with_stderr_contains("[COMPILING] foo [..]") |
| .run(); |
| p.change_file( |
| "Cargo.toml", |
| &format!( |
| r#"{}edition = "2018" |
| [lib] |
| edition = "2015" |
| "#, |
| MANIFEST |
| ), |
| ); |
| p.cargo("build") |
| .with_stderr_contains("[COMPILING] foo [..]") |
| .run(); |
| p.cargo("build -v") |
| .with_stderr_contains("[FRESH] foo[..]") |
| .run(); |
| assert_eq!(p.glob("target/debug/deps/libfoo-*.rlib").count(), 1); |
| } |
| |
| #[cargo_test] |
| fn rename_with_path_deps() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.5.0" |
| authors = [] |
| |
| [dependencies] |
| a = { path = 'a' } |
| "#, |
| ) |
| .file("src/lib.rs", "extern crate a; pub fn foo() { a::foo(); }") |
| .file( |
| "a/Cargo.toml", |
| r#" |
| [package] |
| name = "a" |
| version = "0.5.0" |
| authors = [] |
| |
| [dependencies] |
| b = { path = 'b' } |
| "#, |
| ) |
| .file("a/src/lib.rs", "extern crate b; pub fn foo() { b::foo() }") |
| .file( |
| "a/b/Cargo.toml", |
| r#" |
| [package] |
| name = "b" |
| version = "0.5.0" |
| authors = [] |
| "#, |
| ) |
| .file("a/b/src/lib.rs", "pub fn foo() { }"); |
| let p = p.build(); |
| |
| p.cargo("build").run(); |
| |
| // 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 = p.root(); |
| new.pop(); |
| new.push("foo2"); |
| |
| fs::rename(p.root(), &new).unwrap(); |
| |
| p.cargo("build") |
| .cwd(&new) |
| .with_stderr("[FINISHED] [..]") |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn move_target_directory_with_path_deps() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.5.0" |
| authors = [] |
| |
| [dependencies] |
| a = { path = "a" } |
| "#, |
| ) |
| .file( |
| "a/Cargo.toml", |
| r#" |
| [package] |
| name = "a" |
| version = "0.5.0" |
| authors = [] |
| "#, |
| ) |
| .file("src/lib.rs", "extern crate a; pub use a::print_msg;") |
| .file( |
| "a/build.rs", |
| r###" |
| use std::env; |
| use std::fs; |
| use std::path::Path; |
| |
| fn main() { |
| println!("cargo:rerun-if-changed=build.rs"); |
| let out_dir = env::var("OUT_DIR").unwrap(); |
| let dest_path = Path::new(&out_dir).join("hello.rs"); |
| fs::write(&dest_path, r#" |
| pub fn message() -> &'static str { |
| "Hello, World!" |
| } |
| "#).unwrap(); |
| } |
| "###, |
| ) |
| .file( |
| "a/src/lib.rs", |
| r#" |
| include!(concat!(env!("OUT_DIR"), "/hello.rs")); |
| pub fn print_msg() { message(); } |
| "#, |
| ); |
| let p = p.build(); |
| |
| let mut parent = p.root(); |
| parent.pop(); |
| |
| p.cargo("build").run(); |
| |
| let new_target = p.root().join("target2"); |
| fs::rename(p.root().join("target"), &new_target).unwrap(); |
| |
| p.cargo("build") |
| .env("CARGO_TARGET_DIR", &new_target) |
| .with_stderr("[FINISHED] [..]") |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn rerun_if_changes() { |
| let p = project() |
| .file( |
| "build.rs", |
| r#" |
| fn main() { |
| println!("cargo:rerun-if-env-changed=FOO"); |
| if std::env::var("FOO").is_ok() { |
| println!("cargo:rerun-if-env-changed=BAR"); |
| } |
| } |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build").run(); |
| p.cargo("build").with_stderr("[FINISHED] [..]").run(); |
| |
| p.cargo("build -v") |
| .env("FOO", "1") |
| .with_stderr( |
| "\ |
| [DIRTY] foo [..]: the env variable FOO changed |
| [COMPILING] foo [..] |
| [RUNNING] `[..]build-script-build` |
| [RUNNING] `rustc [..] |
| [FINISHED] [..] |
| ", |
| ) |
| .run(); |
| p.cargo("build") |
| .env("FOO", "1") |
| .with_stderr("[FINISHED] [..]") |
| .run(); |
| |
| p.cargo("build -v") |
| .env("FOO", "1") |
| .env("BAR", "1") |
| .with_stderr( |
| "\ |
| [DIRTY] foo [..]: the env variable BAR changed |
| [COMPILING] foo [..] |
| [RUNNING] `[..]build-script-build` |
| [RUNNING] `rustc [..] |
| [FINISHED] [..] |
| ", |
| ) |
| .run(); |
| p.cargo("build") |
| .env("FOO", "1") |
| .env("BAR", "1") |
| .with_stderr("[FINISHED] [..]") |
| .run(); |
| |
| p.cargo("build -v") |
| .env("BAR", "2") |
| .with_stderr( |
| "\ |
| [DIRTY] foo [..]: the env variable FOO changed |
| [COMPILING] foo [..] |
| [RUNNING] `[..]build-script-build` |
| [RUNNING] `rustc [..] |
| [FINISHED] [..] |
| ", |
| ) |
| .run(); |
| p.cargo("build") |
| .env("BAR", "2") |
| .with_stderr("[FINISHED] [..]") |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn channel_shares_filenames() { |
| // Test that different "nightly" releases use the same output filename. |
| |
| // Create separate rustc binaries to emulate running different toolchains. |
| let nightly1 = format!( |
| "\ |
| rustc 1.44.0-nightly (38114ff16 2020-03-21) |
| binary: rustc |
| commit-hash: 38114ff16e7856f98b2b4be7ab4cd29b38bed59a |
| commit-date: 2020-03-21 |
| host: {} |
| release: 1.44.0-nightly |
| LLVM version: 9.0 |
| ", |
| rustc_host() |
| ); |
| |
| let nightly2 = format!( |
| "\ |
| rustc 1.44.0-nightly (a5b09d354 2020-03-31) |
| binary: rustc |
| commit-hash: a5b09d35473615e7142f5570f5c5fad0caf68bd2 |
| commit-date: 2020-03-31 |
| host: {} |
| release: 1.44.0-nightly |
| LLVM version: 9.0 |
| ", |
| rustc_host() |
| ); |
| |
| let beta1 = format!( |
| "\ |
| rustc 1.43.0-beta.3 (4c587bbda 2020-03-25) |
| binary: rustc |
| commit-hash: 4c587bbda04ab55aaf56feab11dfdfe387a85d7a |
| commit-date: 2020-03-25 |
| host: {} |
| release: 1.43.0-beta.3 |
| LLVM version: 9.0 |
| ", |
| rustc_host() |
| ); |
| |
| let beta2 = format!( |
| "\ |
| rustc 1.42.0-beta.5 (4e1c5f0e9 2020-02-28) |
| binary: rustc |
| commit-hash: 4e1c5f0e9769a588b91c977e3d81e140209ef3a2 |
| commit-date: 2020-02-28 |
| host: {} |
| release: 1.42.0-beta.5 |
| LLVM version: 9.0 |
| ", |
| rustc_host() |
| ); |
| |
| let stable1 = format!( |
| "\ |
| rustc 1.42.0 (b8cedc004 2020-03-09) |
| binary: rustc |
| commit-hash: b8cedc00407a4c56a3bda1ed605c6fc166655447 |
| commit-date: 2020-03-09 |
| host: {} |
| release: 1.42.0 |
| LLVM version: 9.0 |
| ", |
| rustc_host() |
| ); |
| |
| let stable2 = format!( |
| "\ |
| rustc 1.41.1 (f3e1a954d 2020-02-24) |
| binary: rustc |
| commit-hash: f3e1a954d2ead4e2fc197c7da7d71e6c61bad196 |
| commit-date: 2020-02-24 |
| host: {} |
| release: 1.41.1 |
| LLVM version: 9.0 |
| ", |
| rustc_host() |
| ); |
| |
| let compiler = project() |
| .at("compiler") |
| .file("Cargo.toml", &basic_manifest("compiler", "0.1.0")) |
| .file( |
| "src/main.rs", |
| r#" |
| fn main() { |
| if std::env::args_os().any(|a| a == "-vV") { |
| print!("{}", env!("FUNKY_VERSION_TEST")); |
| return; |
| } |
| let mut cmd = std::process::Command::new("rustc"); |
| cmd.args(std::env::args_os().skip(1)); |
| assert!(cmd.status().unwrap().success()); |
| } |
| "#, |
| ) |
| .build(); |
| |
| let makeit = |version, vv| { |
| // Force a rebuild. |
| compiler.target_debug_dir().join("deps").rm_rf(); |
| compiler.cargo("build").env("FUNKY_VERSION_TEST", vv).run(); |
| fs::rename(compiler.bin("compiler"), compiler.bin(version)).unwrap(); |
| }; |
| makeit("nightly1", nightly1); |
| makeit("nightly2", nightly2); |
| makeit("beta1", beta1); |
| makeit("beta2", beta2); |
| makeit("stable1", stable1); |
| makeit("stable2", stable2); |
| |
| // Run `cargo check` with different rustc versions to observe its behavior. |
| let p = project().file("src/lib.rs", "").build(); |
| |
| // Runs `cargo check` and returns the rmeta filename created. |
| // Checks that the freshness matches the given value. |
| let check = |version, fresh| -> String { |
| let output = p |
| .cargo("check --message-format=json") |
| .env("RUSTC", compiler.bin(version)) |
| .exec_with_output() |
| .unwrap(); |
| // Collect the filenames generated. |
| let mut artifacts: Vec<_> = std::str::from_utf8(&output.stdout) |
| .unwrap() |
| .lines() |
| .filter_map(|line| { |
| let value: serde_json::Value = serde_json::from_str(line).unwrap(); |
| if value["reason"].as_str().unwrap() == "compiler-artifact" { |
| assert_eq!(value["fresh"].as_bool().unwrap(), fresh); |
| let filenames = value["filenames"].as_array().unwrap(); |
| assert_eq!(filenames.len(), 1); |
| Some(filenames[0].to_string()) |
| } else { |
| None |
| } |
| }) |
| .collect(); |
| // Should only generate one rmeta file. |
| assert_eq!(artifacts.len(), 1); |
| artifacts.pop().unwrap() |
| }; |
| |
| let nightly1_name = check("nightly1", false); |
| assert_eq!(check("nightly1", true), nightly1_name); |
| assert_eq!(check("nightly2", false), nightly1_name); // same as before |
| assert_eq!(check("nightly2", true), nightly1_name); |
| // Should rebuild going back to nightly1. |
| assert_eq!(check("nightly1", false), nightly1_name); |
| |
| let beta1_name = check("beta1", false); |
| assert_ne!(beta1_name, nightly1_name); |
| assert_eq!(check("beta1", true), beta1_name); |
| assert_eq!(check("beta2", false), beta1_name); // same as before |
| assert_eq!(check("beta2", true), beta1_name); |
| // Should rebuild going back to beta1. |
| assert_eq!(check("beta1", false), beta1_name); |
| |
| let stable1_name = check("stable1", false); |
| assert_ne!(stable1_name, nightly1_name); |
| assert_ne!(stable1_name, beta1_name); |
| let stable2_name = check("stable2", false); |
| assert_ne!(stable1_name, stable2_name); |
| // Check everything is fresh. |
| assert_eq!(check("stable1", true), stable1_name); |
| assert_eq!(check("stable2", true), stable2_name); |
| assert_eq!(check("beta1", true), beta1_name); |
| assert_eq!(check("nightly1", true), nightly1_name); |
| } |
| |
| #[cargo_test] |
| fn linking_interrupted() { |
| // Interrupt during the linking phase shouldn't leave test executable as "fresh". |
| |
| // This is used to detect when linking starts, then to pause the linker so |
| // that the test can kill cargo. |
| let link_listener = TcpListener::bind("127.0.0.1:0").unwrap(); |
| let link_addr = link_listener.local_addr().unwrap(); |
| |
| // This is used to detect when rustc exits. |
| let rustc_listener = TcpListener::bind("127.0.0.1:0").unwrap(); |
| let rustc_addr = rustc_listener.local_addr().unwrap(); |
| |
| // Create a linker that we can interrupt. |
| let linker = project() |
| .at("linker") |
| .file("Cargo.toml", &basic_manifest("linker", "1.0.0")) |
| .file( |
| "src/main.rs", |
| &r#" |
| fn main() { |
| // Figure out the output filename. |
| let output = match std::env::args().find(|a| a.starts_with("/OUT:")) { |
| Some(s) => s[5..].to_string(), |
| None => { |
| let mut args = std::env::args(); |
| loop { |
| if args.next().unwrap() == "-o" { |
| break; |
| } |
| } |
| args.next().unwrap() |
| } |
| }; |
| std::fs::remove_file(&output).unwrap(); |
| std::fs::write(&output, "").unwrap(); |
| // Tell the test that we are ready to be interrupted. |
| let mut socket = std::net::TcpStream::connect("__ADDR__").unwrap(); |
| // Wait for the test to kill us. |
| std::thread::sleep(std::time::Duration::new(60, 0)); |
| } |
| "# |
| .replace("__ADDR__", &link_addr.to_string()), |
| ) |
| .build(); |
| linker.cargo("build").run(); |
| |
| // Create a wrapper around rustc that will tell us when rustc is finished. |
| let rustc = project() |
| .at("rustc-waiter") |
| .file("Cargo.toml", &basic_manifest("rustc-waiter", "1.0.0")) |
| .file( |
| "src/main.rs", |
| &r#" |
| fn main() { |
| let mut conn = None; |
| // Check for a normal build (not -vV or --print). |
| if std::env::args().any(|arg| arg == "t1") { |
| // Tell the test that rustc has started. |
| conn = Some(std::net::TcpStream::connect("__ADDR__").unwrap()); |
| } |
| let status = std::process::Command::new("rustc") |
| .args(std::env::args().skip(1)) |
| .status() |
| .expect("rustc to run"); |
| std::process::exit(status.code().unwrap_or(1)); |
| } |
| "# |
| .replace("__ADDR__", &rustc_addr.to_string()), |
| ) |
| .build(); |
| rustc.cargo("build").run(); |
| |
| // Build it once so that the fingerprint gets saved to disk. |
| let p = project() |
| .file("src/lib.rs", "") |
| .file("tests/t1.rs", "") |
| .build(); |
| p.cargo("test --test t1 --no-run").run(); |
| |
| // Make a change, start a build, then interrupt it. |
| p.change_file("src/lib.rs", "// modified"); |
| let linker_env = format!("CARGO_TARGET_{}_LINKER", rustc_host_env()); |
| // NOTE: This assumes that the paths to the linker or rustc are not in the |
| // fingerprint. But maybe they should be? |
| let mut cmd = p |
| .cargo("test --test t1 --no-run") |
| .env(&linker_env, linker.bin("linker")) |
| .env("RUSTC", rustc.bin("rustc-waiter")) |
| .build_command(); |
| let mut child = cmd |
| .stdout(Stdio::null()) |
| .stderr(Stdio::null()) |
| .env("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE", "1") |
| .spawn() |
| .unwrap(); |
| // Wait for rustc to start. |
| let mut rustc_conn = rustc_listener.accept().unwrap().0; |
| // Wait for linking to start. |
| drop(link_listener.accept().unwrap()); |
| |
| // Interrupt the child. |
| death::ctrl_c(&mut child); |
| assert!(!child.wait().unwrap().success()); |
| // Wait for rustc to exit. If we don't wait, then the command below could |
| // start while rustc is still being torn down. |
| let mut buf = [0]; |
| drop(rustc_conn.read_exact(&mut buf)); |
| |
| // Build again, shouldn't be fresh. |
| p.cargo("test --test t1 -v") |
| .with_stderr( |
| "\ |
| [DIRTY] foo v0.0.1 ([..]): the config settings changed |
| [COMPILING] foo [..] |
| [RUNNING] `rustc --crate-name foo [..] |
| [RUNNING] `rustc --crate-name t1 [..] |
| [FINISHED] [..] |
| [RUNNING] `[..]target/debug/deps/t1-[..][EXE]` |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| #[cfg_attr( |
| not(all(target_arch = "x86_64", target_os = "windows", target_env = "msvc")), |
| ignore |
| )] |
| fn lld_is_fresh() { |
| // Check for bug when using lld linker that it remains fresh with dylib. |
| let p = project() |
| .file( |
| ".cargo/config", |
| r#" |
| [target.x86_64-pc-windows-msvc] |
| linker = "rust-lld" |
| rustflags = ["-C", "link-arg=-fuse-ld=lld"] |
| "#, |
| ) |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.1.0" |
| |
| [lib] |
| crate-type = ["dylib"] |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build").run(); |
| p.cargo("build -v") |
| .with_stderr("[FRESH] foo [..]\n[FINISHED] [..]") |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn env_in_code_causes_rebuild() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.1.0" |
| "#, |
| ) |
| .file( |
| "src/main.rs", |
| r#" |
| fn main() { |
| println!("{:?}", option_env!("FOO")); |
| println!("{:?}", option_env!("FOO\nBAR")); |
| } |
| "#, |
| ) |
| .build(); |
| |
| p.cargo("build").env_remove("FOO").run(); |
| p.cargo("build") |
| .env_remove("FOO") |
| .with_stderr("[FINISHED] [..]") |
| .run(); |
| p.cargo("build -v") |
| .env("FOO", "bar") |
| .with_stderr( |
| "\ |
| [DIRTY] foo [..]: the environment variable FOO changed |
| [COMPILING][..] |
| [RUNNING] `rustc [..] |
| [FINISHED][..]", |
| ) |
| .run(); |
| p.cargo("build") |
| .env("FOO", "bar") |
| .with_stderr("[FINISHED][..]") |
| .run(); |
| p.cargo("build -v") |
| .env("FOO", "baz") |
| .with_stderr( |
| "\ |
| [DIRTY] foo [..]: the environment variable FOO changed |
| [COMPILING][..] |
| [RUNNING] `rustc [..] |
| [FINISHED][..]", |
| ) |
| .run(); |
| p.cargo("build") |
| .env("FOO", "baz") |
| .with_stderr("[FINISHED][..]") |
| .run(); |
| p.cargo("build -v") |
| .env_remove("FOO") |
| .with_stderr( |
| "\ |
| [DIRTY] foo [..]: the environment variable FOO changed |
| [COMPILING][..] |
| [RUNNING] `rustc [..] |
| [FINISHED][..]", |
| ) |
| .run(); |
| p.cargo("build") |
| .env_remove("FOO") |
| .with_stderr("[FINISHED][..]") |
| .run(); |
| |
| let interesting = " #!$\nabc\r\\\t\u{8}\r\n"; |
| p.cargo("build").env("FOO", interesting).run(); |
| p.cargo("build") |
| .env("FOO", interesting) |
| .with_stderr("[FINISHED][..]") |
| .run(); |
| |
| p.cargo("build").env("FOO\nBAR", interesting).run(); |
| p.cargo("build") |
| .env("FOO\nBAR", interesting) |
| .with_stderr("[FINISHED][..]") |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn env_build_script_no_rebuild() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.1.0" |
| "#, |
| ) |
| .file( |
| "build.rs", |
| r#" |
| fn main() { |
| println!("cargo:rustc-env=FOO=bar"); |
| } |
| "#, |
| ) |
| .file( |
| "src/main.rs", |
| r#" |
| fn main() { |
| println!("{:?}", env!("FOO")); |
| } |
| "#, |
| ) |
| .build(); |
| |
| p.cargo("build").run(); |
| p.cargo("build").with_stderr("[FINISHED] [..]").run(); |
| } |
| |
| #[cargo_test] |
| fn cargo_env_changes() { |
| // Checks that changes to the env var CARGO in the dep-info file triggers |
| // a rebuild. |
| let p = project() |
| .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) |
| .file( |
| "src/main.rs", |
| r#" |
| fn main() { |
| println!("{:?}", env!("CARGO")); |
| } |
| "#, |
| ) |
| .build(); |
| |
| let cargo_exe = cargo_test_support::cargo_exe(); |
| let other_cargo_path = p.root().join(cargo_exe.file_name().unwrap()); |
| std::fs::hard_link(&cargo_exe, &other_cargo_path).unwrap(); |
| let other_cargo = || { |
| let mut pb = cargo_test_support::process(&other_cargo_path); |
| pb.cwd(p.root()); |
| cargo_test_support::execs().with_process_builder(pb) |
| }; |
| |
| p.cargo("check").run(); |
| other_cargo() |
| .arg("check") |
| .arg("-v") |
| .with_stderr( |
| "\ |
| [DIRTY] foo v1.0.0 ([..]): the environment variable CARGO changed |
| [CHECKING] foo [..] |
| [RUNNING] `rustc [..] |
| [FINISHED] [..] |
| ", |
| ) |
| .run(); |
| |
| // And just to confirm that without using env! it doesn't rebuild. |
| p.change_file("src/main.rs", "fn main() {}"); |
| p.cargo("check") |
| .with_stderr( |
| "\ |
| [CHECKING] foo [..] |
| [FINISHED] [..] |
| ", |
| ) |
| .run(); |
| other_cargo() |
| .arg("check") |
| .arg("-v") |
| .with_stderr( |
| "\ |
| [FRESH] foo [..] |
| [FINISHED] [..] |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn changing_linker() { |
| // Changing linker should rebuild. |
| let p = project().file("src/main.rs", "fn main() {}").build(); |
| p.cargo("build").run(); |
| let linker_env = format!("CARGO_TARGET_{}_LINKER", rustc_host_env()); |
| p.cargo("build --verbose") |
| .env(&linker_env, "nonexistent-linker") |
| .with_status(101) |
| .with_stderr_contains( |
| "\ |
| [COMPILING] foo v0.0.1 ([..]) |
| [RUNNING] `rustc [..] -C linker=nonexistent-linker [..]` |
| [ERROR] [..]linker[..] |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn verify_source_before_recompile() { |
| Package::new("bar", "0.1.0") |
| .file("src/lib.rs", "") |
| .publish(); |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.1.0" |
| |
| [dependencies] |
| bar = "0.1.0" |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .build(); |
| |
| p.cargo("vendor --respect-source-config").run(); |
| p.change_file( |
| ".cargo/config.toml", |
| r#" |
| [source.crates-io] |
| replace-with = 'vendor' |
| |
| [source.vendor] |
| directory = 'vendor' |
| "#, |
| ); |
| // Sanity check: vendoring works correctly. |
| p.cargo("check --verbose") |
| .with_stderr_contains("[RUNNING] `rustc --crate-name bar [CWD]/vendor/bar/src/lib.rs[..]") |
| .run(); |
| // Now modify vendored crate. |
| p.change_file( |
| "vendor/bar/src/lib.rs", |
| r#"compile_error!("You shall not pass!");"#, |
| ); |
| // Should ignore modifed sources without any recompile. |
| p.cargo("check --verbose") |
| .with_stderr( |
| "\ |
| [FRESH] bar v0.1.0 |
| [FRESH] foo v0.1.0 ([CWD]) |
| [FINISHED] dev [..] |
| ", |
| ) |
| .run(); |
| |
| // Add a `RUSTFLAGS` to trigger a recompile. |
| // |
| // Cargo should refuse to build because of checksum verfication failure. |
| // Cargo shouldn't recompile dependency `bar`. |
| p.cargo("check --verbose") |
| .env("RUSTFLAGS", "-W warnings") |
| .with_status(101) |
| .with_stderr( |
| "\ |
| error: the listed checksum of `[CWD]/vendor/bar/src/lib.rs` has changed: |
| expected: [..] |
| actual: [..] |
| |
| directory sources are not [..] |
| ", |
| ) |
| .run(); |
| } |