--- /dev/null
-all possible versions conflict with previously selected packages.
-required by package `baz v0.1.0`
+ use std::env;
+ use std::fs::{self, File};
+ use std::io::prelude::*;
+
+ use cargo::util::paths::dylib_path_envvar;
+ use cargo::util::{process, ProcessBuilder};
+ use cargotest::{is_nightly, rustc_host, sleep_ms};
+ use cargotest::support::paths::{CargoPathExt,root};
+ use cargotest::support::{ProjectBuilder};
+ use cargotest::support::{project, execs, main_file, basic_bin_manifest};
+ use cargotest::support::registry::Package;
+ use cargotest::ChannelChanger;
+ use hamcrest::{assert_that, existing_file, existing_dir, is_not};
+ use tempdir::TempDir;
+
+ #[test]
+ fn cargo_compile_simple() {
+ let p = project("foo")
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .build();
+
+ assert_that(p.cargo("build"), execs().with_status(0));
+ assert_that(&p.bin("foo"), existing_file());
+
+ assert_that(process(&p.bin("foo")),
+ execs().with_status(0).with_stdout("i am foo\n"));
+ }
+
+ #[test]
+ fn cargo_fail_with_no_stderr() {
+ let p = project("foo")
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", &String::from("refusal"))
+ .build();
+ assert_that(p.cargo("build").arg("--message-format=json"), execs().with_status(101)
+ .with_stderr_does_not_contain("--- stderr"));
+ }
+
+ /// Check that the `CARGO_INCREMENTAL` environment variable results in
+ /// `rustc` getting `-Zincremental` passed to it.
+ #[test]
+ fn cargo_compile_incremental() {
+ if !is_nightly() {
+ return
+ }
+
+ let p = project("foo")
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .build();
+
+ assert_that(
+ p.cargo("build").arg("-v").env("CARGO_INCREMENTAL", "1"),
+ execs().with_stderr_contains(
+ "[RUNNING] `rustc [..] -C incremental=[..][/]target[/]debug[/]incremental[..]`\n")
+ .with_status(0));
+
+ assert_that(
+ p.cargo("test").arg("-v").env("CARGO_INCREMENTAL", "1"),
+ execs().with_stderr_contains(
+ "[RUNNING] `rustc [..] -C incremental=[..][/]target[/]debug[/]incremental[..]`\n")
+ .with_status(0));
+ }
+
+ #[test]
+ fn incremental_profile() {
+ if !is_nightly() {
+ return
+ }
+
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ authors = []
+
+ [profile.dev]
+ incremental = false
+
+ [profile.release]
+ incremental = true
+ "#)
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ assert_that(
+ p.cargo("build").arg("-v").env_remove("CARGO_INCREMENTAL"),
+ execs().with_stderr_does_not_contain("[..]C incremental=[..]")
+ .with_status(0));
+
+ assert_that(
+ p.cargo("build").arg("-v").env("CARGO_INCREMENTAL", "1"),
+ execs().with_stderr_contains("[..]C incremental=[..]")
+ .with_status(0));
+
+ assert_that(
+ p.cargo("build").arg("--release").arg("-v").env_remove("CARGO_INCREMENTAL"),
+ execs().with_stderr_contains("[..]C incremental=[..]")
+ .with_status(0));
+
+ assert_that(
+ p.cargo("build").arg("--release").arg("-v").env("CARGO_INCREMENTAL", "0"),
+ execs().with_stderr_does_not_contain("[..]C incremental=[..]")
+ .with_status(0));
+ }
+
+ #[test]
+ fn incremental_config() {
+ if !is_nightly() {
+ return
+ }
+
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ authors = []
+ "#)
+ .file("src/main.rs", "fn main() {}")
+ .file(".cargo/config", r#"
+ [build]
+ incremental = false
+ "#)
+ .build();
+
+ assert_that(
+ p.cargo("build").arg("-v").env_remove("CARGO_INCREMENTAL"),
+ execs().with_stderr_does_not_contain("[..]C incremental=[..]")
+ .with_status(0));
+
+ assert_that(
+ p.cargo("build").arg("-v").env("CARGO_INCREMENTAL", "1"),
+ execs().with_stderr_contains("[..]C incremental=[..]")
+ .with_status(0));
+ }
+
+ #[test]
+ fn cargo_compile_manifest_path() {
+ let p = project("foo")
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .build();
+
+ assert_that(p.cargo("build")
+ .arg("--manifest-path").arg("foo/Cargo.toml")
+ .cwd(p.root().parent().unwrap()),
+ execs().with_status(0));
+ assert_that(&p.bin("foo"), existing_file());
+ }
+
+ #[test]
+ fn cargo_compile_with_invalid_manifest() {
+ let p = project("foo")
+ .file("Cargo.toml", "")
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs()
+ .with_status(101)
+ .with_stderr("\
+ [ERROR] failed to parse manifest at `[..]`
+
+ Caused by:
+ virtual manifests must be configured with [workspace]
+ "))
+ }
+
+ #[test]
+ fn cargo_compile_with_invalid_manifest2() {
+ let p = project("foo")
+ .file("Cargo.toml", r"
+ [project]
+ foo = bar
+ ")
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs()
+ .with_status(101)
+ .with_stderr("\
+ [ERROR] failed to parse manifest at `[..]`
+
+ Caused by:
+ could not parse input as TOML
+
+ Caused by:
+ invalid number at line 3
+ "))
+ }
+
+ #[test]
+ fn cargo_compile_with_invalid_manifest3() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/Cargo.toml", "a = bar")
+ .build();
+
+ assert_that(p.cargo("build").arg("--manifest-path")
+ .arg("src/Cargo.toml"),
+ execs()
+ .with_status(101)
+ .with_stderr("\
+ [ERROR] failed to parse manifest at `[..]`
+
+ Caused by:
+ could not parse input as TOML
+
+ Caused by:
+ invalid number at line 1
+ "))
+ }
+
+ #[test]
+ fn cargo_compile_duplicate_build_targets() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [lib]
+ name = "main"
+ path = "src/main.rs"
+ crate-type = ["dylib"]
+
+ [dependencies]
+ "#)
+ .file("src/main.rs", r#"
+ #![allow(warnings)]
+ fn main() {}
+ "#)
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs()
+ .with_status(0)
+ .with_stderr("\
+ warning: file found to be present in multiple build targets: [..]main.rs
+ [COMPILING] foo v0.0.1 ([..])
+ [FINISHED] [..]
+ "));
+ }
+
+ #[test]
+ fn cargo_compile_with_invalid_version() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ authors = []
+ version = "1.0"
+ "#)
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs()
+ .with_status(101)
+ .with_stderr("\
+ [ERROR] failed to parse manifest at `[..]`
+
+ Caused by:
+ Expected dot for key `project.version`
+ "))
+
+ }
+
+ #[test]
+ fn cargo_compile_with_invalid_package_name() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = ""
+ authors = []
+ version = "0.0.0"
+ "#)
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs()
+ .with_status(101)
+ .with_stderr("\
+ [ERROR] failed to parse manifest at `[..]`
+
+ Caused by:
+ package name cannot be an empty string
+ "))
+ }
+
+ #[test]
+ fn cargo_compile_with_invalid_bin_target_name() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.0"
+
+ [[bin]]
+ name = ""
+ "#)
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs()
+ .with_status(101)
+ .with_stderr("\
+ [ERROR] failed to parse manifest at `[..]`
+
+ Caused by:
+ binary target names cannot be empty
+ "))
+ }
+
+ #[test]
+ fn cargo_compile_with_forbidden_bin_target_name() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.0"
+
+ [[bin]]
+ name = "build"
+ "#)
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs()
+ .with_status(101)
+ .with_stderr("\
+ [ERROR] failed to parse manifest at `[..]`
+
+ Caused by:
+ the binary target name `build` is forbidden
+ "))
+ }
+
+ #[test]
+ fn cargo_compile_with_invalid_lib_target_name() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.0"
+
+ [lib]
+ name = ""
+ "#)
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs()
+ .with_status(101)
+ .with_stderr("\
+ [ERROR] failed to parse manifest at `[..]`
+
+ Caused by:
+ library target names cannot be empty
+ "))
+ }
+
+ #[test]
+ fn cargo_compile_without_manifest() {
+ let tmpdir = TempDir::new("cargo").unwrap();
+ let p = ProjectBuilder::new("foo", tmpdir.path().to_path_buf()).build();
+
+ assert_that(p.cargo("build"),
+ execs().with_status(101)
+ .with_stderr("\
+ [ERROR] could not find `Cargo.toml` in `[..]` or any parent directory
+ "));
+ }
+
+ #[test]
+ fn cargo_compile_with_invalid_code() {
+ let p = project("foo")
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", "invalid rust code!")
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs()
+ .with_status(101)
+ .with_stderr_contains("\
+ [ERROR] Could not compile `foo`.
+
+ To learn more, run the command again with --verbose.\n"));
+ assert_that(&p.root().join("Cargo.lock"), existing_file());
+ }
+
+ #[test]
+ fn cargo_compile_with_invalid_code_in_deps() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.bar]
+ path = "../bar"
+ [dependencies.baz]
+ path = "../baz"
+ "#)
+ .file("src/main.rs", "invalid rust code!")
+ .build();
+ let _bar = project("bar")
+ .file("Cargo.toml", &basic_bin_manifest("bar"))
+ .file("src/lib.rs", "invalid rust code!")
+ .build();
+ let _baz = project("baz")
+ .file("Cargo.toml", &basic_bin_manifest("baz"))
+ .file("src/lib.rs", "invalid rust code!")
+ .build();
+ assert_that(p.cargo("build"), execs().with_status(101));
+ }
+
+ #[test]
+ fn cargo_compile_with_warnings_in_the_root_package() {
+ let p = project("foo")
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", "fn main() {} fn dead() {}")
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs().with_status(0).with_stderr_contains("\
+ [..]function is never used: `dead`[..]
+ "));
+ }
+
+ #[test]
+ fn cargo_compile_with_warnings_in_a_dep_package() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [dependencies.bar]
+ path = "bar"
+
+ [[bin]]
+
+ name = "foo"
+ "#)
+ .file("src/foo.rs",
+ &main_file(r#""{}", bar::gimme()"#, &["bar"]))
+ .file("bar/Cargo.toml", r#"
+ [project]
+
+ name = "bar"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [lib]
+
+ name = "bar"
+ "#)
+ .file("bar/src/bar.rs", r#"
+ pub fn gimme() -> &'static str {
+ "test passed"
+ }
+
+ fn dead() {}
+ "#)
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs().with_status(0).with_stderr_contains("\
+ [..]function is never used: `dead`[..]
+ "));
+
+ assert_that(&p.bin("foo"), existing_file());
+
+ assert_that(
+ process(&p.bin("foo")),
+ execs().with_status(0).with_stdout("test passed\n"));
+ }
+
+ #[test]
+ fn cargo_compile_with_nested_deps_inferred() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [dependencies.bar]
+ path = 'bar'
+
+ [[bin]]
+ name = "foo"
+ "#)
+ .file("src/foo.rs",
+ &main_file(r#""{}", bar::gimme()"#, &["bar"]))
+ .file("bar/Cargo.toml", r#"
+ [project]
+
+ name = "bar"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [dependencies.baz]
+ path = "../baz"
+ "#)
+ .file("bar/src/lib.rs", r#"
+ extern crate baz;
+
+ pub fn gimme() -> String {
+ baz::gimme()
+ }
+ "#)
+ .file("baz/Cargo.toml", r#"
+ [project]
+
+ name = "baz"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+ "#)
+ .file("baz/src/lib.rs", r#"
+ pub fn gimme() -> String {
+ "test passed".to_string()
+ }
+ "#)
+ .build();
+
+ p.cargo("build")
+ .exec_with_output()
+ .unwrap();
+
+ assert_that(&p.bin("foo"), existing_file());
+ assert_that(&p.bin("libbar.rlib"), is_not(existing_file()));
+ assert_that(&p.bin("libbaz.rlib"), is_not(existing_file()));
+
+ assert_that(
+ process(&p.bin("foo")),
+ execs().with_status(0).with_stdout("test passed\n"));
+ }
+
+ #[test]
+ fn cargo_compile_with_nested_deps_correct_bin() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [dependencies.bar]
+ path = "bar"
+
+ [[bin]]
+ name = "foo"
+ "#)
+ .file("src/main.rs",
+ &main_file(r#""{}", bar::gimme()"#, &["bar"]))
+ .file("bar/Cargo.toml", r#"
+ [project]
+
+ name = "bar"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [dependencies.baz]
+ path = "../baz"
+ "#)
+ .file("bar/src/lib.rs", r#"
+ extern crate baz;
+
+ pub fn gimme() -> String {
+ baz::gimme()
+ }
+ "#)
+ .file("baz/Cargo.toml", r#"
+ [project]
+
+ name = "baz"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+ "#)
+ .file("baz/src/lib.rs", r#"
+ pub fn gimme() -> String {
+ "test passed".to_string()
+ }
+ "#)
+ .build();
+
+ p.cargo("build")
+ .exec_with_output()
+ .unwrap();
+
+ assert_that(&p.bin("foo"), existing_file());
+ assert_that(&p.bin("libbar.rlib"), is_not(existing_file()));
+ assert_that(&p.bin("libbaz.rlib"), is_not(existing_file()));
+
+ assert_that(
+ process(&p.bin("foo")),
+ execs().with_status(0).with_stdout("test passed\n"));
+ }
+
+ #[test]
+ fn cargo_compile_with_nested_deps_shorthand() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [dependencies.bar]
+ path = "bar"
+ "#)
+ .file("src/main.rs",
+ &main_file(r#""{}", bar::gimme()"#, &["bar"]))
+ .file("bar/Cargo.toml", r#"
+ [project]
+
+ name = "bar"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [dependencies.baz]
+ path = "../baz"
+
+ [lib]
+
+ name = "bar"
+ "#)
+ .file("bar/src/bar.rs", r#"
+ extern crate baz;
+
+ pub fn gimme() -> String {
+ baz::gimme()
+ }
+ "#)
+ .file("baz/Cargo.toml", r#"
+ [project]
+
+ name = "baz"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [lib]
+
+ name = "baz"
+ "#)
+ .file("baz/src/baz.rs", r#"
+ pub fn gimme() -> String {
+ "test passed".to_string()
+ }
+ "#)
+ .build();
+
+ p.cargo("build")
+ .exec_with_output()
+ .unwrap();
+
+ assert_that(&p.bin("foo"), existing_file());
+ assert_that(&p.bin("libbar.rlib"), is_not(existing_file()));
+ assert_that(&p.bin("libbaz.rlib"), is_not(existing_file()));
+
+ assert_that(
+ process(&p.bin("foo")),
+ execs().with_status(0).with_stdout("test passed\n"));
+ }
+
+ #[test]
+ fn cargo_compile_with_nested_deps_longhand() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [dependencies.bar]
+ path = "bar"
+ version = "0.5.0"
+
+ [[bin]]
+
+ name = "foo"
+ "#)
+ .file("src/foo.rs",
+ &main_file(r#""{}", bar::gimme()"#, &["bar"]))
+ .file("bar/Cargo.toml", r#"
+ [project]
+
+ name = "bar"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [dependencies.baz]
+ path = "../baz"
+ version = "0.5.0"
+
+ [lib]
+
+ name = "bar"
+ "#)
+ .file("bar/src/bar.rs", r#"
+ extern crate baz;
+
+ pub fn gimme() -> String {
+ baz::gimme()
+ }
+ "#)
+ .file("baz/Cargo.toml", r#"
+ [project]
+
+ name = "baz"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [lib]
+
+ name = "baz"
+ "#)
+ .file("baz/src/baz.rs", r#"
+ pub fn gimme() -> String {
+ "test passed".to_string()
+ }
+ "#)
+ .build();
+
+ assert_that(p.cargo("build"), execs());
+
+ assert_that(&p.bin("foo"), existing_file());
+ assert_that(&p.bin("libbar.rlib"), is_not(existing_file()));
+ assert_that(&p.bin("libbaz.rlib"), is_not(existing_file()));
+
+ assert_that(process(&p.bin("foo")),
+ execs().with_status(0).with_stdout("test passed\n"));
+ }
+
+ // Check that Cargo gives a sensible error if a dependency can't be found
+ // because of a name mismatch.
+ #[test]
+ fn cargo_compile_with_dep_name_mismatch() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+
+ name = "foo"
+ version = "0.0.1"
+ authors = ["wycats@example.com"]
+
+ [[bin]]
+
+ name = "foo"
+
+ [dependencies.notquitebar]
+
+ path = "bar"
+ "#)
+ .file("src/bin/foo.rs", &main_file(r#""i am foo""#, &["bar"]))
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/bar.rs", &main_file(r#""i am bar""#, &[]))
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs().with_status(101).with_stderr(&format!(
+ r#"error: no matching package named `notquitebar` found
+ location searched: {proj_dir}/bar
+ required by package `foo v0.0.1 ({proj_dir})`
+ "#, proj_dir = p.url())));
+ }
+
+ #[test]
+ fn cargo_compile_with_filename() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/bin/a.rs", r#"
+ extern crate foo;
+ fn main() { println!("hello a.rs"); }
+ "#)
+ .file("examples/a.rs", r#"
+ fn main() { println!("example"); }
+ "#)
+ .build();
+
+ assert_that(p.cargo("build").arg("--bin").arg("bin.rs"),
+ execs().with_status(101).with_stderr("\
+ [ERROR] no bin target named `bin.rs`"));
+
+ assert_that(p.cargo("build").arg("--bin").arg("a.rs"),
+ execs().with_status(101).with_stderr("\
+ [ERROR] no bin target named `a.rs`
+
+ Did you mean `a`?"));
+
+ assert_that(p.cargo("build").arg("--example").arg("example.rs"),
+ execs().with_status(101).with_stderr("\
+ [ERROR] no example target named `example.rs`"));
+
+ assert_that(p.cargo("build").arg("--example").arg("a.rs"),
+ execs().with_status(101).with_stderr("\
+ [ERROR] no example target named `a.rs`
+
+ Did you mean `a`?"));
+ }
+
+ #[test]
+ fn cargo_compile_path_with_offline() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.bar]
+ path = "bar"
+ "#)
+ .file("src/lib.rs", "")
+ .file("bar/Cargo.toml", r#"
+ [package]
+ name = "bar"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("bar/src/lib.rs", "")
+ .build();
+
+ assert_that(p.cargo("build").masquerade_as_nightly_cargo().arg("-Zoffline"),
+ execs().with_status(0));
+ }
+
+ #[test]
+ fn cargo_compile_with_downloaded_dependency_with_offline() {
+ Package::new("present_dep", "1.2.3")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "present_dep"
+ version = "1.2.3"
+ "#)
+ .file("src/lib.rs", "")
+ .publish();
+
+ {
+ // make package downloaded
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ present_dep = "1.2.3"
+ "#)
+ .file("src/lib.rs", "")
+ .build();
+ assert_that(p.cargo("build"),execs().with_status(0));
+ }
+
+ let p2 = project("bar")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "bar"
+ version = "0.1.0"
+
+ [dependencies]
+ present_dep = "1.2.3"
+ "#)
+ .file("src/lib.rs", "")
+ .build();
+
+ assert_that(p2.cargo("build").masquerade_as_nightly_cargo().arg("-Zoffline"),
+ execs().with_status(0)
+ .with_stderr(format!("\
+ [COMPILING] present_dep v1.2.3
+ [COMPILING] bar v0.1.0 ([..])
+ [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]")));
+
+ }
+
+ #[test]
+ fn cargo_compile_offline_not_try_update() {
+ let p = project("bar")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "bar"
+ version = "0.1.0"
+
+ [dependencies]
+ not_cached_dep = "1.2.5"
+ "#)
+ .file("src/lib.rs", "")
+ .build();
+
+ assert_that(p.cargo("build").masquerade_as_nightly_cargo().arg("-Zoffline"),
+ execs().with_status(101)
+ .with_stderr("\
+ error: no matching package named `not_cached_dep` found
+ location searched: registry `[..]`
+ required by package `bar v0.1.0 ([..])`
+ As a reminder, you're using offline mode (-Z offline) \
+ which can sometimes cause surprising resolution failures, \
+ if this error is too confusing you may with to retry \
+ without the offline flag."));
+ }
+
+ #[test]
+ fn compile_offline_without_maxvers_cached(){
+ Package::new("present_dep", "1.2.1").publish();
+ Package::new("present_dep", "1.2.2").publish();
+
+ Package::new("present_dep", "1.2.3")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "present_dep"
+ version = "1.2.3"
+ "#)
+ .file("src/lib.rs", r#"pub fn get_version()->&'static str {"1.2.3"}"#)
+ .publish();
+
+ Package::new("present_dep", "1.2.5")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "present_dep"
+ version = "1.2.5"
+ "#)
+ .file("src/lib.rs", r#"pub fn get_version(){"1.2.5"}"#)
+ .publish();
+
+ {
+ // make package cached
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ present_dep = "=1.2.3"
+ "#)
+ .file("src/lib.rs", "")
+ .build();
+ assert_that(p.cargo("build"),execs().with_status(0));
+ }
+
+ let p2 = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ present_dep = "1.2"
+ "#)
+ .file("src/main.rs", "\
+ extern crate present_dep;
+ fn main(){
+ println!(\"{}\", present_dep::get_version());
+ }")
+ .build();
+
+ assert_that(p2.cargo("run").masquerade_as_nightly_cargo().arg("-Zoffline"),
+ execs().with_status(0)
+ .with_stderr(format!("\
+ [COMPILING] present_dep v1.2.3
+ [COMPILING] foo v0.1.0 ({url})
+ [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+ Running `[..]`", url = p2.url()))
+ .with_stdout("1.2.3")
+ );
+ }
+
+ #[test]
+ fn incompatible_dependencies() {
+ Package::new("bad", "0.1.0").publish();
+ Package::new("bad", "1.0.0").publish();
+ Package::new("bad", "1.0.1").publish();
+ Package::new("bad", "1.0.2").publish();
+ Package::new("foo", "0.1.0").dep("bad", "0.1.0").publish();
+ Package::new("bar", "0.1.1").dep("bad", "=1.0.0").publish();
+ Package::new("bar", "0.1.0").dep("bad", "=1.0.0").publish();
+ Package::new("baz", "0.1.2").dep("bad", ">=1.0.1").publish();
+ Package::new("baz", "0.1.1").dep("bad", ">=1.0.1").publish();
+ Package::new("baz", "0.1.0").dep("bad", ">=1.0.1").publish();
+
+ let p = project("transitive_load_test")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "incompatible_dependencies"
+ version = "0.0.1"
+
+ [dependencies]
+ foo = "0.1.0"
+ bar = "0.1.0"
+ baz = "0.1.0"
+ "#)
+ .file("src/main.rs", "fn main(){}")
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs().with_status(101)
+ .with_stderr_contains("\
+ error: failed to select a version for `bad`.
- possible versions to select: 1.0.2, 1.0.1"));
++ ... required by package `baz v0.1.0`
+ ... which is depended on by `incompatible_dependencies v0.0.1 ([..])`
++versions that meet the requirements `>= 1.0.1` are: 1.0.2, 1.0.1
++
++all possible versions conflict with previously selected packages.
++
+ previously selected package `bad v1.0.0`
+ ... which is depended on by `bar v0.1.0`
+ ... which is depended on by `incompatible_dependencies v0.0.1 ([..])`
-required by package `incompatible_dependencies v0.0.1 ([..])`
++
++failed to select a version for `bad` which could resolve this conflict"));
+ }
+
+ #[test]
+ fn incompatible_dependencies_with_multi_semver() {
+ Package::new("bad", "1.0.0").publish();
+ Package::new("bad", "1.0.1").publish();
+ Package::new("bad", "2.0.0").publish();
+ Package::new("bad", "2.0.1").publish();
+ Package::new("bar", "0.1.0").dep("bad", "=1.0.0").publish();
+ Package::new("baz", "0.1.0").dep("bad", ">=2.0.1").publish();
+
+ let p = project("transitive_load_test")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "incompatible_dependencies"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = "0.1.0"
+ baz = "0.1.0"
+ bad = ">=1.0.1, <=2.0.0"
+ "#)
+ .file("src/main.rs", "fn main(){}")
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs().with_status(101)
+ .with_stderr_contains("\
+ error: failed to select a version for `bad`.
++ ... required by package `incompatible_dependencies v0.0.1 ([..])`
++versions that meet the requirements `>= 1.0.1, <= 2.0.0` are: 2.0.0, 1.0.1
++
+ all possible versions conflict with previously selected packages.
- possible versions to select: 2.0.0, 1.0.1"));
++
+ previously selected package `bad v2.0.1`
+ ... which is depended on by `baz v0.1.0`
+ ... which is depended on by `incompatible_dependencies v0.0.1 ([..])`
++
+ previously selected package `bad v1.0.0`
+ ... which is depended on by `bar v0.1.0`
+ ... which is depended on by `incompatible_dependencies v0.0.1 ([..])`
++
++failed to select a version for `bad` which could resolve this conflict"));
+ }
+
+ #[test]
+ fn compile_offline_while_transitive_dep_not_cached() {
+ let bar = Package::new("bar", "1.0.0");
+ let bar_path = bar.archive_dst();
+ bar.publish();
+
+ let mut content = Vec::new();
+
+ let mut file = File::open(bar_path.clone()).ok().unwrap();
+ let _ok = file.read_to_end(&mut content).ok().unwrap();
+ drop(file);
+ drop(File::create(bar_path.clone()).ok().unwrap() );
+
+ Package::new("foo", "0.1.0").dep("bar", "1.0.0").publish();
+
+ let p = project("transitive_load_test")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "transitive_load_test"
+ version = "0.0.1"
+
+ [dependencies]
+ foo = "0.1.0"
+ "#)
+ .file("src/main.rs", "fn main(){}")
+ .build();
+
+ // simulate download foo, but fail to download bar
+ let _out = p.cargo("build").exec_with_output();
+
+ drop( File::create(bar_path).ok().unwrap().write_all(&content) );
+
+ assert_that(p.cargo("build").masquerade_as_nightly_cargo().arg("-Zoffline"),
+ execs().with_status(101)
+ .with_stderr("\
+ error: no matching package named `bar` found
+ location searched: registry `[..]`
+ required by package `foo v0.1.0`
+ ... which is depended on by `transitive_load_test v0.0.1 ([..]/transitive_load_test)`
+ As a reminder, you're using offline mode (-Z offline) \
+ which can sometimes cause surprising resolution failures, \
+ if this error is too confusing you may with to retry \
+ without the offline flag."));
+ }
+
+ #[test]
+ fn compile_path_dep_then_change_version() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.bar]
+ path = "bar"
+ "#)
+ .file("src/lib.rs", "")
+ .file("bar/Cargo.toml", r#"
+ [package]
+ name = "bar"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("bar/src/lib.rs", "")
+ .build();
+
+ assert_that(p.cargo("build"), execs().with_status(0));
+
+ File::create(&p.root().join("bar/Cargo.toml")).unwrap().write_all(br#"
+ [package]
+ name = "bar"
+ version = "0.0.2"
+ authors = []
+ "#).unwrap();
+
+ assert_that(p.cargo("build"),
+ execs().with_status(101).with_stderr("\
+ error: no matching version `= 0.0.1` found for package `bar`
+ location searched: [..]
+ versions found: 0.0.2
+ required by package `foo v0.0.1 ([..]/foo)`
+ consider running `cargo update` to update a path dependency's locked version
+ "));
+ }
+
+ #[test]
+ fn ignores_carriage_return_in_lockfile() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.1"
+ "#)
+ .file("src/main.rs", r#"
+ mod a; fn main() {}
+ "#)
+ .file("src/a.rs", "")
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs().with_status(0));
+
+ let lockfile = p.root().join("Cargo.lock");
+ let mut lock = String::new();
+ File::open(&lockfile).unwrap().read_to_string(&mut lock).unwrap();
+ let lock = lock.replace("\n", "\r\n");
+ File::create(&lockfile).unwrap().write_all(lock.as_bytes()).unwrap();
+ assert_that(p.cargo("build"),
+ execs().with_status(0));
+ }
+
+ #[test]
+ fn cargo_default_env_metadata_env_var() {
+ // Ensure that path dep + dylib + env_var get metadata
+ // (even though path_dep + dylib should not)
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.bar]
+ path = "bar"
+ "#)
+ .file("src/lib.rs", "// hi")
+ .file("bar/Cargo.toml", r#"
+ [package]
+ name = "bar"
+ version = "0.0.1"
+ authors = []
+
+ [lib]
+ name = "bar"
+ crate_type = ["dylib"]
+ "#)
+ .file("bar/src/lib.rs", "// hello")
+ .build();
+
+ // No metadata on libbar since it's a dylib path dependency
+ assert_that(p.cargo("build").arg("-v"),
+ execs().with_status(0).with_stderr(&format!("\
+ [COMPILING] bar v0.0.1 ({url}/bar)
+ [RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type dylib \
+ --emit=dep-info,link \
+ -C prefer-dynamic -C debuginfo=2 \
+ -C metadata=[..] \
+ --out-dir [..] \
+ -L dependency={dir}[/]target[/]debug[/]deps`
+ [COMPILING] foo v0.0.1 ({url})
+ [RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib \
+ --emit=dep-info,link -C debuginfo=2 \
+ -C metadata=[..] \
+ -C extra-filename=[..] \
+ --out-dir [..] \
+ -L dependency={dir}[/]target[/]debug[/]deps \
+ --extern bar={dir}[/]target[/]debug[/]deps[/]{prefix}bar{suffix}`
+ [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+ dir = p.root().display(),
+ url = p.url(),
+ prefix = env::consts::DLL_PREFIX,
+ suffix = env::consts::DLL_SUFFIX,
+ )));
+
+ assert_that(p.cargo("clean"), execs().with_status(0));
+
+ // If you set the env-var, then we expect metadata on libbar
+ assert_that(p.cargo("build").arg("-v").env("__CARGO_DEFAULT_LIB_METADATA", "stable"),
+ execs().with_status(0).with_stderr(&format!("\
+ [COMPILING] bar v0.0.1 ({url}/bar)
+ [RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type dylib \
+ --emit=dep-info,link \
+ -C prefer-dynamic -C debuginfo=2 \
+ -C metadata=[..] \
+ --out-dir [..] \
+ -L dependency={dir}[/]target[/]debug[/]deps`
+ [COMPILING] foo v0.0.1 ({url})
+ [RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib \
+ --emit=dep-info,link -C debuginfo=2 \
+ -C metadata=[..] \
+ -C extra-filename=[..] \
+ --out-dir [..] \
+ -L dependency={dir}[/]target[/]debug[/]deps \
+ --extern bar={dir}[/]target[/]debug[/]deps[/]{prefix}bar-[..]{suffix}`
+ [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+ ",
+ dir = p.root().display(),
+ url = p.url(),
+ prefix = env::consts::DLL_PREFIX,
+ suffix = env::consts::DLL_SUFFIX,
+ )));
+ }
+
+ #[test]
+ fn crate_env_vars() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.5.1-alpha.1"
+ description = "This is foo"
+ homepage = "http://example.com"
+ authors = ["wycats@example.com"]
+ "#)
+ .file("src/main.rs", r#"
+ extern crate foo;
+
+
+ static VERSION_MAJOR: &'static str = env!("CARGO_PKG_VERSION_MAJOR");
+ static VERSION_MINOR: &'static str = env!("CARGO_PKG_VERSION_MINOR");
+ static VERSION_PATCH: &'static str = env!("CARGO_PKG_VERSION_PATCH");
+ static VERSION_PRE: &'static str = env!("CARGO_PKG_VERSION_PRE");
+ static VERSION: &'static str = env!("CARGO_PKG_VERSION");
+ static CARGO_MANIFEST_DIR: &'static str = env!("CARGO_MANIFEST_DIR");
+ static PKG_NAME: &'static str = env!("CARGO_PKG_NAME");
+ static HOMEPAGE: &'static str = env!("CARGO_PKG_HOMEPAGE");
+ static DESCRIPTION: &'static str = env!("CARGO_PKG_DESCRIPTION");
+
+ fn main() {
+ let s = format!("{}-{}-{} @ {} in {}", VERSION_MAJOR,
+ VERSION_MINOR, VERSION_PATCH, VERSION_PRE,
+ CARGO_MANIFEST_DIR);
+ assert_eq!(s, foo::version());
+ println!("{}", s);
+ assert_eq!("foo", PKG_NAME);
+ assert_eq!("http://example.com", HOMEPAGE);
+ assert_eq!("This is foo", DESCRIPTION);
+ let s = format!("{}.{}.{}-{}", VERSION_MAJOR,
+ VERSION_MINOR, VERSION_PATCH, VERSION_PRE);
+ assert_eq!(s, VERSION);
+ }
+ "#)
+ .file("src/lib.rs", r#"
+ pub fn version() -> String {
+ format!("{}-{}-{} @ {} in {}",
+ env!("CARGO_PKG_VERSION_MAJOR"),
+ env!("CARGO_PKG_VERSION_MINOR"),
+ env!("CARGO_PKG_VERSION_PATCH"),
+ env!("CARGO_PKG_VERSION_PRE"),
+ env!("CARGO_MANIFEST_DIR"))
+ }
+ "#)
+ .build();
+
+ println!("build");
+ assert_that(p.cargo("build").arg("-v"), execs().with_status(0));
+
+ println!("bin");
+ assert_that(process(&p.bin("foo")),
+ execs().with_status(0).with_stdout(&format!("0-5-1 @ alpha.1 in {}\n",
+ p.root().display())));
+
+ println!("test");
+ assert_that(p.cargo("test").arg("-v"),
+ execs().with_status(0));
+ }
+
+ #[test]
+ fn crate_authors_env_vars() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.5.1-alpha.1"
+ authors = ["wycats@example.com", "neikos@example.com"]
+ "#)
+ .file("src/main.rs", r#"
+ extern crate foo;
+
+ static AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS");
+
+ fn main() {
+ let s = "wycats@example.com:neikos@example.com";
+ assert_eq!(AUTHORS, foo::authors());
+ println!("{}", AUTHORS);
+ assert_eq!(s, AUTHORS);
+ }
+ "#)
+ .file("src/lib.rs", r#"
+ pub fn authors() -> String {
+ format!("{}", env!("CARGO_PKG_AUTHORS"))
+ }
+ "#)
+ .build();
+
+ println!("build");
+ assert_that(p.cargo("build").arg("-v"), execs().with_status(0));
+
+ println!("bin");
+ assert_that(process(&p.bin("foo")),
+ execs().with_status(0).with_stdout("wycats@example.com:neikos@example.com"));
+
+ println!("test");
+ assert_that(p.cargo("test").arg("-v"),
+ execs().with_status(0));
+ }
+
+ // The tester may already have LD_LIBRARY_PATH=::/foo/bar which leads to a false positive error
+ fn setenv_for_removing_empty_component(mut p: ProcessBuilder) -> ProcessBuilder {
+ let v = dylib_path_envvar();
+ if let Ok(search_path) = env::var(v) {
+ let new_search_path =
+ env::join_paths(env::split_paths(&search_path).filter(|e| !e.as_os_str().is_empty()))
+ .expect("join_paths");
+ p.env(v, new_search_path); // build_command() will override LD_LIBRARY_PATH accordingly
+ }
+ p
+ }
+
+ // Regression test for #4277
+ #[test]
+ fn crate_library_path_env_var() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/main.rs", &format!(r##"
+ fn main() {{
+ let search_path = env!("{}");
+ let paths = std::env::split_paths(&search_path).collect::<Vec<_>>();
+ assert!(!paths.contains(&"".into()));
+ }}
+ "##, dylib_path_envvar()))
+ .build();
+
+ assert_that(setenv_for_removing_empty_component(p.cargo("run")),
+ execs().with_status(0));
+ }
+
+ // Regression test for #4277
+ #[test]
+ fn build_with_fake_libc_not_loading() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/main.rs", r#"
+ fn main() {}
+ "#)
+ .file("src/lib.rs", r#" "#)
+ .file("libc.so.6", r#""#)
+ .build();
+
+ assert_that(setenv_for_removing_empty_component(p.cargo("build")),
+ execs().with_status(0));
+ }
+
+ // this is testing that src/<pkg-name>.rs still works (for now)
+ #[test]
+ fn many_crate_types_old_style_lib_location() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [lib]
+
+ name = "foo"
+ crate_type = ["rlib", "dylib"]
+ "#)
+ .file("src/foo.rs", r#"
+ pub fn foo() {}
+ "#)
+ .build();
+ assert_that(p.cargo("build"), execs().with_status(0).with_stderr_contains("\
+ [WARNING] path `[..]src[/]foo.rs` was erroneously implicitly accepted for library `foo`,
+ please rename the file to `src/lib.rs` or set lib.path in Cargo.toml"));
+
+ assert_that(&p.root().join("target/debug/libfoo.rlib"), existing_file());
+ let fname = format!("{}foo{}", env::consts::DLL_PREFIX,
+ env::consts::DLL_SUFFIX);
+ assert_that(&p.root().join("target/debug").join(&fname), existing_file());
+ }
+
+ #[test]
+ fn many_crate_types_correct() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [lib]
+
+ name = "foo"
+ crate_type = ["rlib", "dylib"]
+ "#)
+ .file("src/lib.rs", r#"
+ pub fn foo() {}
+ "#)
+ .build();
+ assert_that(p.cargo("build"),
+ execs().with_status(0));
+
+ assert_that(&p.root().join("target/debug/libfoo.rlib"), existing_file());
+ let fname = format!("{}foo{}", env::consts::DLL_PREFIX,
+ env::consts::DLL_SUFFIX);
+ assert_that(&p.root().join("target/debug").join(&fname), existing_file());
+ }
+
+ #[test]
+ fn self_dependency() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+
+ name = "test"
+ version = "0.0.0"
+ authors = []
+
+ [dependencies.test]
+
+ path = "."
+
+ [lib]
+ name = "test"
+ path = "src/test.rs"
+ "#)
+ .file("src/test.rs", "fn main() {}")
+ .build();
+ assert_that(p.cargo("build"),
+ execs().with_status(101)
+ .with_stderr("\
+ [ERROR] cyclic package dependency: package `test v0.0.0 ([..])` depends on itself
+ "));
+ }
+
+ #[test]
+ fn ignore_broken_symlinks() {
+ // windows and symlinks don't currently agree that well
+ if cfg!(windows) { return }
+
+ let p = project("foo")
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .symlink("Notafile", "bar")
+ .build();
+
+ assert_that(p.cargo("build"), execs().with_status(0));
+ assert_that(&p.bin("foo"), existing_file());
+
+ assert_that(process(&p.bin("foo")),
+ execs().with_status(0).with_stdout("i am foo\n"));
+ }
+
+ #[test]
+ fn missing_lib_and_bin() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+
+ name = "test"
+ version = "0.0.0"
+ authors = []
+ "#)
+ .build();
+ assert_that(p.cargo("build"),
+ execs().with_status(101)
+ .with_stderr("\
+ [ERROR] failed to parse manifest at `[..]Cargo.toml`
+
+ Caused by:
+ no targets specified in the manifest
+ either src/lib.rs, src/main.rs, a [lib] section, or [[bin]] section must be present\n"));
+ }
+
+ #[test]
+ fn lto_build() {
+ // FIXME: currently this hits a linker bug on 32-bit MSVC
+ if cfg!(all(target_env = "msvc", target_pointer_width = "32")) {
+ return
+ }
+
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+
+ name = "test"
+ version = "0.0.0"
+ authors = []
+
+ [profile.release]
+ lto = true
+ "#)
+ .file("src/main.rs", "fn main() {}")
+ .build();
+ assert_that(p.cargo("build").arg("-v").arg("--release"),
+ execs().with_status(0).with_stderr(&format!("\
+ [COMPILING] test v0.0.0 ({url})
+ [RUNNING] `rustc --crate-name test src[/]main.rs --crate-type bin \
+ --emit=dep-info,link \
+ -C opt-level=3 \
+ -C lto \
+ -C metadata=[..] \
+ --out-dir {dir}[/]target[/]release[/]deps \
+ -L dependency={dir}[/]target[/]release[/]deps`
+ [FINISHED] release [optimized] target(s) in [..]
+ ",
+ dir = p.root().display(),
+ url = p.url(),
+ )));
+ }
+
+ #[test]
+ fn verbose_build() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+
+ name = "test"
+ version = "0.0.0"
+ authors = []
+ "#)
+ .file("src/lib.rs", "")
+ .build();
+ assert_that(p.cargo("build").arg("-v"),
+ execs().with_status(0).with_stderr(&format!("\
+ [COMPILING] test v0.0.0 ({url})
+ [RUNNING] `rustc --crate-name test src[/]lib.rs --crate-type lib \
+ --emit=dep-info,link -C debuginfo=2 \
+ -C metadata=[..] \
+ --out-dir [..] \
+ -L dependency={dir}[/]target[/]debug[/]deps`
+ [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+ ",
+ dir = p.root().display(),
+ url = p.url(),
+ )));
+ }
+
+ #[test]
+ fn verbose_release_build() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+
+ name = "test"
+ version = "0.0.0"
+ authors = []
+ "#)
+ .file("src/lib.rs", "")
+ .build();
+ assert_that(p.cargo("build").arg("-v").arg("--release"),
+ execs().with_status(0).with_stderr(&format!("\
+ [COMPILING] test v0.0.0 ({url})
+ [RUNNING] `rustc --crate-name test src[/]lib.rs --crate-type lib \
+ --emit=dep-info,link \
+ -C opt-level=3 \
+ -C metadata=[..] \
+ --out-dir [..] \
+ -L dependency={dir}[/]target[/]release[/]deps`
+ [FINISHED] release [optimized] target(s) in [..]
+ ",
+ dir = p.root().display(),
+ url = p.url(),
+ )));
+ }
+
+ #[test]
+ fn verbose_release_build_deps() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+
+ name = "test"
+ version = "0.0.0"
+ authors = []
+
+ [dependencies.foo]
+ path = "foo"
+ "#)
+ .file("src/lib.rs", "")
+ .file("foo/Cargo.toml", r#"
+ [package]
+
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+
+ [lib]
+ name = "foo"
+ crate_type = ["dylib", "rlib"]
+ "#)
+ .file("foo/src/lib.rs", "")
+ .build();
+ assert_that(p.cargo("build").arg("-v").arg("--release"),
+ execs().with_status(0).with_stderr(&format!("\
+ [COMPILING] foo v0.0.0 ({url}/foo)
+ [RUNNING] `rustc --crate-name foo foo[/]src[/]lib.rs \
+ --crate-type dylib --crate-type rlib \
+ --emit=dep-info,link \
+ -C prefer-dynamic \
+ -C opt-level=3 \
+ -C metadata=[..] \
+ --out-dir [..] \
+ -L dependency={dir}[/]target[/]release[/]deps`
+ [COMPILING] test v0.0.0 ({url})
+ [RUNNING] `rustc --crate-name test src[/]lib.rs --crate-type lib \
+ --emit=dep-info,link \
+ -C opt-level=3 \
+ -C metadata=[..] \
+ --out-dir [..] \
+ -L dependency={dir}[/]target[/]release[/]deps \
+ --extern foo={dir}[/]target[/]release[/]deps[/]{prefix}foo{suffix} \
+ --extern foo={dir}[/]target[/]release[/]deps[/]libfoo.rlib`
+ [FINISHED] release [optimized] target(s) in [..]
+ ",
+ dir = p.root().display(),
+ url = p.url(),
+ prefix = env::consts::DLL_PREFIX,
+ suffix = env::consts::DLL_SUFFIX)));
+ }
+
+ #[test]
+ fn explicit_examples() {
+ let p = project("world")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "world"
+ version = "1.0.0"
+ authors = []
+
+ [lib]
+ name = "world"
+ path = "src/lib.rs"
+
+ [[example]]
+ name = "hello"
+ path = "examples/ex-hello.rs"
+
+ [[example]]
+ name = "goodbye"
+ path = "examples/ex-goodbye.rs"
+ "#)
+ .file("src/lib.rs", r#"
+ pub fn get_hello() -> &'static str { "Hello" }
+ pub fn get_goodbye() -> &'static str { "Goodbye" }
+ pub fn get_world() -> &'static str { "World" }
+ "#)
+ .file("examples/ex-hello.rs", r#"
+ extern crate world;
+ fn main() { println!("{}, {}!", world::get_hello(), world::get_world()); }
+ "#)
+ .file("examples/ex-goodbye.rs", r#"
+ extern crate world;
+ fn main() { println!("{}, {}!", world::get_goodbye(), world::get_world()); }
+ "#)
+ .build();
+
+ assert_that(p.cargo("test").arg("-v"), execs().with_status(0));
+ assert_that(process(&p.bin("examples/hello")),
+ execs().with_status(0).with_stdout("Hello, World!\n"));
+ assert_that(process(&p.bin("examples/goodbye")),
+ execs().with_status(0).with_stdout("Goodbye, World!\n"));
+ }
+
+ #[test]
+ fn non_existing_example() {
+ let p = project("world")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "world"
+ version = "1.0.0"
+ authors = []
+
+ [lib]
+ name = "world"
+ path = "src/lib.rs"
+
+ [[example]]
+ name = "hello"
+ "#)
+ .file("src/lib.rs", "")
+ .file("examples/ehlo.rs", "")
+ .build();
+
+ assert_that(p.cargo("test").arg("-v"), execs().with_status(101).with_stderr("\
+ [ERROR] failed to parse manifest at `[..]`
+
+ Caused by:
+ can't find `hello` example, specify example.path"));
+ }
+
+ #[test]
+ fn non_existing_binary() {
+ let p = project("world")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "world"
+ version = "1.0.0"
+ authors = []
+
+ [[bin]]
+ name = "hello"
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/bin/ehlo.rs", "")
+ .build();
+
+ assert_that(p.cargo("build").arg("-v"), execs().with_status(101).with_stderr("\
+ [ERROR] failed to parse manifest at `[..]`
+
+ Caused by:
+ can't find `hello` bin, specify bin.path"));
+ }
+
+ #[test]
+ fn legacy_binary_paths_warinigs() {
+ let p = project("world")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "1.0.0"
+ authors = []
+
+ [[bin]]
+ name = "bar"
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build").arg("-v"), execs().with_status(0).with_stderr_contains("\
+ [WARNING] path `[..]src[/]main.rs` was erroneously implicitly accepted for binary `bar`,
+ please set bin.path in Cargo.toml"));
+
+ let p = project("world")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "1.0.0"
+ authors = []
+
+ [[bin]]
+ name = "bar"
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/bin/main.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build").arg("-v"), execs().with_status(0).with_stderr_contains("\
+ [WARNING] path `[..]src[/]bin[/]main.rs` was erroneously implicitly accepted for binary `bar`,
+ please set bin.path in Cargo.toml"));
+
+ let p = project("world")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "1.0.0"
+ authors = []
+
+ [[bin]]
+ name = "bar"
+ "#)
+ .file("src/bar.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build").arg("-v"), execs().with_status(0).with_stderr_contains("\
+ [WARNING] path `[..]src[/]bar.rs` was erroneously implicitly accepted for binary `bar`,
+ please set bin.path in Cargo.toml"));
+ }
+
+ #[test]
+ fn implicit_examples() {
+ let p = project("world")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "world"
+ version = "1.0.0"
+ authors = []
+ "#)
+ .file("src/lib.rs", r#"
+ pub fn get_hello() -> &'static str { "Hello" }
+ pub fn get_goodbye() -> &'static str { "Goodbye" }
+ pub fn get_world() -> &'static str { "World" }
+ "#)
+ .file("examples/hello.rs", r#"
+ extern crate world;
+ fn main() {
+ println!("{}, {}!", world::get_hello(), world::get_world());
+ }
+ "#)
+ .file("examples/goodbye.rs", r#"
+ extern crate world;
+ fn main() {
+ println!("{}, {}!", world::get_goodbye(), world::get_world());
+ }
+ "#)
+ .build();
+
+ assert_that(p.cargo("test"), execs().with_status(0));
+ assert_that(process(&p.bin("examples/hello")),
+ execs().with_status(0).with_stdout("Hello, World!\n"));
+ assert_that(process(&p.bin("examples/goodbye")),
+ execs().with_status(0).with_stdout("Goodbye, World!\n"));
+ }
+
+ #[test]
+ fn standard_build_no_ndebug() {
+ let p = project("world")
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", r#"
+ fn main() {
+ if cfg!(debug_assertions) {
+ println!("slow")
+ } else {
+ println!("fast")
+ }
+ }
+ "#)
+ .build();
+
+ assert_that(p.cargo("build"), execs().with_status(0));
+ assert_that(process(&p.bin("foo")),
+ execs().with_status(0).with_stdout("slow\n"));
+ }
+
+ #[test]
+ fn release_build_ndebug() {
+ let p = project("world")
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", r#"
+ fn main() {
+ if cfg!(debug_assertions) {
+ println!("slow")
+ } else {
+ println!("fast")
+ }
+ }
+ "#)
+ .build();
+
+ assert_that(p.cargo("build").arg("--release"),
+ execs().with_status(0));
+ assert_that(process(&p.release_bin("foo")),
+ execs().with_status(0).with_stdout("fast\n"));
+ }
+
+ #[test]
+ fn inferred_main_bin() {
+ let p = project("world")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/main.rs", r#"
+ fn main() {}
+ "#)
+ .build();
+
+ assert_that(p.cargo("build"), execs().with_status(0));
+ assert_that(process(&p.bin("foo")), execs().with_status(0));
+ }
+
+ #[test]
+ fn deletion_causes_failure() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.bar]
+ path = "bar"
+ "#)
+ .file("src/main.rs", r#"
+ extern crate bar;
+ fn main() {}
+ "#)
+ .file("bar/Cargo.toml", r#"
+ [package]
+ name = "bar"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("bar/src/lib.rs", "")
+ .build();
+
+ assert_that(p.cargo("build"), execs().with_status(0));
+ p.change_file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#);
+ assert_that(p.cargo("build"), execs().with_status(101));
+ }
+
+ #[test]
+ fn bad_cargo_toml_in_target_dir() {
+ let p = project("world")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/main.rs", r#"
+ fn main() {}
+ "#)
+ .file("target/Cargo.toml", "bad-toml")
+ .build();
+
+ assert_that(p.cargo("build"), execs().with_status(0));
+ assert_that(process(&p.bin("foo")), execs().with_status(0));
+ }
+
+ #[test]
+ fn lib_with_standard_name() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "syntax"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/lib.rs", "
+ pub fn foo() {}
+ ")
+ .file("src/main.rs", "
+ extern crate syntax;
+ fn main() { syntax::foo() }
+ ")
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs().with_status(0)
+ .with_stderr(&format!("\
+ [COMPILING] syntax v0.0.1 ({dir})
+ [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+ ",
+ dir = p.url())));
+ }
+
+ #[test]
+ fn simple_staticlib() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.1"
+
+ [lib]
+ name = "foo"
+ crate-type = ["staticlib"]
+ "#)
+ .file("src/lib.rs", "pub fn foo() {}")
+ .build();
+
+ // env var is a test for #1381
+ assert_that(p.cargo("build").env("RUST_LOG", "nekoneko=trace"),
+ execs().with_status(0));
+ }
+
+ #[test]
+ fn staticlib_rlib_and_bin() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.1"
+
+ [lib]
+ name = "foo"
+ crate-type = ["staticlib", "rlib"]
+ "#)
+ .file("src/lib.rs", "pub fn foo() {}")
+ .file("src/main.rs", r#"
+ extern crate foo;
+
+ fn main() {
+ foo::foo();
+ }"#)
+ .build();
+
+ assert_that(p.cargo("build").arg("-v"), execs().with_status(0));
+ }
+
+ #[test]
+ fn opt_out_of_bin() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ bin = []
+
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.1"
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/main.rs", "bad syntax")
+ .build();
+ assert_that(p.cargo("build"), execs().with_status(0));
+ }
+
+ #[test]
+ fn single_lib() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.1"
+
+ [lib]
+ name = "foo"
+ path = "src/bar.rs"
+ "#)
+ .file("src/bar.rs", "")
+ .build();
+ assert_that(p.cargo("build"), execs().with_status(0));
+ }
+
+ #[test]
+ fn freshness_ignores_excluded() {
+ let foo = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ build = "build.rs"
+ exclude = ["src/b*.rs"]
+ "#)
+ .file("build.rs", "fn main() {}")
+ .file("src/lib.rs", "pub fn bar() -> i32 { 1 }")
+ .build();
+ foo.root().move_into_the_past();
+
+ assert_that(foo.cargo("build"),
+ execs().with_status(0)
+ .with_stderr(&format!("\
+ [COMPILING] foo v0.0.0 ({url})
+ [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+ ", url = foo.url())));
+
+ // Smoke test to make sure it doesn't compile again
+ println!("first pass");
+ assert_that(foo.cargo("build"),
+ execs().with_status(0)
+ .with_stdout(""));
+
+ // Modify an ignored file and make sure we don't rebuild
+ println!("second pass");
+ File::create(&foo.root().join("src/bar.rs")).unwrap();
+ assert_that(foo.cargo("build"),
+ execs().with_status(0)
+ .with_stdout(""));
+ }
+
+ #[test]
+ fn rebuild_preserves_out_dir() {
+ let foo = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ build = 'build.rs'
+ "#)
+ .file("build.rs", r#"
+ use std::env;
+ use std::fs::File;
+ use std::path::Path;
+
+ fn main() {
+ let path = Path::new(&env::var("OUT_DIR").unwrap()).join("foo");
+ if env::var_os("FIRST").is_some() {
+ File::create(&path).unwrap();
+ } else {
+ File::create(&path).unwrap();
+ }
+ }
+ "#)
+ .file("src/lib.rs", "pub fn bar() -> i32 { 1 }")
+ .build();
+ foo.root().move_into_the_past();
+
+ assert_that(foo.cargo("build").env("FIRST", "1"),
+ execs().with_status(0)
+ .with_stderr(&format!("\
+ [COMPILING] foo v0.0.0 ({url})
+ [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+ ", url = foo.url())));
+
+ File::create(&foo.root().join("src/bar.rs")).unwrap();
+ assert_that(foo.cargo("build"),
+ execs().with_status(0)
+ .with_stderr(&format!("\
+ [COMPILING] foo v0.0.0 ({url})
+ [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+ ", url = foo.url())));
+ }
+
+ #[test]
+ fn dep_no_libs() {
+ let foo = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+
+ [dependencies.bar]
+ path = "bar"
+ "#)
+ .file("src/lib.rs", "pub fn bar() -> i32 { 1 }")
+ .file("bar/Cargo.toml", r#"
+ [package]
+ name = "bar"
+ version = "0.0.0"
+ authors = []
+ "#)
+ .file("bar/src/main.rs", "")
+ .build();
+ assert_that(foo.cargo("build"),
+ execs().with_status(0));
+ }
+
+ #[test]
+ fn recompile_space_in_name() {
+ let foo = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+
+ [lib]
+ name = "foo"
+ path = "src/my lib.rs"
+ "#)
+ .file("src/my lib.rs", "")
+ .build();
+ assert_that(foo.cargo("build"), execs().with_status(0));
+ foo.root().move_into_the_past();
+ assert_that(foo.cargo("build"),
+ execs().with_status(0).with_stdout(""));
+ }
+
+ #[cfg(unix)]
+ #[test]
+ fn ignore_bad_directories() {
+ use std::os::unix::prelude::*;
+ let foo = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ "#)
+ .file("src/lib.rs", "")
+ .build();
+ let dir = foo.root().join("tmp");
+ fs::create_dir(&dir).unwrap();
+ let stat = fs::metadata(&dir).unwrap();
+ let mut perms = stat.permissions();
+ perms.set_mode(0o644);
+ fs::set_permissions(&dir, perms.clone()).unwrap();
+ assert_that(foo.cargo("build"),
+ execs().with_status(0));
+ perms.set_mode(0o755);
+ fs::set_permissions(&dir, perms).unwrap();
+ }
+
+ #[test]
+ fn bad_cargo_config() {
+ let foo = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ "#)
+ .file("src/lib.rs", "")
+ .file(".cargo/config", r#"
+ this is not valid toml
+ "#)
+ .build();
+ assert_that(foo.cargo("build").arg("-v"),
+ execs().with_status(101).with_stderr("\
+ [ERROR] Couldn't load Cargo configuration
+
+ Caused by:
+ could not parse TOML configuration in `[..]`
+
+ Caused by:
+ could not parse input as TOML
+
+ Caused by:
+ expected an equals, found an identifier at line 2
+ "));
+ }
+
+ #[test]
+ fn cargo_platform_specific_dependency() {
+ let host = rustc_host();
+ let p = project("foo")
+ .file("Cargo.toml", &format!(r#"
+ [project]
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+ build = "build.rs"
+
+ [target.{host}.dependencies]
+ dep = {{ path = "dep" }}
+ [target.{host}.build-dependencies]
+ build = {{ path = "build" }}
+ [target.{host}.dev-dependencies]
+ dev = {{ path = "dev" }}
+ "#, host = host))
+ .file("src/main.rs", r#"
+ extern crate dep;
+ fn main() { dep::dep() }
+ "#)
+ .file("tests/foo.rs", r#"
+ extern crate dev;
+ #[test]
+ fn foo() { dev::dev() }
+ "#)
+ .file("build.rs", r#"
+ extern crate build;
+ fn main() { build::build(); }
+ "#)
+ .file("dep/Cargo.toml", r#"
+ [project]
+ name = "dep"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+ "#)
+ .file("dep/src/lib.rs", "pub fn dep() {}")
+ .file("build/Cargo.toml", r#"
+ [project]
+ name = "build"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+ "#)
+ .file("build/src/lib.rs", "pub fn build() {}")
+ .file("dev/Cargo.toml", r#"
+ [project]
+ name = "dev"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+ "#)
+ .file("dev/src/lib.rs", "pub fn dev() {}")
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs().with_status(0));
+
+ assert_that(&p.bin("foo"), existing_file());
+ assert_that(p.cargo("test"),
+ execs().with_status(0));
+ }
+
+ #[test]
+ fn bad_platform_specific_dependency() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [target.wrong-target.dependencies.bar]
+ path = "bar"
+ "#)
+ .file("src/main.rs",
+ &main_file(r#""{}", bar::gimme()"#, &["bar"]))
+ .file("bar/Cargo.toml", r#"
+ [project]
+
+ name = "bar"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+ "#)
+ .file("bar/src/lib.rs", r#"
+ extern crate baz;
+
+ pub fn gimme() -> String {
+ format!("")
+ }
+ "#)
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs().with_status(101));
+ }
+
+ #[test]
+ fn cargo_platform_specific_dependency_wrong_platform() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [target.non-existing-triplet.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"]
+ "#)
+ .file("bar/src/lib.rs", r#"
+ invalid rust file, should not be compiled
+ "#)
+ .build();
+
+ p.cargo("build").exec_with_output().unwrap();
+
+ assert_that(&p.bin("foo"), existing_file());
+ assert_that(process(&p.bin("foo")),
+ execs().with_status(0));
+
+ let loc = p.root().join("Cargo.lock");
+ let mut lockfile = String::new();
+ File::open(&loc).unwrap().read_to_string(&mut lockfile).unwrap();
+ assert!(lockfile.contains("bar"))
+ }
+
+ #[test]
+ fn example_as_lib() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [[example]]
+ name = "ex"
+ crate-type = ["lib"]
+ "#)
+ .file("src/lib.rs", "")
+ .file("examples/ex.rs", "")
+ .build();
+
+ assert_that(p.cargo("build").arg("--example=ex"), execs().with_status(0));
+ assert_that(&p.example_lib("ex", "lib"), existing_file());
+ }
+
+ #[test]
+ fn example_as_rlib() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [[example]]
+ name = "ex"
+ crate-type = ["rlib"]
+ "#)
+ .file("src/lib.rs", "")
+ .file("examples/ex.rs", "")
+ .build();
+
+ assert_that(p.cargo("build").arg("--example=ex"), execs().with_status(0));
+ assert_that(&p.example_lib("ex", "rlib"), existing_file());
+ }
+
+ #[test]
+ fn example_as_dylib() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [[example]]
+ name = "ex"
+ crate-type = ["dylib"]
+ "#)
+ .file("src/lib.rs", "")
+ .file("examples/ex.rs", "")
+ .build();
+
+ assert_that(p.cargo("build").arg("--example=ex"), execs().with_status(0));
+ assert_that(&p.example_lib("ex", "dylib"), existing_file());
+ }
+
+ #[test]
+ fn example_as_proc_macro() {
+ if !is_nightly() {
+ return;
+ }
+
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [[example]]
+ name = "ex"
+ crate-type = ["proc-macro"]
+ "#)
+ .file("src/lib.rs", "")
+ .file("examples/ex.rs", "#![feature(proc_macro)]")
+ .build();
+
+ assert_that(p.cargo("build").arg("--example=ex"), execs().with_status(0));
+ assert_that(&p.example_lib("ex", "proc-macro"), existing_file());
+ }
+
+ #[test]
+ fn example_bin_same_name() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/main.rs", "fn main() {}")
+ .file("examples/foo.rs", "fn main() {}")
+ .build();
+
+ p.cargo("test").arg("--no-run").arg("-v")
+ .exec_with_output()
+ .unwrap();
+
+ assert_that(&p.bin("foo"), is_not(existing_file()));
+ // We expect a file of the form bin/foo-{metadata_hash}
+ assert_that(&p.bin("examples/foo"), existing_file());
+
+ p.cargo("test").arg("--no-run").arg("-v")
+ .exec_with_output()
+ .unwrap();
+
+ assert_that(&p.bin("foo"), is_not(existing_file()));
+ // We expect a file of the form bin/foo-{metadata_hash}
+ assert_that(&p.bin("examples/foo"), existing_file());
+ }
+
+ #[test]
+ fn compile_then_delete() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("run").arg("-v"), execs().with_status(0));
+ assert_that(&p.bin("foo"), existing_file());
+ if cfg!(windows) {
+ // On windows unlinking immediately after running often fails, so sleep
+ sleep_ms(100);
+ }
+ fs::remove_file(&p.bin("foo")).unwrap();
+ assert_that(p.cargo("run").arg("-v"),
+ execs().with_status(0));
+ }
+
+ #[test]
+ fn transitive_dependencies_not_available() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.aaaaa]
+ path = "a"
+ "#)
+ .file("src/main.rs", "extern crate bbbbb; extern crate aaaaa; fn main() {}")
+ .file("a/Cargo.toml", r#"
+ [package]
+ name = "aaaaa"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.bbbbb]
+ path = "../b"
+ "#)
+ .file("a/src/lib.rs", "extern crate bbbbb;")
+ .file("b/Cargo.toml", r#"
+ [package]
+ name = "bbbbb"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("b/src/lib.rs", "")
+ .build();
+
+ assert_that(p.cargo("build").arg("-v"),
+ execs().with_status(101)
+ .with_stderr_contains("\
+ [..] can't find crate for `bbbbb`[..]
+ "));
+ }
+
+ #[test]
+ fn cyclic_deps_rejected() {
+ let p = project("foo")
+ .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 = []
+
+ [dependencies.foo]
+ path = ".."
+ "#)
+ .file("a/src/lib.rs", "")
+ .build();
+
+ assert_that(p.cargo("build").arg("-v"),
+ execs().with_status(101)
+ .with_stderr("\
+ [ERROR] cyclic package dependency: package `a v0.0.1 ([..])` depends on itself
+ "));
+ }
+
+ #[test]
+ fn predictable_filenames() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [lib]
+ name = "foo"
+ crate-type = ["dylib", "rlib"]
+ "#)
+ .file("src/lib.rs", "")
+ .build();
+
+ assert_that(p.cargo("build").arg("-v"),
+ execs().with_status(0));
+ assert_that(&p.root().join("target/debug/libfoo.rlib"), existing_file());
+ let dylib_name = format!("{}foo{}", env::consts::DLL_PREFIX,
+ env::consts::DLL_SUFFIX);
+ assert_that(&p.root().join("target/debug").join(dylib_name),
+ existing_file());
+ }
+
+ #[test]
+ fn dashes_to_underscores() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo-bar"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/main.rs", "extern crate foo_bar; fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build").arg("-v"),
+ execs().with_status(0));
+ assert_that(&p.bin("foo-bar"), existing_file());
+ }
+
+ #[test]
+ fn dashes_in_crate_name_bad() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [lib]
+ name = "foo-bar"
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/main.rs", "extern crate foo_bar; fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build").arg("-v"),
+ execs().with_status(101));
+ }
+
+ #[test]
+ fn rustc_env_var() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/lib.rs", "")
+ .build();
+
+ assert_that(p.cargo("build")
+ .env("RUSTC", "rustc-that-does-not-exist").arg("-v"),
+ execs().with_status(101)
+ .with_stderr("\
+ [ERROR] could not execute process `rustc-that-does-not-exist -vV` ([..])
+
+ Caused by:
+ [..]
+ "));
+ assert_that(&p.bin("a"), is_not(existing_file()));
+ }
+
+ #[test]
+ fn filtering() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/bin/a.rs", "fn main() {}")
+ .file("src/bin/b.rs", "fn main() {}")
+ .file("examples/a.rs", "fn main() {}")
+ .file("examples/b.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build").arg("--lib"),
+ execs().with_status(0));
+ assert_that(&p.bin("a"), is_not(existing_file()));
+
+ assert_that(p.cargo("build").arg("--bin=a").arg("--example=a"),
+ execs().with_status(0));
+ assert_that(&p.bin("a"), existing_file());
+ assert_that(&p.bin("b"), is_not(existing_file()));
+ assert_that(&p.bin("examples/a"), existing_file());
+ assert_that(&p.bin("examples/b"), is_not(existing_file()));
+ }
+
+ #[test]
+ fn filtering_implicit_bins() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/bin/a.rs", "fn main() {}")
+ .file("src/bin/b.rs", "fn main() {}")
+ .file("examples/a.rs", "fn main() {}")
+ .file("examples/b.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build").arg("--bins"),
+ execs().with_status(0));
+ assert_that(&p.bin("a"), existing_file());
+ assert_that(&p.bin("b"), existing_file());
+ assert_that(&p.bin("examples/a"), is_not(existing_file()));
+ assert_that(&p.bin("examples/b"), is_not(existing_file()));
+ }
+
+ #[test]
+ fn filtering_implicit_examples() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/bin/a.rs", "fn main() {}")
+ .file("src/bin/b.rs", "fn main() {}")
+ .file("examples/a.rs", "fn main() {}")
+ .file("examples/b.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build").arg("--examples"),
+ execs().with_status(0));
+ assert_that(&p.bin("a"), is_not(existing_file()));
+ assert_that(&p.bin("b"), is_not(existing_file()));
+ assert_that(&p.bin("examples/a"), existing_file());
+ assert_that(&p.bin("examples/b"), existing_file());
+ }
+
+ #[test]
+ fn ignore_dotfile() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/bin/.a.rs", "")
+ .file("src/bin/a.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs().with_status(0));
+ }
+
+ #[test]
+ fn ignore_dotdirs() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/bin/a.rs", "fn main() {}")
+ .file(".git/Cargo.toml", "")
+ .file(".pc/dummy-fix.patch/Cargo.toml", "")
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs().with_status(0));
+ }
+
+ #[test]
+ fn dotdir_root() {
+ let p = ProjectBuilder::new("foo", root().join(".foo"))
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/bin/a.rs", "fn main() {}")
+ .build();
+ assert_that(p.cargo("build"),
+ execs().with_status(0));
+ }
+
+
+ #[test]
+ fn custom_target_dir() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ let exe_name = format!("foo{}", env::consts::EXE_SUFFIX);
+
+ assert_that(p.cargo("build").env("CARGO_TARGET_DIR", "foo/target"),
+ execs().with_status(0));
+ assert_that(&p.root().join("foo/target/debug").join(&exe_name),
+ existing_file());
+ assert_that(&p.root().join("target/debug").join(&exe_name),
+ is_not(existing_file()));
+
+ assert_that(p.cargo("build"),
+ execs().with_status(0));
+ assert_that(&p.root().join("foo/target/debug").join(&exe_name),
+ existing_file());
+ assert_that(&p.root().join("target/debug").join(&exe_name),
+ existing_file());
+
+ fs::create_dir(p.root().join(".cargo")).unwrap();
+ File::create(p.root().join(".cargo/config")).unwrap().write_all(br#"
+ [build]
+ target-dir = "foo/target"
+ "#).unwrap();
+ assert_that(p.cargo("build").env("CARGO_TARGET_DIR", "bar/target"),
+ execs().with_status(0));
+ assert_that(&p.root().join("bar/target/debug").join(&exe_name),
+ existing_file());
+ assert_that(&p.root().join("foo/target/debug").join(&exe_name),
+ existing_file());
+ assert_that(&p.root().join("target/debug").join(&exe_name),
+ existing_file());
+ }
+
+ #[test]
+ fn rustc_no_trans() {
+ if !is_nightly() { return }
+
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("rustc").arg("-v").arg("--").arg("-Zno-trans"),
+ execs().with_status(0));
+ }
+
+ #[test]
+ fn build_multiple_packages() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.d1]
+ path = "d1"
+ [dependencies.d2]
+ path = "d2"
+
+ [[bin]]
+ name = "foo"
+ "#)
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .file("d1/Cargo.toml", r#"
+ [package]
+ name = "d1"
+ version = "0.0.1"
+ authors = []
+
+ [[bin]]
+ name = "d1"
+ "#)
+ .file("d1/src/lib.rs", "")
+ .file("d1/src/main.rs", "fn main() { println!(\"d1\"); }")
+ .file("d2/Cargo.toml", r#"
+ [package]
+ name = "d2"
+ version = "0.0.1"
+ authors = []
+
+ [[bin]]
+ name = "d2"
+ doctest = false
+ "#)
+ .file("d2/src/main.rs", "fn main() { println!(\"d2\"); }")
+ .build();
+
+ assert_that(p.cargo("build").arg("-p").arg("d1").arg("-p").arg("d2")
+ .arg("-p").arg("foo"),
+ execs().with_status(0));
+
+ assert_that(&p.bin("foo"), existing_file());
+ assert_that(process(&p.bin("foo")),
+ execs().with_status(0).with_stdout("i am foo\n"));
+
+ let d1_path = &p.build_dir().join("debug")
+ .join(format!("d1{}", env::consts::EXE_SUFFIX));
+ let d2_path = &p.build_dir().join("debug")
+ .join(format!("d2{}", env::consts::EXE_SUFFIX));
+
+ assert_that(d1_path, existing_file());
+ assert_that(process(d1_path), execs().with_status(0).with_stdout("d1"));
+
+ assert_that(d2_path, existing_file());
+ assert_that(process(d2_path),
+ execs().with_status(0).with_stdout("d2"));
+ }
+
+ #[test]
+ fn invalid_spec() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.d1]
+ path = "d1"
+
+ [[bin]]
+ name = "foo"
+ "#)
+ .file("src/bin/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .file("d1/Cargo.toml", r#"
+ [package]
+ name = "d1"
+ version = "0.0.1"
+ authors = []
+
+ [[bin]]
+ name = "d1"
+ "#)
+ .file("d1/src/lib.rs", "")
+ .file("d1/src/main.rs", "fn main() { println!(\"d1\"); }")
+ .build();
+
+ assert_that(p.cargo("build").arg("-p").arg("notAValidDep"),
+ execs().with_status(101).with_stderr("\
+ [ERROR] package id specification `notAValidDep` matched no packages
+ "));
+
+ assert_that(p.cargo("build").arg("-p").arg("d1").arg("-p").arg("notAValidDep"),
+ execs().with_status(101).with_stderr("\
+ [ERROR] package id specification `notAValidDep` matched no packages
+ "));
+ }
+
+ #[test]
+ fn manifest_with_bom_is_ok() {
+ let p = project("foo")
+ .file("Cargo.toml", "\u{FEFF}
+ [package]
+ name = \"foo\"
+ version = \"0.0.1\"
+ authors = []
+ ")
+ .file("src/lib.rs", "")
+ .build();
+ assert_that(p.cargo("build").arg("-v"),
+ execs().with_status(0));
+ }
+
+ #[test]
+ fn panic_abort_compiles_with_panic_abort() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [profile.dev]
+ panic = 'abort'
+ "#)
+ .file("src/lib.rs", "")
+ .build();
+ assert_that(p.cargo("build").arg("-v"),
+ execs().with_status(0)
+ .with_stderr_contains("[..] -C panic=abort [..]"));
+ }
+
+ #[test]
+ fn explicit_color_config_is_propagated_to_rustc() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+
+ name = "test"
+ version = "0.0.0"
+ authors = []
+ "#)
+ .file("src/lib.rs", "")
+ .build();
+ assert_that(p.cargo("build").arg("-v").arg("--color").arg("always"),
+ execs().with_status(0).with_stderr_contains(
+ "[..]rustc [..] src[/]lib.rs --color always[..]"));
+
+ assert_that(p.cargo("clean"), execs().with_status(0));
+
+ assert_that(p.cargo("build").arg("-v").arg("--color").arg("never"),
+ execs().with_status(0).with_stderr("\
+ [COMPILING] test v0.0.0 ([..])
+ [RUNNING] `rustc [..] --color never [..]`
+ [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+ "));
+ }
+
+ #[test]
+ fn compiler_json_error_format() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [profile.dev]
+ debug = false # prevent the *.dSYM from affecting the test result
+
+ [dependencies.bar]
+ path = "bar"
+ "#)
+ .file("src/main.rs", "fn main() { let unused = 92; }")
+ .file("bar/Cargo.toml", r#"
+ [project]
+
+ name = "bar"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+ "#)
+ .file("bar/src/lib.rs", r#"fn dead() {}"#)
+ .build();
+
+ assert_that(p.cargo("build").arg("-v")
+ .arg("--message-format").arg("json"),
+ execs().with_status(0).with_json(r#"
+ {
+ "reason":"compiler-message",
+ "package_id":"bar 0.5.0 ([..])",
+ "target":{
+ "kind":["lib"],
+ "crate_types":["lib"],
+ "name":"bar",
+ "src_path":"[..]lib.rs"
+ },
+ "message":"{...}"
+ }
+
+ {
+ "reason":"compiler-artifact",
+ "profile": {
+ "debug_assertions": true,
+ "debuginfo": null,
+ "opt_level": "0",
+ "overflow_checks": true,
+ "test": false
+ },
+ "features": [],
+ "package_id":"bar 0.5.0 ([..])",
+ "target":{
+ "kind":["lib"],
+ "crate_types":["lib"],
+ "name":"bar",
+ "src_path":"[..]lib.rs"
+ },
+ "filenames":["[..].rlib"],
+ "fresh": false
+ }
+
+ {
+ "reason":"compiler-message",
+ "package_id":"foo 0.5.0 ([..])",
+ "target":{
+ "kind":["bin"],
+ "crate_types":["bin"],
+ "name":"foo",
+ "src_path":"[..]main.rs"
+ },
+ "message":"{...}"
+ }
+
+ {
+ "reason":"compiler-artifact",
+ "package_id":"foo 0.5.0 ([..])",
+ "target":{
+ "kind":["bin"],
+ "crate_types":["bin"],
+ "name":"foo",
+ "src_path":"[..]main.rs"
+ },
+ "profile": {
+ "debug_assertions": true,
+ "debuginfo": null,
+ "opt_level": "0",
+ "overflow_checks": true,
+ "test": false
+ },
+ "features": [],
+ "filenames": ["[..]"],
+ "fresh": false
+ }
+ "#));
+
+ // With fresh build, we should repeat the artifacts,
+ // but omit compiler warnings.
+ assert_that(p.cargo("build").arg("-v")
+ .arg("--message-format").arg("json"),
+ execs().with_status(0).with_json(r#"
+ {
+ "reason":"compiler-artifact",
+ "profile": {
+ "debug_assertions": true,
+ "debuginfo": null,
+ "opt_level": "0",
+ "overflow_checks": true,
+ "test": false
+ },
+ "features": [],
+ "package_id":"bar 0.5.0 ([..])",
+ "target":{
+ "kind":["lib"],
+ "crate_types":["lib"],
+ "name":"bar",
+ "src_path":"[..]lib.rs"
+ },
+ "filenames":["[..].rlib"],
+ "fresh": true
+ }
+
+ {
+ "reason":"compiler-artifact",
+ "package_id":"foo 0.5.0 ([..])",
+ "target":{
+ "kind":["bin"],
+ "crate_types":["bin"],
+ "name":"foo",
+ "src_path":"[..]main.rs"
+ },
+ "profile": {
+ "debug_assertions": true,
+ "debuginfo": null,
+ "opt_level": "0",
+ "overflow_checks": true,
+ "test": false
+ },
+ "features": [],
+ "filenames": ["[..]"],
+ "fresh": true
+ }
+ "#));
+ }
+
+ #[test]
+ fn wrong_message_format_option() {
+ let p = project("foo")
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build").arg("--message-format").arg("XML"),
+ execs().with_status(1)
+ .with_stderr_contains(
+ r#"[ERROR] Could not match 'xml' with any of the allowed variants: ["Human", "Json"]"#));
+ }
+
+ #[test]
+ fn message_format_json_forward_stderr() {
+ let p = project("foo")
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/main.rs", "fn main() { let unused = 0; }")
+ .build();
+
+ assert_that(p.cargo("rustc").arg("--release").arg("--bin").arg("foo")
+ .arg("--message-format").arg("JSON"),
+ execs().with_status(0)
+ .with_json(r#"
+ {
+ "reason":"compiler-message",
+ "package_id":"foo 0.5.0 ([..])",
+ "target":{
+ "kind":["bin"],
+ "crate_types":["bin"],
+ "name":"foo",
+ "src_path":"[..]"
+ },
+ "message":"{...}"
+ }
+
+ {
+ "reason":"compiler-artifact",
+ "package_id":"foo 0.5.0 ([..])",
+ "target":{
+ "kind":["bin"],
+ "crate_types":["bin"],
+ "name":"foo",
+ "src_path":"[..]"
+ },
+ "profile":{
+ "debug_assertions":false,
+ "debuginfo":null,
+ "opt_level":"3",
+ "overflow_checks": false,
+ "test":false
+ },
+ "features":[],
+ "filenames":["[..]"],
+ "fresh": false
+ }
+ "#));
+ }
+
+ #[test]
+ fn no_warn_about_package_metadata() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [package.metadata]
+ foo = "bar"
+ a = true
+ b = 3
+
+ [package.metadata.another]
+ bar = 3
+ "#)
+ .file("src/lib.rs", "")
+ .build();
+ assert_that(p.cargo("build"),
+ execs().with_status(0)
+ .with_stderr("[..] foo v0.0.1 ([..])\n\
+ [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n"));
+ }
+
+ #[test]
+ fn cargo_build_empty_target() {
+ let p = project("foo")
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build").arg("--target").arg(""),
+ execs().with_status(101)
+ .with_stderr_contains("[..] target was empty"));
+ }
+
+ #[test]
+ fn build_all_workspace() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = { path = "bar" }
+
+ [workspace]
+ "#)
+ .file("src/main.rs", r#"
+ fn main() {}
+ "#)
+ .file("bar/Cargo.toml", r#"
+ [project]
+ name = "bar"
+ version = "0.1.0"
+ "#)
+ .file("bar/src/lib.rs", r#"
+ pub fn bar() {}
+ "#)
+ .build();
+
+ assert_that(p.cargo("build")
+ .arg("--all"),
+ execs().with_status(0)
+ .with_stderr("[..] Compiling bar v0.1.0 ([..])\n\
+ [..] Compiling foo v0.1.0 ([..])\n\
+ [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n"));
+ }
+
+ #[test]
+ fn build_all_exclude() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.1.0"
+
+ [workspace]
+ members = ["bar", "baz"]
+ "#)
+ .file("src/main.rs", r#"
+ fn main() {}
+ "#)
+ .file("bar/Cargo.toml", r#"
+ [project]
+ name = "bar"
+ version = "0.1.0"
+ "#)
+ .file("bar/src/lib.rs", r#"
+ pub fn bar() {}
+ "#)
+ .file("baz/Cargo.toml", r#"
+ [project]
+ name = "baz"
+ version = "0.1.0"
+ "#)
+ .file("baz/src/lib.rs", r#"
+ pub fn baz() {
+ break_the_build();
+ }
+ "#)
+ .build();
+
+ assert_that(p.cargo("build")
+ .arg("--all")
+ .arg("--exclude")
+ .arg("baz"),
+ execs().with_status(0)
+ .with_stderr_contains("[..]Compiling foo v0.1.0 [..]")
+ .with_stderr_contains("[..]Compiling bar v0.1.0 [..]")
+ .with_stderr_does_not_contain("[..]Compiling baz v0.1.0 [..]"));
+ }
+
+ #[test]
+ fn build_all_workspace_implicit_examples() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = { path = "bar" }
+
+ [workspace]
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/bin/a.rs", "fn main() {}")
+ .file("src/bin/b.rs", "fn main() {}")
+ .file("examples/c.rs", "fn main() {}")
+ .file("examples/d.rs", "fn main() {}")
+ .file("bar/Cargo.toml", r#"
+ [project]
+ name = "bar"
+ version = "0.1.0"
+ "#)
+ .file("bar/src/lib.rs", "")
+ .file("bar/src/bin/e.rs", "fn main() {}")
+ .file("bar/src/bin/f.rs", "fn main() {}")
+ .file("bar/examples/g.rs", "fn main() {}")
+ .file("bar/examples/h.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build")
+ .arg("--all").arg("--examples"),
+ execs().with_status(0)
+ .with_stderr("[..] Compiling bar v0.1.0 ([..])\n\
+ [..] Compiling foo v0.1.0 ([..])\n\
+ [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n"));
+ assert_that(&p.bin("a"), is_not(existing_file()));
+ assert_that(&p.bin("b"), is_not(existing_file()));
+ assert_that(&p.bin("examples/c"), existing_file());
+ assert_that(&p.bin("examples/d"), existing_file());
+ assert_that(&p.bin("e"), is_not(existing_file()));
+ assert_that(&p.bin("f"), is_not(existing_file()));
+ assert_that(&p.bin("examples/g"), existing_file());
+ assert_that(&p.bin("examples/h"), existing_file());
+ }
+
+ #[test]
+ fn build_all_virtual_manifest() {
+ let p = project("workspace")
+ .file("Cargo.toml", r#"
+ [workspace]
+ members = ["foo", "bar"]
+ "#)
+ .file("foo/Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.1.0"
+ "#)
+ .file("foo/src/lib.rs", r#"
+ pub fn foo() {}
+ "#)
+ .file("bar/Cargo.toml", r#"
+ [project]
+ name = "bar"
+ version = "0.1.0"
+ "#)
+ .file("bar/src/lib.rs", r#"
+ pub fn bar() {}
+ "#)
+ .build();
+
+ // The order in which foo and bar are built is not guaranteed
+ assert_that(p.cargo("build")
+ .arg("--all"),
+ execs().with_status(0)
+ .with_stderr_contains("[..] Compiling bar v0.1.0 ([..])")
+ .with_stderr_contains("[..] Compiling foo v0.1.0 ([..])")
+ .with_stderr("[..] Compiling [..] v0.1.0 ([..])\n\
+ [..] Compiling [..] v0.1.0 ([..])\n\
+ [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n"));
+ }
+
+ #[test]
+ fn build_virtual_manifest_all_implied() {
+ let p = project("workspace")
+ .file("Cargo.toml", r#"
+ [workspace]
+ members = ["foo", "bar"]
+ "#)
+ .file("foo/Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.1.0"
+ "#)
+ .file("foo/src/lib.rs", r#"
+ pub fn foo() {}
+ "#)
+ .file("bar/Cargo.toml", r#"
+ [project]
+ name = "bar"
+ version = "0.1.0"
+ "#)
+ .file("bar/src/lib.rs", r#"
+ pub fn bar() {}
+ "#)
+ .build();
+
+ // The order in which foo and bar are built is not guaranteed
+ assert_that(p.cargo("build"),
+ execs().with_status(0)
+ .with_stderr_contains("[..] Compiling bar v0.1.0 ([..])")
+ .with_stderr_contains("[..] Compiling foo v0.1.0 ([..])")
+ .with_stderr("[..] Compiling [..] v0.1.0 ([..])\n\
+ [..] Compiling [..] v0.1.0 ([..])\n\
+ [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n"));
+ }
+
+ #[test]
+ fn build_virtual_manifest_one_project() {
+ let p = project("workspace")
+ .file("Cargo.toml", r#"
+ [workspace]
+ members = ["foo", "bar"]
+ "#)
+ .file("foo/Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.1.0"
+ "#)
+ .file("foo/src/lib.rs", r#"
+ pub fn foo() {}
+ "#)
+ .file("bar/Cargo.toml", r#"
+ [project]
+ name = "bar"
+ version = "0.1.0"
+ "#)
+ .file("bar/src/lib.rs", r#"
+ pub fn bar() {}
+ "#)
+ .build();
+
+ assert_that(p.cargo("build")
+ .arg("-p").arg("foo"),
+ execs().with_status(0)
+ .with_stderr_does_not_contain("bar")
+ .with_stderr_contains("[..] Compiling foo v0.1.0 ([..])")
+ .with_stderr("[..] Compiling [..] v0.1.0 ([..])\n\
+ [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n"));
+ }
+
+ #[test]
+ fn build_all_virtual_manifest_implicit_examples() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [workspace]
+ members = ["foo", "bar"]
+ "#)
+ .file("foo/Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.1.0"
+ "#)
+ .file("foo/src/lib.rs", "")
+ .file("foo/src/bin/a.rs", "fn main() {}")
+ .file("foo/src/bin/b.rs", "fn main() {}")
+ .file("foo/examples/c.rs", "fn main() {}")
+ .file("foo/examples/d.rs", "fn main() {}")
+ .file("bar/Cargo.toml", r#"
+ [project]
+ name = "bar"
+ version = "0.1.0"
+ "#)
+ .file("bar/src/lib.rs", "")
+ .file("bar/src/bin/e.rs", "fn main() {}")
+ .file("bar/src/bin/f.rs", "fn main() {}")
+ .file("bar/examples/g.rs", "fn main() {}")
+ .file("bar/examples/h.rs", "fn main() {}")
+ .build();
+
+ // The order in which foo and bar are built is not guaranteed
+ assert_that(p.cargo("build")
+ .arg("--all").arg("--examples"),
+ execs().with_status(0)
+ .with_stderr_contains("[..] Compiling bar v0.1.0 ([..])")
+ .with_stderr_contains("[..] Compiling foo v0.1.0 ([..])")
+ .with_stderr("[..] Compiling [..] v0.1.0 ([..])\n\
+ [..] Compiling [..] v0.1.0 ([..])\n\
+ [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n"));
+ assert_that(&p.bin("a"), is_not(existing_file()));
+ assert_that(&p.bin("b"), is_not(existing_file()));
+ assert_that(&p.bin("examples/c"), existing_file());
+ assert_that(&p.bin("examples/d"), existing_file());
+ assert_that(&p.bin("e"), is_not(existing_file()));
+ assert_that(&p.bin("f"), is_not(existing_file()));
+ assert_that(&p.bin("examples/g"), existing_file());
+ assert_that(&p.bin("examples/h"), existing_file());
+ }
+
+ #[test]
+ fn build_all_member_dependency_same_name() {
+ let p = project("workspace")
+ .file("Cargo.toml", r#"
+ [workspace]
+ members = ["a"]
+ "#)
+ .file("a/Cargo.toml", r#"
+ [project]
+ name = "a"
+ version = "0.1.0"
+
+ [dependencies]
+ a = "0.1.0"
+ "#)
+ .file("a/src/lib.rs", r#"
+ pub fn a() {}
+ "#)
+ .build();
+
+ Package::new("a", "0.1.0").publish();
+
+ assert_that(p.cargo("build")
+ .arg("--all"),
+ execs().with_status(0)
+ .with_stderr("[..] Updating registry `[..]`\n\
+ [..] Downloading a v0.1.0 ([..])\n\
+ [..] Compiling a v0.1.0\n\
+ [..] Compiling a v0.1.0 ([..])\n\
+ [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n"));
+ }
+
+ #[test]
+ fn run_proper_binary() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.0"
+ [[bin]]
+ name = "main"
+ [[bin]]
+ name = "other"
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/bin/main.rs", r#"
+ fn main() {
+ panic!("This should never be run.");
+ }
+ "#)
+ .file("src/bin/other.rs", r#"
+ fn main() {
+ }
+ "#)
+ .build();
+
+ assert_that(p.cargo("run").arg("--bin").arg("other"),
+ execs().with_status(0));
+ }
+
+ #[test]
+ fn run_proper_binary_main_rs() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.0"
+ [[bin]]
+ name = "foo"
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/bin/main.rs", r#"
+ fn main() {
+ }
+ "#)
+ .build();
+
+ assert_that(p.cargo("run").arg("--bin").arg("foo"),
+ execs().with_status(0));
+ }
+
+ #[test]
+ fn run_proper_alias_binary_from_src() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.0"
+ [[bin]]
+ name = "foo"
+ [[bin]]
+ name = "bar"
+ "#)
+ .file("src/foo.rs", r#"
+ fn main() {
+ println!("foo");
+ }
+ "#).file("src/bar.rs", r#"
+ fn main() {
+ println!("bar");
+ }
+ "#)
+ .build();
+
+ assert_that(p.cargo("build")
+ .arg("--all"),
+ execs().with_status(0)
+ );
+ assert_that(process(&p.bin("foo")),
+ execs().with_status(0).with_stdout("foo\n"));
+ assert_that(process(&p.bin("bar")),
+ execs().with_status(0).with_stdout("bar\n"));
+ }
+
+ #[test]
+ fn run_proper_alias_binary_main_rs() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.0"
+ [[bin]]
+ name = "foo"
+ [[bin]]
+ name = "bar"
+ "#)
+ .file("src/main.rs", r#"
+ fn main() {
+ println!("main");
+ }
+ "#)
+ .build();
+
+ assert_that(p.cargo("build")
+ .arg("--all"),
+ execs().with_status(0)
+ );
+ assert_that(process(&p.bin("foo")),
+ execs().with_status(0).with_stdout("main\n"));
+ assert_that(process(&p.bin("bar")),
+ execs().with_status(0).with_stdout("main\n"));
+ }
+
+ #[test]
+ fn run_proper_binary_main_rs_as_foo() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.0"
+ [[bin]]
+ name = "foo"
+ "#)
+ .file("src/foo.rs", r#"
+ fn main() {
+ panic!("This should never be run.");
+ }
+ "#)
+ .file("src/main.rs", r#"
+ fn main() {
+ }
+ "#)
+ .build();
+
+ assert_that(p.cargo("run").arg("--bin").arg("foo"),
+ execs().with_status(0));
+ }
+
+ #[test]
+ fn rustc_wrapper() {
+ // We don't have /usr/bin/env on Windows.
+ if cfg!(windows) { return }
+
+ let p = project("foo")
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .build();
+
+ assert_that(p.cargo("build").arg("-v").env("RUSTC_WRAPPER", "/usr/bin/env"),
+ execs().with_stderr_contains(
+ "[RUNNING] `/usr/bin/env rustc --crate-name foo [..]")
+ .with_status(0));
+ }
+
+ #[test]
+ fn cdylib_not_lifted() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ authors = []
+ version = "0.1.0"
+
+ [lib]
+ crate-type = ["cdylib"]
+ "#)
+ .file("src/lib.rs", "")
+ .build();
+
+ assert_that(p.cargo("build"), execs().with_status(0));
+
+ let files = if cfg!(windows) {
+ vec!["foo.dll.lib", "foo.dll.exp", "foo.dll"]
+ } else if cfg!(target_os = "macos") {
+ vec!["libfoo.dylib"]
+ } else {
+ vec!["libfoo.so"]
+ };
+
+ for file in files {
+ println!("checking: {}", file);
+ assert_that(&p.root().join("target/debug/deps").join(&file),
+ existing_file());
+ }
+ }
+
+ #[test]
+ fn cdylib_final_outputs() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo-bar"
+ authors = []
+ version = "0.1.0"
+
+ [lib]
+ crate-type = ["cdylib"]
+ "#)
+ .file("src/lib.rs", "")
+ .build();
+
+ assert_that(p.cargo("build"), execs().with_status(0));
+
+ let files = if cfg!(windows) {
+ vec!["foo_bar.dll.lib", "foo_bar.dll"]
+ } else if cfg!(target_os = "macos") {
+ vec!["libfoo_bar.dylib"]
+ } else {
+ vec!["libfoo_bar.so"]
+ };
+
+ for file in files {
+ println!("checking: {}", file);
+ assert_that(&p.root().join("target/debug").join(&file), existing_file());
+ }
+ }
+
+ #[test]
+ fn deterministic_cfg_flags() {
+ // This bug is non-deterministic
+
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.1.0"
+ authors = []
+ build = "build.rs"
+
+ [features]
+ default = ["f_a", "f_b", "f_c", "f_d"]
+ f_a = []
+ f_b = []
+ f_c = []
+ f_d = []
+ "#)
+ .file("build.rs", r#"
+ fn main() {
+ println!("cargo:rustc-cfg=cfg_a");
+ println!("cargo:rustc-cfg=cfg_b");
+ println!("cargo:rustc-cfg=cfg_c");
+ println!("cargo:rustc-cfg=cfg_d");
+ println!("cargo:rustc-cfg=cfg_e");
+ }
+ "#)
+ .file("src/main.rs", r#"
+ fn main() {}
+ "#)
+ .build();
+
+ assert_that(p.cargo("build").arg("-v"),
+ execs().with_status(0)
+ .with_stderr("\
+ [COMPILING] foo v0.1.0 [..]
+ [RUNNING] [..]
+ [RUNNING] [..]
+ [RUNNING] `rustc --crate-name foo [..] \
+ --cfg[..]default[..]--cfg[..]f_a[..]--cfg[..]f_b[..]\
+ --cfg[..]f_c[..]--cfg[..]f_d[..] \
+ --cfg cfg_a --cfg cfg_b --cfg cfg_c --cfg cfg_d --cfg cfg_e`
+ [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]"));
+ }
+
+ #[test]
+ fn explicit_bins_without_paths() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ authors = []
+
+ [[bin]]
+ name = "foo"
+
+ [[bin]]
+ name = "bar"
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/main.rs", "fn main() {}")
+ .file("src/bin/bar.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build"), execs().with_status(0));
+ }
+
+ #[test]
+ fn no_bin_in_src_with_lib() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ authors = []
+
+ [[bin]]
+ name = "foo"
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/foo.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs().with_status(101)
+ .with_stderr_contains("\
+ [ERROR] failed to parse manifest at `[..]`
+
+ Caused by:
+ can't find `foo` bin, specify bin.path"));
+ }
+
+
+ #[test]
+ fn inferred_bins() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ authors = []
+ "#)
+ .file("src/main.rs", "fn main() {}")
+ .file("src/bin/bar.rs", "fn main() {}")
+ .file("src/bin/baz/main.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build"), execs().with_status(0));
+ assert_that(&p.bin("foo"), existing_file());
+ assert_that(&p.bin("bar"), existing_file());
+ assert_that(&p.bin("baz"), existing_file());
+ }
+
+ #[test]
+ fn inferred_bins_duplicate_name() {
+ // this should fail, because we have two binaries with the same name
+ let p = project("bar")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "bar"
+ version = "0.1.0"
+ authors = []
+ "#)
+ .file("src/main.rs", "fn main() {}")
+ .file("src/bin/foo.rs", "fn main() {}")
+ .file("src/bin/foo/main.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs().with_status(101)
+ .with_stderr_contains("\
+ [..]found duplicate binary name foo, but all binary targets must have a unique name[..]
+ "));
+ }
+
+ #[test]
+ fn inferred_bin_path() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ authors = []
+
+ [[bin]]
+ name = "bar"
+ # Note, no `path` key!
+ "#)
+ .file("src/bin/bar/main.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build"), execs().with_status(0));
+ assert_that(&p.bin("bar"), existing_file());
+ }
+
+ #[test]
+ fn inferred_examples() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ authors = []
+ "#)
+ .file("src/lib.rs", "fn main() {}")
+ .file("examples/bar.rs", "fn main() {}")
+ .file("examples/baz/main.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("test"), execs().with_status(0));
+ assert_that(&p.bin("examples/bar"), existing_file());
+ assert_that(&p.bin("examples/baz"), existing_file());
+ }
+
+ #[test]
+ fn inferred_tests() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ authors = []
+ "#)
+ .file("src/lib.rs", "fn main() {}")
+ .file("tests/bar.rs", "fn main() {}")
+ .file("tests/baz/main.rs", "fn main() {}")
+ .build();
+
+ assert_that(
+ p.cargo("test").arg("--test=bar").arg("--test=baz"),
+ execs().with_status(0));
+ }
+
+ #[test]
+ fn inferred_benchmarks() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ authors = []
+ "#)
+ .file("src/lib.rs", "fn main() {}")
+ .file("benches/bar.rs", "fn main() {}")
+ .file("benches/baz/main.rs", "fn main() {}")
+ .build();
+
+ assert_that(
+ p.cargo("bench").arg("--bench=bar").arg("--bench=baz"),
+ execs().with_status(0));
+ }
+
+ #[test]
+ fn same_metadata_different_directory() {
+ // A top-level crate built in two different workspaces should have the
+ // same metadata hash.
+ let p = project("foo1")
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .build();
+ let output = t!(String::from_utf8(
+ t!(p.cargo("build").arg("-v").exec_with_output())
+ .stderr,
+ ));
+ let metadata = output
+ .split_whitespace()
+ .find(|arg| arg.starts_with("metadata="))
+ .unwrap();
+
+ let p = project("foo2")
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .build();
+
+ assert_that(
+ p.cargo("build").arg("-v"),
+ execs().with_status(0).with_stderr_contains(
+ format!("[..]{}[..]", metadata),
+ ),
+ );
+ }
+
+ #[test]
+ fn building_a_dependent_crate_witout_bin_should_fail() {
+ Package::new("testless", "0.1.0")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "testless"
+ version = "0.1.0"
+
+ [[bin]]
+ name = "a_bin"
+ "#)
+ .file("src/lib.rs", "")
+ .publish();
+
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ testless = "0.1.0"
+ "#)
+ .file("src/lib.rs", "")
+ .build();
+
+ assert_that(p.cargo("build"),
+ execs().with_status(101).with_stderr_contains(
+ "[..]can't find `a_bin` bin, specify bin.path"
+ ));
+ }
+
+ #[test]
+ fn uplift_dsym_of_bin_on_mac() {
+ if !cfg!(any(target_os = "macos", target_os = "ios")) {
+ return
+ }
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.1.0"
+ "#)
+ .file("src/main.rs", "fn main() { panic!(); }")
+ .file("src/bin/b.rs", "fn main() { panic!(); }")
+ .file("examples/c.rs", "fn main() { panic!(); }")
+ .file("tests/d.rs", "fn main() { panic!(); }")
+ .build();
+
+ assert_that(
+ p.cargo("build").arg("--bins").arg("--examples").arg("--tests"),
+ execs().with_status(0)
+ );
+ assert_that(&p.bin("foo.dSYM"), existing_dir());
+ assert_that(&p.bin("b.dSYM"), existing_dir());
+ assert!(
+ p.bin("b.dSYM")
+ .symlink_metadata()
+ .expect("read metadata from b.dSYM")
+ .file_type()
+ .is_symlink()
+ );
+ assert_that(&p.bin("c.dSYM"), is_not(existing_dir()));
+ assert_that(&p.bin("d.dSYM"), is_not(existing_dir()));
+ }
+
+ // Make sure that `cargo build` chooses the correct profile for building
+ // targets based on filters (assuming --profile is not specified).
+ #[test]
+ fn build_filter_infer_profile() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ authors = []
+ "#)
+ .file("src/lib.rs", "")
+ .file("src/main.rs", "fn main() {}")
+ .file("tests/t1.rs", "")
+ .file("benches/b1.rs", "")
+ .file("examples/ex1.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("build").arg("-v"),
+ execs().with_status(0)
+ .with_stderr_contains("\
+ [RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib \
+ --emit=dep-info,link[..]")
+ .with_stderr_contains("\
+ [RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin \
+ --emit=dep-info,link[..]")
+ );
+
+ p.root().join("target").rm_rf();
+ assert_that(p.cargo("build").arg("-v").arg("--test=t1"),
+ execs().with_status(0)
+ .with_stderr_contains("\
+ [RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib \
+ --emit=dep-info,link[..]")
+ .with_stderr_contains("\
+ [RUNNING] `rustc --crate-name t1 tests[/]t1.rs --emit=dep-info,link[..]")
+ .with_stderr_contains("\
+ [RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin \
+ --emit=dep-info,link[..]")
+ );
+
+ p.root().join("target").rm_rf();
+ assert_that(p.cargo("build").arg("-v").arg("--bench=b1"),
+ execs().with_status(0)
+ .with_stderr_contains("\
+ [RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib \
+ --emit=dep-info,link[..]")
+ .with_stderr_contains("\
+ [RUNNING] `rustc --crate-name b1 benches[/]b1.rs --emit=dep-info,link \
+ -C opt-level=3[..]")
+ .with_stderr_contains("\
+ [RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin \
+ --emit=dep-info,link[..]")
+ );
+ }
+
+ #[test]
+ fn all_targets_no_lib() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ authors = []
+ "#)
+ .file("src/main.rs", "fn main() {}")
+ .build();
+ assert_that(p.cargo("build").arg("-v").arg("--all-targets"),
+ execs().with_status(0)
+ // bin
+ .with_stderr_contains("\
+ [RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin \
+ --emit=dep-info,link[..]")
+ // bench
+ .with_stderr_contains("\
+ [RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,link \
+ -C opt-level=3 --test [..]")
+ // unit test
+ .with_stderr_contains("\
+ [RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,link \
+ -C debuginfo=2 --test [..]")
+ );
+ }
+
+ #[test]
+ fn no_linkable_target() {
+ // Issue 3169. This is currently not an error as per discussion in PR #4797
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ authors = []
+ [dependencies]
+ the_lib = { path = "the_lib" }
+ "#)
+ .file("src/main.rs", "fn main() {}")
+ .file("the_lib/Cargo.toml", r#"
+ [package]
+ name = "the_lib"
+ version = "0.1.0"
+ [lib]
+ name = "the_lib"
+ crate-type = ["staticlib"]
+ "#)
+ .file("the_lib/src/lib.rs", "pub fn foo() {}")
+ .build();
+ assert_that(p.cargo("build"),
+ execs()
+ .with_status(0)
+ .with_stderr_contains("\
+ [WARNING] The package `the_lib` provides no linkable [..] \
+ while compiling `foo`. [..] in `the_lib`'s Cargo.toml. [..]"));
+ }
--- /dev/null
-[ERROR] multiple packages link to native library `a`, but a native library can \
-be linked only once
+ 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::{project, execs};
+ use cargotest::support::paths::CargoPathExt;
+ use cargotest::support::registry::Package;
+ use hamcrest::{assert_that, existing_file, existing_dir};
+
+ #[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");
+ }}
+ "#,
+ 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_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("\
-links to native library `a`
++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 ([..])`
-package `a-sys v0.5.0 ([..])`
- ... which is depended on by `foo v0.5.0 ([..])`
-also links to native library `a`
+
-[ERROR] multiple packages link to native library `a`, but a native library can \
-be linked only once
++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("\
-links to native library `a`
++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 ([..])`
-package `a-sys v0.5.0 ([..])`
- ... which is depended on by `a v0.5.0 ([..])`
- ... which is depended on by `foo v0.5.0 ([..])`
-also links to native library `a`
+
-[ERROR] multiple packages link to native library `a`, but a native library can \
-be linked only once
++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 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_recieves_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("\
- ... which is depended on by `b v0.5.0 ([..])`
-links to native library `a`
++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 ([..])`
-package `a v0.5.0 (file://[..])`
- ... which is depended on by `foo v0.5.0 ([..])`
- ... which is depended on by `b v0.5.0 ([..])`
-also links to native library `a`
+
++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 = []
+ "#)
+ .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] [..]
+ "));
+ }
--- /dev/null
- let summary = Summary::new(pkg.clone(), deps, BTreeMap::new()).unwrap();
+ #![deny(warnings)]
+
+ use std::collections::BTreeMap;
+
+ use hamcrest::{assert_that, equal_to, contains, not};
+
+ use cargo::core::source::{SourceId, GitReference};
+ use cargo::core::dependency::Kind::{self, Development};
+ use cargo::core::{Dependency, PackageId, Summary, Registry};
+ use cargo::util::{CargoResult, ToUrl};
+ use cargo::core::resolver::{self, Method};
+
+ fn resolve(pkg: &PackageId, deps: Vec<Dependency>, registry: &[Summary])
+ -> CargoResult<Vec<PackageId>>
+ {
+ struct MyRegistry<'a>(&'a [Summary]);
+ impl<'a> Registry for MyRegistry<'a> {
+ fn query(&mut self,
+ dep: &Dependency,
+ f: &mut FnMut(Summary)) -> CargoResult<()> {
+ for summary in self.0.iter() {
+ if dep.matches(summary) {
+ f(summary.clone());
+ }
+ }
+ Ok(())
+ }
+ fn supports_checksums(&self) -> bool { false }
+ fn requires_precise(&self) -> bool { false }
+ }
+ let mut registry = MyRegistry(registry);
- Summary::new($pkgid.to_pkgid(), d, BTreeMap::new()).unwrap()
++ let summary = Summary::new(pkg.clone(), deps, BTreeMap::new(), None).unwrap();
+ let method = Method::Everything;
+ let resolve = resolver::resolve(&[(summary, method)], &[], &mut registry, None, false)?;
+ let res = resolve.iter().cloned().collect();
+ Ok(res)
+ }
+
+ trait ToDep {
+ fn to_dep(self) -> Dependency;
+ }
+
+ impl ToDep for &'static str {
+ fn to_dep(self) -> Dependency {
+ let url = "http://example.com".to_url().unwrap();
+ let source_id = SourceId::for_registry(&url).unwrap();
+ Dependency::parse_no_deprecated(self, Some("1.0.0"), &source_id).unwrap()
+ }
+ }
+
+ impl ToDep for Dependency {
+ fn to_dep(self) -> Dependency {
+ self
+ }
+ }
+
+ trait ToPkgId {
+ fn to_pkgid(&self) -> PackageId;
+ }
+
+ impl ToPkgId for &'static str {
+ fn to_pkgid(&self) -> PackageId {
+ PackageId::new(*self, "1.0.0", ®istry_loc()).unwrap()
+ }
+ }
+
+ impl ToPkgId for (&'static str, &'static str) {
+ fn to_pkgid(&self) -> PackageId {
+ let (name, vers) = *self;
+ PackageId::new(name, vers, ®istry_loc()).unwrap()
+ }
+ }
+
+ impl ToPkgId for (&'static str, String) {
+ fn to_pkgid(&self) -> PackageId {
+ let (name, ref vers) = *self;
+ PackageId::new(name, vers, ®istry_loc()).unwrap()
+ }
+ }
+
+ macro_rules! pkg {
+ ($pkgid:expr => [$($deps:expr),+]) => ({
+ let d: Vec<Dependency> = vec![$($deps.to_dep()),+];
++ let pkgid = $pkgid.to_pkgid();
++ let link = if pkgid.name().ends_with("-sys") {Some(pkgid.name().to_string())} else {None};
+
- ($pkgid:expr) => (
- Summary::new($pkgid.to_pkgid(), Vec::new(), BTreeMap::new()).unwrap()
- )
++ Summary::new(pkgid, d, BTreeMap::new(), link).unwrap()
+ });
+
- Summary::new(pkg_id(name), Vec::new(), BTreeMap::new()).unwrap()
++ ($pkgid:expr) => ({
++ let pkgid = $pkgid.to_pkgid();
++ let link = if pkgid.name().ends_with("-sys") {Some(pkgid.name().to_string())} else {None};
++ Summary::new(pkgid, Vec::new(), BTreeMap::new(), link).unwrap()
++ })
+ }
+
+ fn registry_loc() -> SourceId {
+ let remote = "http://example.com".to_url().unwrap();
+ SourceId::for_registry(&remote).unwrap()
+ }
+
+ fn pkg(name: &str) -> Summary {
- Summary::new(pkg_id_loc(name, loc), Vec::new(), BTreeMap::new()).unwrap()
++ let link = if name.ends_with("-sys") {Some(name.to_string())} else {None};
++ Summary::new(pkg_id(name), Vec::new(), BTreeMap::new(), link).unwrap()
+ }
+
+ fn pkg_id(name: &str) -> PackageId {
+ PackageId::new(name, "1.0.0", ®istry_loc()).unwrap()
+ }
+
+ fn pkg_id_loc(name: &str, loc: &str) -> PackageId {
+ let remote = loc.to_url();
+ let master = GitReference::Branch("master".to_string());
+ let source_id = SourceId::for_git(&remote.unwrap(), master).unwrap();
+
+ PackageId::new(name, "1.0.0", &source_id).unwrap()
+ }
+
+ fn pkg_loc(name: &str, loc: &str) -> Summary {
++ let link = if name.ends_with("-sys") {Some(name.to_string())} else {None};
++ Summary::new(pkg_id_loc(name, loc), Vec::new(), BTreeMap::new(), link).unwrap()
+ }
+
+ fn dep(name: &str) -> Dependency { dep_req(name, "1.0.0") }
+ fn dep_req(name: &str, req: &str) -> Dependency {
+ let url = "http://example.com".to_url().unwrap();
+ let source_id = SourceId::for_registry(&url).unwrap();
+ Dependency::parse_no_deprecated(name, Some(req), &source_id).unwrap()
+ }
+
+ fn dep_loc(name: &str, location: &str) -> Dependency {
+ let url = location.to_url().unwrap();
+ let master = GitReference::Branch("master".to_string());
+ let source_id = SourceId::for_git(&url, master).unwrap();
+ Dependency::parse_no_deprecated(name, Some("1.0.0"), &source_id).unwrap()
+ }
+ fn dep_kind(name: &str, kind: Kind) -> Dependency {
+ dep(name).set_kind(kind).clone()
+ }
+
+ fn registry(pkgs: Vec<Summary>) -> Vec<Summary> {
+ pkgs
+ }
+
+ fn names<P: ToPkgId>(names: &[P]) -> Vec<PackageId> {
+ names.iter().map(|name| name.to_pkgid()).collect()
+ }
+
+ fn loc_names(names: &[(&'static str, &'static str)]) -> Vec<PackageId> {
+ names.iter()
+ .map(|&(name, loc)| pkg_id_loc(name, loc)).collect()
+ }
+
+ #[test]
+ fn test_resolving_empty_dependency_list() {
+ let res = resolve(&pkg_id("root"), Vec::new(),
+ ®istry(vec![])).unwrap();
+
+ assert_that(&res, equal_to(&names(&["root"])));
+ }
+
+ #[test]
+ fn test_resolving_only_package() {
+ let reg = registry(vec![pkg("foo")]);
+ let res = resolve(&pkg_id("root"), vec![dep("foo")], ®);
+
+ assert_that(&res.unwrap(), contains(names(&["root", "foo"])).exactly());
+ }
+
+ #[test]
+ fn test_resolving_one_dep() {
+ let reg = registry(vec![pkg("foo"), pkg("bar")]);
+ let res = resolve(&pkg_id("root"), vec![dep("foo")], ®);
+
+ assert_that(&res.unwrap(), contains(names(&["root", "foo"])).exactly());
+ }
+
+ #[test]
+ fn test_resolving_multiple_deps() {
+ let reg = registry(vec![pkg!("foo"), pkg!("bar"), pkg!("baz")]);
+ let res = resolve(&pkg_id("root"), vec![dep("foo"), dep("baz")],
+ ®).unwrap();
+
+ assert_that(&res, contains(names(&["root", "foo", "baz"])).exactly());
+ }
+
+ #[test]
+ fn test_resolving_transitive_deps() {
+ let reg = registry(vec![pkg!("foo"), pkg!("bar" => ["foo"])]);
+ let res = resolve(&pkg_id("root"), vec![dep("bar")], ®).unwrap();
+
+ assert_that(&res, contains(names(&["root", "foo", "bar"])));
+ }
+
+ #[test]
+ fn test_resolving_common_transitive_deps() {
+ let reg = registry(vec![pkg!("foo" => ["bar"]), pkg!("bar")]);
+ let res = resolve(&pkg_id("root"), vec![dep("foo"), dep("bar")],
+ ®).unwrap();
+
+ assert_that(&res, contains(names(&["root", "foo", "bar"])));
+ }
+
+ #[test]
+ fn test_resolving_with_same_name() {
+ let list = vec![pkg_loc("foo", "http://first.example.com"),
+ pkg_loc("bar", "http://second.example.com")];
+
+ let reg = registry(list);
+ let res = resolve(&pkg_id("root"),
+ vec![dep_loc("foo", "http://first.example.com"),
+ dep_loc("bar", "http://second.example.com")],
+ ®);
+
+ let mut names = loc_names(&[("foo", "http://first.example.com"),
+ ("bar", "http://second.example.com")]);
+
+ names.push(pkg_id("root"));
+
+ assert_that(&res.unwrap(), contains(names).exactly());
+ }
+
+ #[test]
+ fn test_resolving_with_dev_deps() {
+ let reg = registry(vec![
+ pkg!("foo" => ["bar", dep_kind("baz", Development)]),
+ pkg!("baz" => ["bat", dep_kind("bam", Development)]),
+ pkg!("bar"),
+ pkg!("bat")
+ ]);
+
+ let res = resolve(&pkg_id("root"),
+ vec![dep("foo"), dep_kind("baz", Development)],
+ ®).unwrap();
+
+ assert_that(&res, contains(names(&["root", "foo", "bar", "baz"])));
+ }
+
+ #[test]
+ fn resolving_with_many_versions() {
+ let reg = registry(vec![
+ pkg!(("foo", "1.0.1")),
+ pkg!(("foo", "1.0.2")),
+ ]);
+
+ let res = resolve(&pkg_id("root"), vec![dep("foo")], ®).unwrap();
+
+ assert_that(&res, contains(names(&[("root", "1.0.0"),
+ ("foo", "1.0.2")])));
+ }
+
+ #[test]
+ fn resolving_with_specific_version() {
+ let reg = registry(vec![
+ pkg!(("foo", "1.0.1")),
+ pkg!(("foo", "1.0.2")),
+ ]);
+
+ let res = resolve(&pkg_id("root"), vec![dep_req("foo", "=1.0.1")],
+ ®).unwrap();
+
+ assert_that(&res, contains(names(&[("root", "1.0.0"),
+ ("foo", "1.0.1")])));
+ }
+
+ #[test]
+ fn test_resolving_maximum_version_with_transitive_deps() {
+ let reg = registry(vec![
+ pkg!(("util", "1.2.2")),
+ pkg!(("util", "1.0.0")),
+ pkg!(("util", "1.1.1")),
+ pkg!("foo" => [dep_req("util", "1.0.0")]),
+ pkg!("bar" => [dep_req("util", ">=1.0.1")]),
+ ]);
+
+ let res = resolve(&pkg_id("root"), vec![dep_req("foo", "1.0.0"), dep_req("bar", "1.0.0")],
+ ®).unwrap();
+
+ assert_that(&res, contains(names(&[("root", "1.0.0"),
+ ("foo", "1.0.0"),
+ ("bar", "1.0.0"),
+ ("util", "1.2.2")])));
+ assert_that(&res, not(contains(names(&[("util", "1.0.1")]))));
+ assert_that(&res, not(contains(names(&[("util", "1.1.1")]))));
+ }
+
+ #[test]
+ fn resolving_incompat_versions() {
+ let reg = registry(vec![
+ pkg!(("foo", "1.0.1")),
+ pkg!(("foo", "1.0.2")),
+ pkg!("bar" => [dep_req("foo", "=1.0.2")]),
+ ]);
+
+ assert!(resolve(&pkg_id("root"), vec![
+ dep_req("foo", "=1.0.1"),
+ dep("bar"),
+ ], ®).is_err());
+ }
+
+ #[test]
+ fn resolving_backtrack() {
+ let reg = registry(vec![
+ pkg!(("foo", "1.0.2") => [dep("bar")]),
+ pkg!(("foo", "1.0.1") => [dep("baz")]),
+ pkg!("bar" => [dep_req("foo", "=2.0.2")]),
+ pkg!("baz"),
+ ]);
+
+ let res = resolve(&pkg_id("root"), vec![
+ dep_req("foo", "^1"),
+ ], ®).unwrap();
+
+ assert_that(&res, contains(names(&[("root", "1.0.0"),
+ ("foo", "1.0.1"),
+ ("baz", "1.0.0")])));
+ }
+
+ #[test]
+ fn resolving_allows_multiple_compatible_versions() {
+ let reg = registry(vec![
+ pkg!(("foo", "1.0.0")),
+ pkg!(("foo", "2.0.0")),
+ pkg!(("foo", "0.1.0")),
+ pkg!(("foo", "0.2.0")),
+
+ pkg!("bar" => ["d1", "d2", "d3", "d4"]),
+ pkg!("d1" => [dep_req("foo", "1")]),
+ pkg!("d2" => [dep_req("foo", "2")]),
+ pkg!("d3" => [dep_req("foo", "0.1")]),
+ pkg!("d4" => [dep_req("foo", "0.2")]),
+ ]);
+
+ let res = resolve(&pkg_id("root"), vec![
+ dep("bar"),
+ ], ®).unwrap();
+
+ assert_that(&res, contains(names(&[("root", "1.0.0"),
+ ("foo", "1.0.0"),
+ ("foo", "2.0.0"),
+ ("foo", "0.1.0"),
+ ("foo", "0.2.0"),
+ ("d1", "1.0.0"),
+ ("d2", "1.0.0"),
+ ("d3", "1.0.0"),
+ ("d4", "1.0.0"),
+ ("bar", "1.0.0")])));
+ }
+
+ #[test]
+ fn resolving_with_deep_backtracking() {
+ let reg = registry(vec![
+ pkg!(("foo", "1.0.1") => [dep_req("bar", "1")]),
+ pkg!(("foo", "1.0.0") => [dep_req("bar", "2")]),
+
+ pkg!(("bar", "1.0.0") => [dep_req("baz", "=1.0.2"),
+ dep_req("other", "1")]),
+ pkg!(("bar", "2.0.0") => [dep_req("baz", "=1.0.1")]),
+
+ pkg!(("baz", "1.0.2") => [dep_req("other", "2")]),
+ pkg!(("baz", "1.0.1")),
+
+ pkg!(("dep_req", "1.0.0")),
+ pkg!(("dep_req", "2.0.0")),
+ ]);
+
+ let res = resolve(&pkg_id("root"), vec![
+ dep_req("foo", "1"),
+ ], ®).unwrap();
+
+ assert_that(&res, contains(names(&[("root", "1.0.0"),
+ ("foo", "1.0.0"),
+ ("bar", "2.0.0"),
+ ("baz", "1.0.1")])));
+ }
+
++#[test]
++fn resolving_with_sys_crates() {
++ // This is based on issues/4902
++ // With `l` a normal library we get 2copies so everyone gets the newest compatible.
++ // But `l-sys` a library with a links attribute we make sure there is only one.
++ let reg = registry(vec![
++ pkg!(("l-sys", "0.9.1")),
++ pkg!(("l-sys", "0.10.0")),
++ pkg!(("l", "0.9.1")),
++ pkg!(("l", "0.10.0")),
++ pkg!(("d", "1.0.0") => [dep_req("l-sys", ">=0.8.0, <=0.10.0"), dep_req("l", ">=0.8.0, <=0.10.0")]),
++ pkg!(("r", "1.0.0") => [dep_req("l-sys", "0.9"), dep_req("l", "0.9")]),
++ ]);
++
++ let res = resolve(&pkg_id("root"), vec![
++ dep_req("d", "1"),
++ dep_req("r", "1"),
++ ], ®).unwrap();
++
++ assert_that(&res, contains(names(&[("root", "1.0.0"),
++ ("d", "1.0.0"),
++ ("r", "1.0.0"),
++ ("l-sys", "0.9.1"),
++ ("l", "0.9.1"),
++ ("l", "0.10.0")])));
++}
++
+ #[test]
+ fn resolving_with_constrained_sibling_backtrack_parent() {
+ // There is no point in considering all of the backtrack_trap{1,2}
+ // candidates since they can't change the result of failing to
+ // resolve 'constrained'. Cargo should (ideally) skip past them and resume
+ // resolution once the activation of the parent, 'bar', is rolled back.
+ // Note that the traps are slightly more constrained to make sure they
+ // get picked first.
+ let mut reglist = vec![
+ pkg!(("foo", "1.0.0") => [dep_req("bar", "1.0"),
+ dep_req("constrained", "=1.0.0")]),
+
+ pkg!(("bar", "1.0.0") => [dep_req("backtrack_trap1", "1.0.2"),
+ dep_req("backtrack_trap2", "1.0.2"),
+ dep_req("constrained", "1.0.0")]),
+ pkg!(("constrained", "1.0.0")),
+ pkg!(("backtrack_trap1", "1.0.0")),
+ pkg!(("backtrack_trap2", "1.0.0")),
+ ];
+ // Bump this to make the test harder - it adds more versions of bar that will
+ // fail to resolve, and more versions of the traps to consider.
+ const NUM_BARS_AND_TRAPS: usize = 50; // minimum 2
+ for i in 1..NUM_BARS_AND_TRAPS {
+ let vsn = format!("1.0.{}", i);
+ reglist.push(pkg!(("bar", vsn.clone()) => [dep_req("backtrack_trap1", "1.0.2"),
+ dep_req("backtrack_trap2", "1.0.2"),
+ dep_req("constrained", "1.0.1")]));
+ reglist.push(pkg!(("backtrack_trap1", vsn.clone())));
+ reglist.push(pkg!(("backtrack_trap2", vsn.clone())));
+ reglist.push(pkg!(("constrained", vsn.clone())));
+ }
+ let reg = registry(reglist);
+
+ let res = resolve(&pkg_id("root"), vec![
+ dep_req("foo", "1"),
+ ], ®).unwrap();
+
+ assert_that(&res, contains(names(&[("root", "1.0.0"),
+ ("foo", "1.0.0"),
+ ("bar", "1.0.0"),
+ ("constrained", "1.0.0")])));
+ }
+
+ #[test]
+ fn resolving_with_constrained_sibling_backtrack_activation() {
+ // It makes sense to resolve most-constrained deps first, but
+ // with that logic the backtrack traps here come between the two
+ // attempted resolutions of 'constrained'. When backtracking,
+ // cargo should skip past them and resume resolution once the
+ // number of activations for 'constrained' changes.
+ let mut reglist = vec![
+ pkg!(("foo", "1.0.0") => [dep_req("bar", "=1.0.0"),
+ dep_req("backtrack_trap1", "1.0"),
+ dep_req("backtrack_trap2", "1.0"),
+ dep_req("constrained", "<=1.0.60")]),
+ pkg!(("bar", "1.0.0") => [dep_req("constrained", ">=1.0.60")]),
+ ];
+ // Bump these to make the test harder, but you'll also need to
+ // change the version constraints on `constrained` above. To correctly
+ // exercise Cargo, the relationship between the values is:
+ // NUM_CONSTRAINED - vsn < NUM_TRAPS < vsn
+ // to make sure the traps are resolved between `constrained`.
+ const NUM_TRAPS: usize = 45; // min 1
+ const NUM_CONSTRAINED: usize = 100; // min 1
+ for i in 0..NUM_TRAPS {
+ let vsn = format!("1.0.{}", i);
+ reglist.push(pkg!(("backtrack_trap1", vsn.clone())));
+ reglist.push(pkg!(("backtrack_trap2", vsn.clone())));
+ }
+ for i in 0..NUM_CONSTRAINED {
+ let vsn = format!("1.0.{}", i);
+ reglist.push(pkg!(("constrained", vsn.clone())));
+ }
+ let reg = registry(reglist);
+
+ let res = resolve(&pkg_id("root"), vec![
+ dep_req("foo", "1"),
+ ], ®).unwrap();
+
+ assert_that(&res, contains(names(&[("root", "1.0.0"),
+ ("foo", "1.0.0"),
+ ("bar", "1.0.0"),
+ ("constrained", "1.0.60")])));
+ }
+
+ #[test]
+ fn resolving_with_constrained_sibling_transitive_dep_effects() {
+ // When backtracking due to a failed dependency, if Cargo is
+ // trying to be clever and skip irrelevant dependencies, care must
+ // be taken to not miss the transitive effects of alternatives. E.g.
+ // in the right-to-left resolution of the graph below, B may
+ // affect whether D is successfully resolved.
+ //
+ // A
+ // / | \
+ // B C D
+ // | |
+ // C D
+ let reg = registry(vec![
+ pkg!(("A", "1.0.0") => [dep_req("B", "1.0"),
+ dep_req("C", "1.0"),
+ dep_req("D", "1.0.100")]),
+
+ pkg!(("B", "1.0.0") => [dep_req("C", ">=1.0.0")]),
+ pkg!(("B", "1.0.1") => [dep_req("C", ">=1.0.1")]),
+
+ pkg!(("C", "1.0.0") => [dep_req("D", "1.0.0")]),
+ pkg!(("C", "1.0.1") => [dep_req("D", ">=1.0.1,<1.0.100")]),
+ pkg!(("C", "1.0.2") => [dep_req("D", ">=1.0.2,<1.0.100")]),
+
+ pkg!(("D", "1.0.0")),
+ pkg!(("D", "1.0.1")),
+ pkg!(("D", "1.0.2")),
+ pkg!(("D", "1.0.100")),
+ pkg!(("D", "1.0.101")),
+ pkg!(("D", "1.0.102")),
+ pkg!(("D", "1.0.103")),
+ pkg!(("D", "1.0.104")),
+ pkg!(("D", "1.0.105")),
+ ]);
+
+ let res = resolve(&pkg_id("root"), vec![
+ dep_req("A", "1"),
+ ], ®).unwrap();
+
+ assert_that(&res, contains(names(&[("A", "1.0.0"),
+ ("B", "1.0.0"),
+ ("C", "1.0.0"),
+ ("D", "1.0.105")])));
+ }
+
+ #[test]
+ fn resolving_but_no_exists() {
+ let reg = registry(vec![
+ ]);
+
+ let res = resolve(&pkg_id("root"), vec![
+ dep_req("foo", "1"),
+ ], ®);
+ assert!(res.is_err());
+
+ assert_eq!(res.err().unwrap().to_string(), "\
+ no matching package named `foo` found\n\
+ location searched: registry `http://example.com/`\n\
+ required by package `root v1.0.0 (registry `http://example.com/`)`\
+ ");
+ }
+
+ #[test]
+ fn resolving_cycle() {
+ let reg = registry(vec![
+ pkg!("foo" => ["foo"]),
+ ]);
+
+ let _ = resolve(&pkg_id("root"), vec![
+ dep_req("foo", "1"),
+ ], ®);
+ }
+
+ #[test]
+ fn hard_equality() {
+ let reg = registry(vec![
+ pkg!(("foo", "1.0.1")),
+ pkg!(("foo", "1.0.0")),
+
+ pkg!(("bar", "1.0.0") => [dep_req("foo", "1.0.0")]),
+ ]);
+
+ let res = resolve(&pkg_id("root"), vec![
+ dep_req("bar", "1"),
+ dep_req("foo", "=1.0.0"),
+ ], ®).unwrap();
+
+ assert_that(&res, contains(names(&[("root", "1.0.0"),
+ ("foo", "1.0.0"),
+ ("bar", "1.0.0")])));
+ }