summaryrefslogtreecommitdiffstats
path: root/src/tools/cargo/tests/testsuite/lto.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/cargo/tests/testsuite/lto.rs')
-rw-r--r--src/tools/cargo/tests/testsuite/lto.rs850
1 files changed, 850 insertions, 0 deletions
diff --git a/src/tools/cargo/tests/testsuite/lto.rs b/src/tools/cargo/tests/testsuite/lto.rs
new file mode 100644
index 000000000..40b4f7ca2
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/lto.rs
@@ -0,0 +1,850 @@
+use cargo::core::compiler::Lto;
+use cargo_test_support::registry::Package;
+use cargo_test_support::{basic_manifest, project, Project};
+use std::process::Output;
+
+#[cargo_test]
+fn with_deps() {
+ Package::new("bar", "0.0.1").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "test"
+ version = "0.0.0"
+
+ [dependencies]
+ bar = "*"
+
+ [profile.release]
+ lto = true
+ "#,
+ )
+ .file("src/main.rs", "extern crate bar; fn main() {}")
+ .build();
+ p.cargo("build -v --release")
+ .with_stderr_contains("[..]`rustc[..]--crate-name bar[..]-C linker-plugin-lto[..]`")
+ .with_stderr_contains("[..]`rustc[..]--crate-name test[..]-C lto[..]`")
+ .run();
+}
+
+#[cargo_test]
+fn shared_deps() {
+ Package::new("bar", "0.0.1").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "test"
+ version = "0.0.0"
+
+ [dependencies]
+ bar = "*"
+
+ [build-dependencies]
+ bar = "*"
+
+ [profile.release]
+ lto = true
+ "#,
+ )
+ .file("build.rs", "extern crate bar; fn main() {}")
+ .file("src/main.rs", "extern crate bar; fn main() {}")
+ .build();
+ p.cargo("build -v --release")
+ .with_stderr_contains("[..]`rustc[..]--crate-name test[..]-C lto[..]`")
+ .run();
+}
+
+#[cargo_test]
+fn build_dep_not_ltod() {
+ Package::new("bar", "0.0.1").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "test"
+ version = "0.0.0"
+
+ [build-dependencies]
+ bar = "*"
+
+ [profile.release]
+ lto = true
+ "#,
+ )
+ .file("build.rs", "extern crate bar; fn main() {}")
+ .file("src/main.rs", "fn main() {}")
+ .build();
+ p.cargo("build -v --release")
+ .with_stderr_contains("[..]`rustc[..]--crate-name bar[..]-C embed-bitcode=no[..]`")
+ .with_stderr_contains("[..]`rustc[..]--crate-name test[..]-C lto[..]`")
+ .run();
+}
+
+#[cargo_test]
+fn complicated() {
+ Package::new("dep-shared", "0.0.1")
+ .file("src/lib.rs", "pub fn foo() {}")
+ .publish();
+ Package::new("dep-normal2", "0.0.1")
+ .file("src/lib.rs", "pub fn foo() {}")
+ .publish();
+ Package::new("dep-normal", "0.0.1")
+ .dep("dep-shared", "*")
+ .dep("dep-normal2", "*")
+ .file(
+ "src/lib.rs",
+ "
+ pub fn foo() {
+ dep_shared::foo();
+ dep_normal2::foo();
+ }
+ ",
+ )
+ .publish();
+ Package::new("dep-build2", "0.0.1")
+ .file("src/lib.rs", "pub fn foo() {}")
+ .publish();
+ Package::new("dep-build", "0.0.1")
+ .dep("dep-shared", "*")
+ .dep("dep-build2", "*")
+ .file(
+ "src/lib.rs",
+ "
+ pub fn foo() {
+ dep_shared::foo();
+ dep_build2::foo();
+ }
+ ",
+ )
+ .publish();
+ Package::new("dep-proc-macro2", "0.0.1")
+ .file("src/lib.rs", "pub fn foo() {}")
+ .publish();
+ Package::new("dep-proc-macro", "0.0.1")
+ .proc_macro(true)
+ .dep("dep-shared", "*")
+ .dep("dep-proc-macro2", "*")
+ .file(
+ "src/lib.rs",
+ "
+ extern crate proc_macro;
+ use proc_macro::TokenStream;
+
+ #[proc_macro_attribute]
+ pub fn foo(_: TokenStream, a: TokenStream) -> TokenStream {
+ dep_shared::foo();
+ dep_proc_macro2::foo();
+ a
+ }
+ ",
+ )
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "test"
+ version = "0.0.0"
+
+ [lib]
+ crate-type = ['cdylib', 'staticlib']
+
+ [dependencies]
+ dep-normal = "*"
+ dep-proc-macro = "*"
+
+ [build-dependencies]
+ dep-build = "*"
+
+ [profile.release]
+ lto = true
+
+ # force build deps to share an opt-level with the rest of the
+ # graph so they only get built once.
+ [profile.release.build-override]
+ opt-level = 3
+ "#,
+ )
+ .file("build.rs", "fn main() { dep_build::foo() }")
+ .file(
+ "src/bin/foo-bin.rs",
+ "#[dep_proc_macro::foo] fn main() { dep_normal::foo() }",
+ )
+ .file(
+ "src/lib.rs",
+ "#[dep_proc_macro::foo] pub fn foo() { dep_normal::foo() }",
+ )
+ .build();
+ p.cargo("build -v --release")
+ // normal deps and their transitive dependencies do not need object
+ // code, so they should have linker-plugin-lto specified
+ .with_stderr_contains(
+ "[..]`rustc[..]--crate-name dep_normal2 [..]-C linker-plugin-lto[..]`",
+ )
+ .with_stderr_contains("[..]`rustc[..]--crate-name dep_normal [..]-C linker-plugin-lto[..]`")
+ // build dependencies and their transitive deps don't need any bitcode,
+ // so embedding should be turned off
+ .with_stderr_contains("[..]`rustc[..]--crate-name dep_build2 [..]-C embed-bitcode=no[..]`")
+ .with_stderr_contains("[..]`rustc[..]--crate-name dep_build [..]-C embed-bitcode=no[..]`")
+ .with_stderr_contains(
+ "[..]`rustc[..]--crate-name build_script_build [..]-C embed-bitcode=no[..]`",
+ )
+ // proc macro deps are the same as build deps here
+ .with_stderr_contains(
+ "[..]`rustc[..]--crate-name dep_proc_macro2 [..]-C embed-bitcode=no[..]`",
+ )
+ .with_stderr_contains(
+ "[..]`rustc[..]--crate-name dep_proc_macro [..]-C embed-bitcode=no[..]`",
+ )
+ .with_stderr_contains(
+ "[..]`rustc[..]--crate-name foo_bin [..]--crate-type bin[..]-C lto[..]`",
+ )
+ .with_stderr_contains(
+ "[..]`rustc[..]--crate-name test [..]--crate-type cdylib[..]-C lto[..]`",
+ )
+ .with_stderr_contains("[..]`rustc[..]--crate-name dep_shared [..]`")
+ .with_stderr_does_not_contain("[..]--crate-name dep_shared[..]-C lto[..]")
+ .with_stderr_does_not_contain("[..]--crate-name dep_shared[..]-C linker-plugin-lto[..]")
+ .with_stderr_does_not_contain("[..]--crate-name dep_shared[..]-C embed-bitcode[..]")
+ .run();
+}
+
+#[cargo_test]
+fn off_in_manifest_works() {
+ Package::new("bar", "0.0.1")
+ .file("src/lib.rs", "pub fn foo() {}")
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "test"
+ version = "0.0.0"
+
+ [dependencies]
+ bar = "*"
+
+ [profile.release]
+ lto = "off"
+ "#,
+ )
+ .file("src/lib.rs", "pub fn foo() {}")
+ .file(
+ "src/main.rs",
+ "fn main() {
+ test::foo();
+ bar::foo();
+ }",
+ )
+ .build();
+ p.cargo("build -v --release")
+ .with_stderr(
+ "\
+[UPDATING] [..]
+[DOWNLOADING] [..]
+[DOWNLOADED] [..]
+[COMPILING] bar v0.0.1
+[RUNNING] `rustc --crate-name bar [..]--crate-type lib [..]-C lto=off -C embed-bitcode=no[..]
+[COMPILING] test [..]
+[RUNNING] `rustc --crate-name test [..]--crate-type lib [..]-C lto=off -C embed-bitcode=no[..]
+[RUNNING] `rustc --crate-name test src/main.rs [..]--crate-type bin [..]-C lto=off[..]
+[FINISHED] [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn between_builds() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "test"
+ version = "0.0.0"
+
+ [profile.release]
+ lto = true
+ "#,
+ )
+ .file("src/lib.rs", "pub fn foo() {}")
+ .file("src/main.rs", "fn main() { test::foo() }")
+ .build();
+ p.cargo("build -v --release --lib")
+ .with_stderr(
+ "\
+[COMPILING] test [..]
+[RUNNING] `rustc [..]--crate-type lib[..]-C linker-plugin-lto[..]
+[FINISHED] [..]
+",
+ )
+ .run();
+ p.cargo("build -v --release")
+ .with_stderr_contains(
+ "\
+[COMPILING] test [..]
+[RUNNING] `rustc [..]--crate-type bin[..]-C lto[..]
+[FINISHED] [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn test_all() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+
+ [profile.release]
+ lto = true
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .file("tests/a.rs", "")
+ .file("tests/b.rs", "")
+ .build();
+ p.cargo("test --release -v")
+ .with_stderr_contains("[RUNNING] `rustc[..]--crate-name foo[..]-C lto[..]")
+ .run();
+}
+
+#[cargo_test]
+fn test_all_and_bench() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+
+ [profile.release]
+ lto = true
+ [profile.bench]
+ lto = true
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .file("tests/a.rs", "")
+ .file("tests/b.rs", "")
+ .build();
+ p.cargo("test --release -v")
+ .with_stderr_contains("[RUNNING] `rustc[..]--crate-name a[..]-C lto[..]")
+ .with_stderr_contains("[RUNNING] `rustc[..]--crate-name b[..]-C lto[..]")
+ .with_stderr_contains("[RUNNING] `rustc[..]--crate-name foo[..]-C lto[..]")
+ .run();
+}
+
+/// Basic setup:
+///
+/// foo v0.0.0
+/// ├── bar v0.0.0
+/// │ ├── registry v0.0.1
+/// │ └── registry-shared v0.0.1
+/// └── registry-shared v0.0.1
+///
+/// Where `bar` will have the given crate types.
+fn project_with_dep(crate_types: &str) -> Project {
+ Package::new("registry", "0.0.1")
+ .file("src/lib.rs", r#"pub fn foo() { println!("registry"); }"#)
+ .publish();
+ Package::new("registry-shared", "0.0.1")
+ .file("src/lib.rs", r#"pub fn foo() { println!("shared"); }"#)
+ .publish();
+
+ project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+
+ [workspace]
+
+ [dependencies]
+ bar = { path = 'bar' }
+ registry-shared = "*"
+
+ [profile.release]
+ lto = true
+ "#,
+ )
+ .file(
+ "src/main.rs",
+ "
+ fn main() {
+ bar::foo();
+ registry_shared::foo();
+ }
+ ",
+ )
+ .file(
+ "bar/Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "bar"
+ version = "0.0.0"
+
+ [dependencies]
+ registry = "*"
+ registry-shared = "*"
+
+ [lib]
+ crate-type = [{}]
+ "#,
+ crate_types
+ ),
+ )
+ .file(
+ "bar/src/lib.rs",
+ r#"
+ pub fn foo() {
+ println!("bar");
+ registry::foo();
+ registry_shared::foo();
+ }
+ "#,
+ )
+ .file("tests/a.rs", "")
+ .file("bar/tests/b.rs", "")
+ .build()
+}
+
+/// Helper for checking which LTO behavior is used for a specific crate.
+///
+/// `krate_info` is extra compiler flags used to distinguish this if the same
+/// crate name is being built multiple times.
+fn verify_lto(output: &Output, krate: &str, krate_info: &str, expected_lto: Lto) {
+ let stderr = std::str::from_utf8(&output.stderr).unwrap();
+ let mut matches = stderr.lines().filter(|line| {
+ line.contains("Running")
+ && line.contains(&format!("--crate-name {} ", krate))
+ && line.contains(krate_info)
+ });
+ let line = matches.next().unwrap_or_else(|| {
+ panic!(
+ "expected to find crate `{}` info: `{}`, not found in output:\n{}",
+ krate, krate_info, stderr
+ );
+ });
+ if let Some(line2) = matches.next() {
+ panic!(
+ "found multiple lines matching crate `{}` info: `{}`:\nline1:{}\nline2:{}\noutput:\n{}",
+ krate, krate_info, line, line2, stderr
+ );
+ }
+ let actual_lto = if let Some(index) = line.find("-C lto=") {
+ let s = &line[index..];
+ let end = s.find(' ').unwrap();
+ let mode = &line[index..index + end];
+ if mode == "off" {
+ Lto::Off
+ } else {
+ Lto::Run(Some(mode.into()))
+ }
+ } else if line.contains("-C lto") {
+ Lto::Run(None)
+ } else if line.contains("-C linker-plugin-lto") {
+ Lto::OnlyBitcode
+ } else if line.contains("-C embed-bitcode=no") {
+ Lto::OnlyObject
+ } else {
+ Lto::ObjectAndBitcode
+ };
+ assert_eq!(
+ actual_lto, expected_lto,
+ "did not find expected LTO in line: {}",
+ line
+ );
+}
+
+#[cargo_test]
+fn cdylib_and_rlib() {
+ let p = project_with_dep("'cdylib', 'rlib'");
+ let output = p.cargo("build --release -v").exec_with_output().unwrap();
+ // `registry` is ObjectAndBitcode because it needs Object for the
+ // rlib, and Bitcode for the cdylib (which doesn't support LTO).
+ verify_lto(
+ &output,
+ "registry",
+ "--crate-type lib",
+ Lto::ObjectAndBitcode,
+ );
+ // Same as `registry`
+ verify_lto(
+ &output,
+ "registry_shared",
+ "--crate-type lib",
+ Lto::ObjectAndBitcode,
+ );
+ // Same as `registry`
+ verify_lto(
+ &output,
+ "bar",
+ "--crate-type cdylib --crate-type rlib",
+ Lto::ObjectAndBitcode,
+ );
+ verify_lto(&output, "foo", "--crate-type bin", Lto::Run(None));
+ p.cargo("test --release -v")
+ .with_stderr_unordered(
+ "\
+[FRESH] registry v0.0.1
+[FRESH] registry-shared v0.0.1
+[FRESH] bar v0.0.0 [..]
+[COMPILING] foo [..]
+[RUNNING] `rustc --crate-name foo [..]-C lto [..]--test[..]
+[RUNNING] `rustc --crate-name a [..]-C lto [..]--test[..]
+[FINISHED] [..]
+[RUNNING] [..]
+[RUNNING] [..]
+",
+ )
+ .run();
+ p.cargo("build --release -v --manifest-path bar/Cargo.toml")
+ .with_stderr_unordered(
+ "\
+[FRESH] registry-shared v0.0.1
+[FRESH] registry v0.0.1
+[FRESH] bar v0.0.0 [..]
+[FINISHED] [..]
+",
+ )
+ .run();
+ p.cargo("test --release -v --manifest-path bar/Cargo.toml")
+ .with_stderr_unordered(
+ "\
+[FRESH] registry-shared v0.0.1
+[FRESH] registry v0.0.1
+[COMPILING] bar [..]
+[RUNNING] `rustc --crate-name bar [..]-C lto[..]--test[..]
+[RUNNING] `rustc --crate-name b [..]-C lto[..]--test[..]
+[FINISHED] [..]
+[RUNNING] [..]target/release/deps/bar-[..]
+[RUNNING] [..]target/release/deps/b-[..]
+[DOCTEST] bar
+[RUNNING] `rustdoc --crate-type cdylib --crate-type rlib --crate-name bar --test [..]-C lto[..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn dylib() {
+ let p = project_with_dep("'dylib'");
+ let output = p.cargo("build --release -v").exec_with_output().unwrap();
+ // `registry` is OnlyObject because rustc doesn't support LTO with dylibs.
+ verify_lto(&output, "registry", "--crate-type lib", Lto::OnlyObject);
+ // `registry_shared` is both because it is needed by both bar (Object) and
+ // foo (Bitcode for LTO).
+ verify_lto(
+ &output,
+ "registry_shared",
+ "--crate-type lib",
+ Lto::ObjectAndBitcode,
+ );
+ // `bar` is OnlyObject because rustc doesn't support LTO with dylibs.
+ verify_lto(&output, "bar", "--crate-type dylib", Lto::OnlyObject);
+ // `foo` is LTO because it is a binary, and the profile specifies `lto=true`.
+ verify_lto(&output, "foo", "--crate-type bin", Lto::Run(None));
+ // `cargo test` should not rebuild dependencies. It builds the test
+ // executables with `lto=true` because the tests are built with the
+ // `--release` flag.
+ p.cargo("test --release -v")
+ .with_stderr_unordered(
+ "\
+[FRESH] registry v0.0.1
+[FRESH] registry-shared v0.0.1
+[FRESH] bar v0.0.0 [..]
+[COMPILING] foo [..]
+[RUNNING] `rustc --crate-name foo [..]-C lto [..]--test[..]
+[RUNNING] `rustc --crate-name a [..]-C lto [..]--test[..]
+[FINISHED] [..]
+[RUNNING] [..]
+[RUNNING] [..]
+",
+ )
+ .run();
+ // Building just `bar` causes `registry-shared` to get rebuilt because it
+ // switches to OnlyObject because it is now only being used with a dylib
+ // which does not support LTO.
+ //
+ // `bar` gets rebuilt because `registry_shared` got rebuilt.
+ p.cargo("build --release -v --manifest-path bar/Cargo.toml")
+ .with_stderr_unordered(
+ "\
+[COMPILING] registry-shared v0.0.1
+[FRESH] registry v0.0.1
+[RUNNING] `rustc --crate-name registry_shared [..]-C embed-bitcode=no[..]
+[DIRTY] bar v0.0.0 ([..]): dependency info changed
+[COMPILING] bar [..]
+[RUNNING] `rustc --crate-name bar [..]--crate-type dylib [..]-C embed-bitcode=no[..]
+[FINISHED] [..]
+",
+ )
+ .run();
+ // Testing just `bar` causes `registry` to get rebuilt because it switches
+ // to needing both Object (for the `bar` dylib) and Bitcode (for the test
+ // built with LTO).
+ //
+ // `bar` the dylib gets rebuilt because `registry` got rebuilt.
+ p.cargo("test --release -v --manifest-path bar/Cargo.toml")
+ .with_stderr_unordered(
+ "\
+[FRESH] registry-shared v0.0.1
+[COMPILING] registry v0.0.1
+[RUNNING] `rustc --crate-name registry [..]
+[DIRTY] bar v0.0.0 ([..]): dependency info changed
+[COMPILING] bar [..]
+[RUNNING] `rustc --crate-name bar [..]--crate-type dylib [..]-C embed-bitcode=no[..]
+[RUNNING] `rustc --crate-name bar [..]-C lto [..]--test[..]
+[RUNNING] `rustc --crate-name b [..]-C lto [..]--test[..]
+[FINISHED] [..]
+[RUNNING] [..]
+[RUNNING] [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+// This is currently broken on windows-gnu, see https://github.com/rust-lang/rust/issues/109797
+#[cfg_attr(
+ all(target_os = "windows", target_env = "gnu"),
+ ignore = "windows-gnu not working"
+)]
+fn test_profile() {
+ Package::new("bar", "0.0.1")
+ .file("src/lib.rs", "pub fn foo() -> i32 { 123 } ")
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ edition = "2018"
+
+ [profile.test]
+ lto = 'thin'
+
+ [dependencies]
+ bar = "*"
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ #[test]
+ fn t1() {
+ assert_eq!(123, bar::foo());
+ }
+ "#,
+ )
+ .build();
+
+ p.cargo("test -v")
+ // unordered because the two `foo` builds start in parallel
+ .with_stderr_unordered("\
+[UPDATING] [..]
+[DOWNLOADING] [..]
+[DOWNLOADED] [..]
+[COMPILING] bar v0.0.1
+[RUNNING] `rustc --crate-name bar [..]crate-type lib[..]
+[COMPILING] foo [..]
+[RUNNING] `rustc --crate-name foo [..]--crate-type lib --emit=dep-info,metadata,link -C linker-plugin-lto[..]
+[RUNNING] `rustc --crate-name foo [..]--emit=dep-info,link -C lto=thin [..]--test[..]
+[FINISHED] [..]
+[RUNNING] [..]
+[DOCTEST] foo
+[RUNNING] `rustdoc [..]
+")
+ .run();
+}
+
+#[cargo_test]
+fn doctest() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ edition = "2018"
+
+ [profile.release]
+ lto = true
+
+ [dependencies]
+ bar = { path = "bar" }
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ /// Foo!
+ ///
+ /// ```
+ /// foo::foo();
+ /// ```
+ pub fn foo() { bar::bar(); }
+ "#,
+ )
+ .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
+ .file(
+ "bar/src/lib.rs",
+ r#"
+ pub fn bar() { println!("hi!"); }
+ "#,
+ )
+ .build();
+
+ p.cargo("test --doc --release -v")
+ .with_stderr_contains("[..]`rustc --crate-name bar[..]-C linker-plugin-lto[..]")
+ .with_stderr_contains("[..]`rustc --crate-name foo[..]-C linker-plugin-lto[..]")
+ // embed-bitcode should be harmless here
+ .with_stderr_contains("[..]`rustdoc [..]-C lto[..]")
+ .run();
+
+ // Try with bench profile.
+ p.cargo("test --doc --release -v")
+ .env("CARGO_PROFILE_BENCH_LTO", "true")
+ .with_stderr_unordered(
+ "\
+[FRESH] bar v0.1.0 [..]
+[FRESH] foo v0.1.0 [..]
+[FINISHED] release [..]
+[DOCTEST] foo
+[RUNNING] `rustdoc [..]-C lto[..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn dylib_rlib_bin() {
+ // dylib+rlib linked with a binary
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [lib]
+ crate-type = ["dylib", "rlib"]
+
+ [profile.release]
+ lto = true
+ "#,
+ )
+ .file("src/lib.rs", "pub fn foo() { println!(\"hi!\"); }")
+ .file("src/bin/ferret.rs", "fn main() { foo::foo(); }")
+ .build();
+
+ let output = p.cargo("build --release -v").exec_with_output().unwrap();
+ verify_lto(
+ &output,
+ "foo",
+ "--crate-type dylib --crate-type rlib",
+ Lto::ObjectAndBitcode,
+ );
+ verify_lto(&output, "ferret", "--crate-type bin", Lto::Run(None));
+}
+
+#[cargo_test]
+fn fresh_swapping_commands() {
+ // In some rare cases, different commands end up building dependencies
+ // with different LTO settings. This checks that it doesn't cause the
+ // cache to thrash in that scenario.
+ Package::new("bar", "1.0.0").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = "1.0"
+
+ [profile.release]
+ lto = true
+ "#,
+ )
+ .file("src/lib.rs", "pub fn foo() { println!(\"hi!\"); }")
+ .build();
+
+ p.cargo("build --release -v")
+ .with_stderr(
+ "\
+[UPDATING] [..]
+[DOWNLOADING] crates ...
+[DOWNLOADED] bar v1.0.0 [..]
+[COMPILING] bar v1.0.0
+[RUNNING] `rustc --crate-name bar [..]-C linker-plugin-lto[..]
+[COMPILING] foo v0.1.0 [..]
+[RUNNING] `rustc --crate-name foo src/lib.rs [..]-C linker-plugin-lto[..]
+[FINISHED] [..]
+",
+ )
+ .run();
+ p.cargo("test --release -v")
+ .with_stderr_unordered(
+ "\
+[FRESH] bar v1.0.0
+[COMPILING] foo v0.1.0 [..]
+[RUNNING] `rustc --crate-name foo src/lib.rs [..]-C lto[..]--test[..]
+[FINISHED] [..]
+[RUNNING] `[..]/foo[..]`
+[DOCTEST] foo
+[RUNNING] `rustdoc [..]-C lto[..]
+",
+ )
+ .run();
+
+ p.cargo("build --release -v")
+ .with_stderr(
+ "\
+[FRESH] bar v1.0.0
+[FRESH] foo [..]
+[FINISHED] [..]
+",
+ )
+ .run();
+ p.cargo("test --release -v --no-run -v")
+ .with_stderr(
+ "\
+[FRESH] bar v1.0.0
+[FRESH] foo [..]
+[FINISHED] [..]
+[EXECUTABLE] `[..]/target/release/deps/foo-[..][EXE]`
+",
+ )
+ .run();
+}