@@ -849,29 +849,79 @@ fn wait(
849849#[ doc( inline) ]
850850pub use crate :: cargo_bin;
851851
852- /// Look up the path to a cargo-built binary within an integration test.
852+ /// Look up the path to a cargo-built binary within an integration test
853853///
854- /// **NOTE:** Prefer [`cargo_bin!`] as this makes assumptions about cargo
854+ /// Cargo support:
855+ /// - `>1.94`: works
856+ /// - `>=1.91,<=1.93`: works with default `build-dir`
857+ /// - `<=1.92`: works
858+ ///
859+ /// # Panic
860+ ///
861+ /// Panics if no binary is found
855862pub fn cargo_bin ( name : & str ) -> std:: path:: PathBuf {
856- let env_var = format ! ( "CARGO_BIN_EXE_{name}" ) ;
863+ cargo_bin_opt ( name) . unwrap_or_else ( || missing_cargo_bin ( name) )
864+ }
865+
866+ /// Look up the path to a cargo-built binary within an integration test
867+ ///
868+ /// Returns `None` if the binary doesn't exist
869+ ///
870+ /// Cargo support:
871+ /// - `>1.94`: works
872+ /// - `>=1.91,<=1.93`: works with default `build-dir`
873+ /// - `<=1.92`: works
874+ pub fn cargo_bin_opt ( name : & str ) -> Option < std:: path:: PathBuf > {
875+ let env_var = format ! ( "{CARGO_BIN_EXE_}{name}" ) ;
857876 std:: env:: var_os ( env_var)
858877 . map ( |p| p. into ( ) )
859- . unwrap_or_else ( || target_dir ( ) . join ( format ! ( "{}{}" , name, std:: env:: consts:: EXE_SUFFIX ) ) )
878+ . or_else ( || legacy_cargo_bin ( name) )
879+ }
880+
881+ const CARGO_BIN_EXE_ : & str = "CARGO_BIN_EXE_" ;
882+
883+ fn missing_cargo_bin ( name : & str ) -> ! {
884+ let possible_names: Vec < _ > = std:: env:: vars_os ( )
885+ . filter_map ( |( k, _) | k. into_string ( ) . ok ( ) )
886+ . filter_map ( |k| k. strip_prefix ( CARGO_BIN_EXE_ ) . map ( |s| s. to_owned ( ) ) )
887+ . collect ( ) ;
888+ if possible_names. is_empty ( ) {
889+ panic ! ( "`CARGO_BIN_EXE_{name}` is unset
890+ help: if this is running within a unit test, move it to an integration test to gain access to `CARGO_BIN_EXE_{name}`" )
891+ } else {
892+ let mut names = String :: new ( ) ;
893+ for ( i, name) in possible_names. iter ( ) . enumerate ( ) {
894+ use std:: fmt:: Write as _;
895+ if i != 0 {
896+ let _ = write ! ( & mut names, ", " ) ;
897+ }
898+ let _ = write ! ( & mut names, "\" {name}\" " ) ;
899+ }
900+ panic ! (
901+ "`CARGO_BIN_EXE_{name}` is unset
902+ help: available binary names are {names}"
903+ )
904+ }
905+ }
906+
907+ fn legacy_cargo_bin ( name : & str ) -> Option < std:: path:: PathBuf > {
908+ let target_dir = target_dir ( ) ?;
909+ let bin_path = target_dir. join ( format ! ( "{}{}" , name, std:: env:: consts:: EXE_SUFFIX ) ) ;
910+ if !bin_path. exists ( ) {
911+ return None ;
912+ }
913+ Some ( bin_path)
860914}
861915
862916// Adapted from
863917// https://github.com/rust-lang/cargo/blob/485670b3983b52289a2f353d589c57fae2f60f82/tests/testsuite/support/mod.rs#L507
864- fn target_dir ( ) -> std:: path:: PathBuf {
865- std:: env:: current_exe ( )
866- . ok ( )
867- . map ( |mut path| {
868- path. pop ( ) ;
869- if path. ends_with ( "deps" ) {
870- path. pop ( ) ;
871- }
872- path
873- } )
874- . unwrap ( )
918+ fn target_dir ( ) -> Option < std:: path:: PathBuf > {
919+ let mut path = std:: env:: current_exe ( ) . ok ( ) ?;
920+ let _test_bin_name = path. pop ( ) ;
921+ if path. ends_with ( "deps" ) {
922+ let _deps = path. pop ( ) ;
923+ }
924+ Some ( path)
875925}
876926
877927#[ cfg( feature = "examples" ) ]
@@ -1009,3 +1059,10 @@ pub(crate) mod examples {
10091059 target. crate_types == [ "bin" ] && target. kind == [ "example" ]
10101060 }
10111061}
1062+
1063+ #[ test]
1064+ #[ should_panic = "`CARGO_BIN_EXE_non-existent` is unset
1065+ help: if this is running within a unit test, move it to an integration test to gain access to `CARGO_BIN_EXE_non-existent`" ]
1066+ fn cargo_bin_in_unit_test ( ) {
1067+ cargo_bin ( "non-existent" ) ;
1068+ }
0 commit comments