1 use cargo::core::compiler::Lto;
2 use cargo_test_support::registry::Package;
3 use cargo_test_support::{basic_manifest, project, Project};
4 use std::process::Output;
5 
6 #[cargo_test]
with_deps()7 fn with_deps() {
8     Package::new("bar", "0.0.1").publish();
9 
10     let p = project()
11         .file(
12             "Cargo.toml",
13             r#"
14                 [package]
15                 name = "test"
16                 version = "0.0.0"
17 
18                 [dependencies]
19                 bar = "*"
20 
21                 [profile.release]
22                 lto = true
23             "#,
24         )
25         .file("src/main.rs", "extern crate bar; fn main() {}")
26         .build();
27     p.cargo("build -v --release")
28         .with_stderr_contains("[..]`rustc[..]--crate-name bar[..]-C linker-plugin-lto[..]`")
29         .with_stderr_contains("[..]`rustc[..]--crate-name test[..]-C lto[..]`")
30         .run();
31 }
32 
33 #[cargo_test]
shared_deps()34 fn shared_deps() {
35     Package::new("bar", "0.0.1").publish();
36 
37     let p = project()
38         .file(
39             "Cargo.toml",
40             r#"
41                 [package]
42                 name = "test"
43                 version = "0.0.0"
44 
45                 [dependencies]
46                 bar = "*"
47 
48                 [build-dependencies]
49                 bar = "*"
50 
51                 [profile.release]
52                 lto = true
53             "#,
54         )
55         .file("build.rs", "extern crate bar; fn main() {}")
56         .file("src/main.rs", "extern crate bar; fn main() {}")
57         .build();
58     p.cargo("build -v --release")
59         .with_stderr_contains("[..]`rustc[..]--crate-name test[..]-C lto[..]`")
60         .run();
61 }
62 
63 #[cargo_test]
build_dep_not_ltod()64 fn build_dep_not_ltod() {
65     Package::new("bar", "0.0.1").publish();
66 
67     let p = project()
68         .file(
69             "Cargo.toml",
70             r#"
71                 [package]
72                 name = "test"
73                 version = "0.0.0"
74 
75                 [build-dependencies]
76                 bar = "*"
77 
78                 [profile.release]
79                 lto = true
80             "#,
81         )
82         .file("build.rs", "extern crate bar; fn main() {}")
83         .file("src/main.rs", "fn main() {}")
84         .build();
85     p.cargo("build -v --release")
86         .with_stderr_contains("[..]`rustc[..]--crate-name bar[..]-C embed-bitcode=no[..]`")
87         .with_stderr_contains("[..]`rustc[..]--crate-name test[..]-C lto[..]`")
88         .run();
89 }
90 
91 #[cargo_test]
complicated()92 fn complicated() {
93     Package::new("dep-shared", "0.0.1")
94         .file("src/lib.rs", "pub fn foo() {}")
95         .publish();
96     Package::new("dep-normal2", "0.0.1")
97         .file("src/lib.rs", "pub fn foo() {}")
98         .publish();
99     Package::new("dep-normal", "0.0.1")
100         .dep("dep-shared", "*")
101         .dep("dep-normal2", "*")
102         .file(
103             "src/lib.rs",
104             "
105                 pub fn foo() {
106                     dep_shared::foo();
107                     dep_normal2::foo();
108                 }
109             ",
110         )
111         .publish();
112     Package::new("dep-build2", "0.0.1")
113         .file("src/lib.rs", "pub fn foo() {}")
114         .publish();
115     Package::new("dep-build", "0.0.1")
116         .dep("dep-shared", "*")
117         .dep("dep-build2", "*")
118         .file(
119             "src/lib.rs",
120             "
121                 pub fn foo() {
122                     dep_shared::foo();
123                     dep_build2::foo();
124                 }
125             ",
126         )
127         .publish();
128     Package::new("dep-proc-macro2", "0.0.1")
129         .file("src/lib.rs", "pub fn foo() {}")
130         .publish();
131     Package::new("dep-proc-macro", "0.0.1")
132         .proc_macro(true)
133         .dep("dep-shared", "*")
134         .dep("dep-proc-macro2", "*")
135         .file(
136             "src/lib.rs",
137             "
138                 extern crate proc_macro;
139                 use proc_macro::TokenStream;
140 
141                 #[proc_macro_attribute]
142                 pub fn foo(_: TokenStream, a: TokenStream) -> TokenStream {
143                     dep_shared::foo();
144                     dep_proc_macro2::foo();
145                     a
146                 }
147             ",
148         )
149         .publish();
150 
151     let p = project()
152         .file(
153             "Cargo.toml",
154             r#"
155                 [package]
156                 name = "test"
157                 version = "0.0.0"
158 
159                 [lib]
160                 crate-type = ['cdylib', 'staticlib']
161 
162                 [dependencies]
163                 dep-normal = "*"
164                 dep-proc-macro = "*"
165 
166                 [build-dependencies]
167                 dep-build = "*"
168 
169                 [profile.release]
170                 lto = true
171 
172                 # force build deps to share an opt-level with the rest of the
173                 # graph so they only get built once.
174                 [profile.release.build-override]
175                 opt-level = 3
176             "#,
177         )
178         .file("build.rs", "fn main() { dep_build::foo() }")
179         .file(
180             "src/bin/foo-bin.rs",
181             "#[dep_proc_macro::foo] fn main() { dep_normal::foo() }",
182         )
183         .file(
184             "src/lib.rs",
185             "#[dep_proc_macro::foo] pub fn foo() { dep_normal::foo() }",
186         )
187         .build();
188     p.cargo("build -v --release")
189         // normal deps and their transitive dependencies do not need object
190         // code, so they should have linker-plugin-lto specified
191         .with_stderr_contains(
192             "[..]`rustc[..]--crate-name dep_normal2 [..]-C linker-plugin-lto[..]`",
193         )
194         .with_stderr_contains("[..]`rustc[..]--crate-name dep_normal [..]-C linker-plugin-lto[..]`")
195         // build dependencies and their transitive deps don't need any bitcode,
196         // so embedding should be turned off
197         .with_stderr_contains("[..]`rustc[..]--crate-name dep_build2 [..]-C embed-bitcode=no[..]`")
198         .with_stderr_contains("[..]`rustc[..]--crate-name dep_build [..]-C embed-bitcode=no[..]`")
199         .with_stderr_contains(
200             "[..]`rustc[..]--crate-name build_script_build [..]-C embed-bitcode=no[..]`",
201         )
202         // proc macro deps are the same as build deps here
203         .with_stderr_contains(
204             "[..]`rustc[..]--crate-name dep_proc_macro2 [..]-C embed-bitcode=no[..]`",
205         )
206         .with_stderr_contains(
207             "[..]`rustc[..]--crate-name dep_proc_macro [..]-C embed-bitcode=no[..]`",
208         )
209         .with_stderr_contains(
210             "[..]`rustc[..]--crate-name foo_bin [..]--crate-type bin[..]-C lto[..]`",
211         )
212         .with_stderr_contains(
213             "[..]`rustc[..]--crate-name test [..]--crate-type cdylib[..]-C lto[..]`",
214         )
215         .with_stderr_contains("[..]`rustc[..]--crate-name dep_shared [..]`")
216         .with_stderr_does_not_contain("[..]--crate-name dep_shared[..]-C lto[..]")
217         .with_stderr_does_not_contain("[..]--crate-name dep_shared[..]-C linker-plugin-lto[..]")
218         .with_stderr_does_not_contain("[..]--crate-name dep_shared[..]-C embed-bitcode[..]")
219         .run();
220 }
221 
222 #[cargo_test]
off_in_manifest_works()223 fn off_in_manifest_works() {
224     Package::new("bar", "0.0.1")
225         .file("src/lib.rs", "pub fn foo() {}")
226         .publish();
227 
228     let p = project()
229         .file(
230             "Cargo.toml",
231             r#"
232                 [package]
233                 name = "test"
234                 version = "0.0.0"
235 
236                 [dependencies]
237                 bar = "*"
238 
239                 [profile.release]
240                 lto = "off"
241             "#,
242         )
243         .file("src/lib.rs", "pub fn foo() {}")
244         .file(
245             "src/main.rs",
246             "fn main() {
247             test::foo();
248             bar::foo();
249         }",
250         )
251         .build();
252     p.cargo("build -v --release")
253         .with_stderr(
254             "\
255 [UPDATING] [..]
256 [DOWNLOADING] [..]
257 [DOWNLOADED] [..]
258 [COMPILING] bar v0.0.1
259 [RUNNING] `rustc --crate-name bar [..]--crate-type lib [..]-C lto=off -C embed-bitcode=no[..]
260 [COMPILING] test [..]
261 [RUNNING] `rustc --crate-name test [..]--crate-type lib [..]-C lto=off -C embed-bitcode=no[..]
262 [RUNNING] `rustc --crate-name test src/main.rs [..]--crate-type bin [..]-C lto=off[..]
263 [FINISHED] [..]
264 ",
265         )
266         .run();
267 }
268 
269 #[cargo_test]
between_builds()270 fn between_builds() {
271     let p = project()
272         .file(
273             "Cargo.toml",
274             r#"
275                 [package]
276                 name = "test"
277                 version = "0.0.0"
278 
279                 [profile.release]
280                 lto = true
281             "#,
282         )
283         .file("src/lib.rs", "pub fn foo() {}")
284         .file("src/main.rs", "fn main() { test::foo() }")
285         .build();
286     p.cargo("build -v --release --lib")
287         .with_stderr(
288             "\
289 [COMPILING] test [..]
290 [RUNNING] `rustc [..]--crate-type lib[..]-C linker-plugin-lto[..]
291 [FINISHED] [..]
292 ",
293         )
294         .run();
295     p.cargo("build -v --release")
296         .with_stderr_contains(
297             "\
298 [COMPILING] test [..]
299 [RUNNING] `rustc [..]--crate-type bin[..]-C lto[..]
300 [FINISHED] [..]
301 ",
302         )
303         .run();
304 }
305 
306 #[cargo_test]
test_all()307 fn test_all() {
308     let p = project()
309         .file(
310             "Cargo.toml",
311             r#"
312                 [package]
313                 name = "foo"
314                 version = "0.0.0"
315 
316                 [profile.release]
317                 lto = true
318             "#,
319         )
320         .file("src/main.rs", "fn main() {}")
321         .file("tests/a.rs", "")
322         .file("tests/b.rs", "")
323         .build();
324     p.cargo("test --release -v")
325         .with_stderr_contains("[RUNNING] `rustc[..]--crate-name foo[..]-C lto[..]")
326         .run();
327 }
328 
329 #[cargo_test]
test_all_and_bench()330 fn test_all_and_bench() {
331     let p = project()
332         .file(
333             "Cargo.toml",
334             r#"
335                 [package]
336                 name = "foo"
337                 version = "0.0.0"
338 
339                 [profile.release]
340                 lto = true
341                 [profile.bench]
342                 lto = true
343             "#,
344         )
345         .file("src/main.rs", "fn main() {}")
346         .file("tests/a.rs", "")
347         .file("tests/b.rs", "")
348         .build();
349     p.cargo("test --release -v")
350         .with_stderr_contains("[RUNNING] `rustc[..]--crate-name a[..]-C lto[..]")
351         .with_stderr_contains("[RUNNING] `rustc[..]--crate-name b[..]-C lto[..]")
352         .with_stderr_contains("[RUNNING] `rustc[..]--crate-name foo[..]-C lto[..]")
353         .run();
354 }
355 
356 /// Basic setup:
357 ///
358 /// foo v0.0.0
359 /// ├── bar v0.0.0
360 /// │   ├── registry v0.0.1
361 /// │   └── registry-shared v0.0.1
362 /// └── registry-shared v0.0.1
363 ///
364 /// Where `bar` will have the given crate types.
project_with_dep(crate_types: &str) -> Project365 fn project_with_dep(crate_types: &str) -> Project {
366     Package::new("registry", "0.0.1")
367         .file("src/lib.rs", r#"pub fn foo() { println!("registry"); }"#)
368         .publish();
369     Package::new("registry-shared", "0.0.1")
370         .file("src/lib.rs", r#"pub fn foo() { println!("shared"); }"#)
371         .publish();
372 
373     project()
374         .file(
375             "Cargo.toml",
376             r#"
377                 [package]
378                 name = "foo"
379                 version = "0.0.0"
380 
381                 [workspace]
382 
383                 [dependencies]
384                 bar = { path = 'bar' }
385                 registry-shared = "*"
386 
387                 [profile.release]
388                 lto = true
389             "#,
390         )
391         .file(
392             "src/main.rs",
393             "
394                 fn main() {
395                     bar::foo();
396                     registry_shared::foo();
397                 }
398             ",
399         )
400         .file(
401             "bar/Cargo.toml",
402             &format!(
403                 r#"
404                     [package]
405                     name = "bar"
406                     version = "0.0.0"
407 
408                     [dependencies]
409                     registry = "*"
410                     registry-shared = "*"
411 
412                     [lib]
413                     crate-type = [{}]
414                 "#,
415                 crate_types
416             ),
417         )
418         .file(
419             "bar/src/lib.rs",
420             r#"
421                 pub fn foo() {
422                     println!("bar");
423                     registry::foo();
424                     registry_shared::foo();
425                 }
426             "#,
427         )
428         .file("tests/a.rs", "")
429         .file("bar/tests/b.rs", "")
430         .build()
431 }
432 
433 /// Helper for checking which LTO behavior is used for a specific crate.
434 ///
435 /// `krate_info` is extra compiler flags used to distinguish this if the same
436 /// crate name is being built multiple times.
verify_lto(output: &Output, krate: &str, krate_info: &str, expected_lto: Lto)437 fn verify_lto(output: &Output, krate: &str, krate_info: &str, expected_lto: Lto) {
438     let stderr = std::str::from_utf8(&output.stderr).unwrap();
439     let mut matches = stderr.lines().filter(|line| {
440         line.contains("Running")
441             && line.contains(&format!("--crate-name {} ", krate))
442             && line.contains(krate_info)
443     });
444     let line = matches.next().unwrap_or_else(|| {
445         panic!(
446             "expected to find crate `{}` info: `{}`, not found in output:\n{}",
447             krate, krate_info, stderr
448         );
449     });
450     if let Some(line2) = matches.next() {
451         panic!(
452             "found multiple lines matching crate `{}` info: `{}`:\nline1:{}\nline2:{}\noutput:\n{}",
453             krate, krate_info, line, line2, stderr
454         );
455     }
456     let actual_lto = if let Some(index) = line.find("-C lto=") {
457         let s = &line[index..];
458         let end = s.find(' ').unwrap();
459         let mode = &line[index..index + end];
460         if mode == "off" {
461             Lto::Off
462         } else {
463             Lto::Run(Some(mode.into()))
464         }
465     } else if line.contains("-C lto") {
466         Lto::Run(None)
467     } else if line.contains("-C linker-plugin-lto") {
468         Lto::OnlyBitcode
469     } else if line.contains("-C embed-bitcode=no") {
470         Lto::OnlyObject
471     } else {
472         Lto::ObjectAndBitcode
473     };
474     assert_eq!(
475         actual_lto, expected_lto,
476         "did not find expected LTO in line: {}",
477         line
478     );
479 }
480 
481 #[cargo_test]
cdylib_and_rlib()482 fn cdylib_and_rlib() {
483     let p = project_with_dep("'cdylib', 'rlib'");
484     let output = p.cargo("build --release -v").exec_with_output().unwrap();
485     // `registry` is ObjectAndBitcode because because it needs Object for the
486     // rlib, and Bitcode for the cdylib (which doesn't support LTO).
487     verify_lto(
488         &output,
489         "registry",
490         "--crate-type lib",
491         Lto::ObjectAndBitcode,
492     );
493     // Same as `registry`
494     verify_lto(
495         &output,
496         "registry_shared",
497         "--crate-type lib",
498         Lto::ObjectAndBitcode,
499     );
500     // Same as `registry`
501     verify_lto(
502         &output,
503         "bar",
504         "--crate-type cdylib --crate-type rlib",
505         Lto::ObjectAndBitcode,
506     );
507     verify_lto(&output, "foo", "--crate-type bin", Lto::Run(None));
508     p.cargo("test --release -v")
509         .with_stderr_unordered(
510             "\
511 [FRESH] registry v0.0.1
512 [FRESH] registry-shared v0.0.1
513 [FRESH] bar v0.0.0 [..]
514 [COMPILING] foo [..]
515 [RUNNING] `rustc --crate-name foo [..]-C lto [..]--test[..]
516 [RUNNING] `rustc --crate-name a [..]-C lto [..]--test[..]
517 [FINISHED] [..]
518 [RUNNING] [..]
519 [RUNNING] [..]
520 ",
521         )
522         .run();
523     p.cargo("build --release -v --manifest-path bar/Cargo.toml")
524         .with_stderr_unordered(
525             "\
526 [FRESH] registry-shared v0.0.1
527 [FRESH] registry v0.0.1
528 [FRESH] bar v0.0.0 [..]
529 [FINISHED] [..]
530 ",
531         )
532         .run();
533     p.cargo("test --release -v --manifest-path bar/Cargo.toml")
534         .with_stderr_unordered(
535             "\
536 [FRESH] registry-shared v0.0.1
537 [FRESH] registry v0.0.1
538 [COMPILING] bar [..]
539 [RUNNING] `rustc --crate-name bar [..]-C lto[..]--test[..]
540 [RUNNING] `rustc --crate-name b [..]-C lto[..]--test[..]
541 [FINISHED] [..]
542 [RUNNING] [..]target/release/deps/bar-[..]
543 [RUNNING] [..]target/release/deps/b-[..]
544 [DOCTEST] bar
545 [RUNNING] `rustdoc --crate-type cdylib --crate-type rlib --crate-name bar --test [..]-C lto[..]
546 ",
547         )
548         .run();
549 }
550 
551 #[cargo_test]
dylib()552 fn dylib() {
553     let p = project_with_dep("'dylib'");
554     let output = p.cargo("build --release -v").exec_with_output().unwrap();
555     // `registry` is OnlyObject because rustc doesn't support LTO with dylibs.
556     verify_lto(&output, "registry", "--crate-type lib", Lto::OnlyObject);
557     // `registry_shared` is both because it is needed by both bar (Object) and
558     // foo (Bitcode for LTO).
559     verify_lto(
560         &output,
561         "registry_shared",
562         "--crate-type lib",
563         Lto::ObjectAndBitcode,
564     );
565     // `bar` is OnlyObject because rustc doesn't support LTO with dylibs.
566     verify_lto(&output, "bar", "--crate-type dylib", Lto::OnlyObject);
567     // `foo` is LTO because it is a binary, and the profile specifies `lto=true`.
568     verify_lto(&output, "foo", "--crate-type bin", Lto::Run(None));
569     // `cargo test` should not rebuild dependencies. It builds the test
570     // executables with `lto=true` because the tests are built with the
571     // `--release` flag.
572     p.cargo("test --release -v")
573         .with_stderr_unordered(
574             "\
575 [FRESH] registry v0.0.1
576 [FRESH] registry-shared v0.0.1
577 [FRESH] bar v0.0.0 [..]
578 [COMPILING] foo [..]
579 [RUNNING] `rustc --crate-name foo [..]-C lto [..]--test[..]
580 [RUNNING] `rustc --crate-name a [..]-C lto [..]--test[..]
581 [FINISHED] [..]
582 [RUNNING] [..]
583 [RUNNING] [..]
584 ",
585         )
586         .run();
587     // Building just `bar` causes `registry-shared` to get rebuilt because it
588     // switches to OnlyObject because it is now only being used with a dylib
589     // which does not support LTO.
590     //
591     // `bar` gets rebuilt because `registry_shared` got rebuilt.
592     p.cargo("build --release -v --manifest-path bar/Cargo.toml")
593         .with_stderr_unordered(
594             "\
595 [COMPILING] registry-shared v0.0.1
596 [FRESH] registry v0.0.1
597 [RUNNING] `rustc --crate-name registry_shared [..]-C embed-bitcode=no[..]
598 [COMPILING] bar [..]
599 [RUNNING] `rustc --crate-name bar [..]--crate-type dylib [..]-C embed-bitcode=no[..]
600 [FINISHED] [..]
601 ",
602         )
603         .run();
604     // Testing just `bar` causes `registry` to get rebuilt because it switches
605     // to needing both Object (for the `bar` dylib) and Bitcode (for the test
606     // built with LTO).
607     //
608     // `bar` the dylib gets rebuilt because `registry` got rebuilt.
609     p.cargo("test --release -v --manifest-path bar/Cargo.toml")
610         .with_stderr_unordered(
611             "\
612 [FRESH] registry-shared v0.0.1
613 [COMPILING] registry v0.0.1
614 [RUNNING] `rustc --crate-name registry [..]
615 [COMPILING] bar [..]
616 [RUNNING] `rustc --crate-name bar [..]--crate-type dylib [..]-C embed-bitcode=no[..]
617 [RUNNING] `rustc --crate-name bar [..]-C lto [..]--test[..]
618 [RUNNING] `rustc --crate-name b [..]-C lto [..]--test[..]
619 [FINISHED] [..]
620 [RUNNING] [..]
621 [RUNNING] [..]
622 ",
623         )
624         .run();
625 }
626 
627 #[cargo_test]
test_profile()628 fn test_profile() {
629     Package::new("bar", "0.0.1")
630         .file("src/lib.rs", "pub fn foo() -> i32 { 123 } ")
631         .publish();
632 
633     let p = project()
634         .file(
635             "Cargo.toml",
636             r#"
637                 [package]
638                 name = "foo"
639                 version = "0.1.0"
640                 edition = "2018"
641 
642                 [profile.test]
643                 lto = 'thin'
644 
645                 [dependencies]
646                 bar = "*"
647             "#,
648         )
649         .file(
650             "src/lib.rs",
651             r#"
652                 #[test]
653                 fn t1() {
654                     assert_eq!(123, bar::foo());
655                 }
656             "#,
657         )
658         .build();
659 
660     p.cargo("test -v")
661         // unordered because the two `foo` builds start in parallel
662         .with_stderr_unordered("\
663 [UPDATING] [..]
664 [DOWNLOADING] [..]
665 [DOWNLOADED] [..]
666 [COMPILING] bar v0.0.1
667 [RUNNING] `rustc --crate-name bar [..]crate-type lib[..]
668 [COMPILING] foo [..]
669 [RUNNING] `rustc --crate-name foo [..]--crate-type lib --emit=dep-info,metadata,link -C linker-plugin-lto[..]
670 [RUNNING] `rustc --crate-name foo [..]--emit=dep-info,link -C lto=thin [..]--test[..]
671 [FINISHED] [..]
672 [RUNNING] [..]
673 [DOCTEST] foo
674 [RUNNING] `rustdoc [..]
675 ")
676         .run();
677 }
678 
679 #[cargo_test]
doctest()680 fn doctest() {
681     let p = project()
682         .file(
683             "Cargo.toml",
684             r#"
685                 [package]
686                 name = "foo"
687                 version = "0.1.0"
688                 edition = "2018"
689 
690                 [profile.release]
691                 lto = true
692 
693                 [dependencies]
694                 bar = { path = "bar" }
695             "#,
696         )
697         .file(
698             "src/lib.rs",
699             r#"
700                 /// Foo!
701                 ///
702                 /// ```
703                 /// foo::foo();
704                 /// ```
705                 pub fn foo() { bar::bar(); }
706             "#,
707         )
708         .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
709         .file(
710             "bar/src/lib.rs",
711             r#"
712                 pub fn bar() { println!("hi!"); }
713             "#,
714         )
715         .build();
716 
717     p.cargo("test --doc --release -v")
718         .with_stderr_contains("[..]`rustc --crate-name bar[..]-C linker-plugin-lto[..]")
719         .with_stderr_contains("[..]`rustc --crate-name foo[..]-C linker-plugin-lto[..]")
720         // embed-bitcode should be harmless here
721         .with_stderr_contains("[..]`rustdoc [..]-C lto[..]")
722         .run();
723 
724     // Try with bench profile.
725     p.cargo("test --doc --release -v")
726         .env("CARGO_PROFILE_BENCH_LTO", "true")
727         .with_stderr_unordered(
728             "\
729 [FRESH] bar v0.1.0 [..]
730 [FRESH] foo v0.1.0 [..]
731 [FINISHED] release [..]
732 [DOCTEST] foo
733 [RUNNING] `rustdoc [..]-C lto[..]
734 ",
735         )
736         .run();
737 }
738 
739 #[cargo_test]
dylib_rlib_bin()740 fn dylib_rlib_bin() {
741     // dylib+rlib linked with a binary
742     let p = project()
743         .file(
744             "Cargo.toml",
745             r#"
746                 [package]
747                 name = "foo"
748                 version = "0.1.0"
749 
750                 [lib]
751                 crate-type = ["dylib", "rlib"]
752 
753                 [profile.release]
754                 lto = true
755             "#,
756         )
757         .file("src/lib.rs", "pub fn foo() { println!(\"hi!\"); }")
758         .file("src/bin/ferret.rs", "fn main() { foo::foo(); }")
759         .build();
760 
761     let output = p.cargo("build --release -v").exec_with_output().unwrap();
762     verify_lto(
763         &output,
764         "foo",
765         "--crate-type dylib --crate-type rlib",
766         Lto::ObjectAndBitcode,
767     );
768     verify_lto(&output, "ferret", "--crate-type bin", Lto::Run(None));
769 }
770 
771 #[cargo_test]
fresh_swapping_commands()772 fn fresh_swapping_commands() {
773     // In some rare cases, different commands end up building dependencies
774     // with different LTO settings. This checks that it doesn't cause the
775     // cache to thrash in that scenario.
776     Package::new("bar", "1.0.0").publish();
777 
778     let p = project()
779         .file(
780             "Cargo.toml",
781             r#"
782                 [package]
783                 name = "foo"
784                 version = "0.1.0"
785 
786                 [dependencies]
787                 bar = "1.0"
788 
789                 [profile.release]
790                 lto = true
791             "#,
792         )
793         .file("src/lib.rs", "pub fn foo() { println!(\"hi!\"); }")
794         .build();
795 
796     p.cargo("build --release -v")
797         .with_stderr(
798             "\
799 [UPDATING] [..]
800 [DOWNLOADING] crates ...
801 [DOWNLOADED] bar v1.0.0 [..]
802 [COMPILING] bar v1.0.0
803 [RUNNING] `rustc --crate-name bar [..]-C linker-plugin-lto[..]
804 [COMPILING] foo v0.1.0 [..]
805 [RUNNING] `rustc --crate-name foo src/lib.rs [..]-C linker-plugin-lto[..]
806 [FINISHED] [..]
807 ",
808         )
809         .run();
810     p.cargo("test --release -v")
811         .with_stderr_unordered(
812             "\
813 [FRESH] bar v1.0.0
814 [COMPILING] foo v0.1.0 [..]
815 [RUNNING] `rustc --crate-name foo src/lib.rs [..]-C lto[..]--test[..]
816 [FINISHED] [..]
817 [RUNNING] `[..]/foo[..]`
818 [DOCTEST] foo
819 [RUNNING] `rustdoc [..]-C lto[..]
820 ",
821         )
822         .run();
823 
824     p.cargo("build --release -v")
825         .with_stderr(
826             "\
827 [FRESH] bar v1.0.0
828 [FRESH] foo [..]
829 [FINISHED] [..]
830 ",
831         )
832         .run();
833     p.cargo("test --release -v --no-run -v")
834         .with_stderr(
835             "\
836 [FRESH] bar v1.0.0
837 [FRESH] foo [..]
838 [FINISHED] [..]
839 ",
840         )
841         .run();
842 }
843