11use std:: {
2- env, fs,
2+ env,
3+ ffi:: OsString ,
4+ fs,
35 path:: { Path , PathBuf } ,
46 process:: Stdio ,
57} ;
@@ -25,6 +27,8 @@ const ANDROID_CMDLINE_TOOLS_ARCHIVE: &str = "commandlinetools-mac-14742923_lates
2527const MANAGED_JAVA_FEATURE_VERSION : u16 = 21 ;
2628const SDKMANAGER_LICENSE_INPUT_REPEATS : usize = 200 ;
2729
30+ type ToolEnv = ( & ' static str , OsString ) ;
31+
2832#[ derive( Debug , Clone ) ]
2933/// Android Emulator平台Provider。
3034///
@@ -745,24 +749,33 @@ fn parse_java_major_version(version: &str) -> Option<u16> {
745749}
746750
747751/// Android SDK工具运行时需要的一组环境变量。
748- fn android_tool_envs < ' a > (
749- sdk_root : & ' a Path ,
750- avd_root : & ' a Path ,
751- java_runtime : & ' a JavaRuntime ,
752- ) -> Vec < ( & ' static str , & ' a Path ) > {
752+ fn android_tool_envs ( sdk_root : & Path , avd_root : & Path , java_runtime : & JavaRuntime ) -> Vec < ToolEnv > {
753753 let mut envs = vec ! [
754- ( "ANDROID_SDK_ROOT" , sdk_root) ,
755- ( "ANDROID_HOME" , sdk_root) ,
756- ( "ANDROID_AVD_HOME" , avd_root) ,
754+ ( "ANDROID_SDK_ROOT" , sdk_root. as_os_str ( ) . to_os_string ( ) ) ,
755+ ( "ANDROID_HOME" , sdk_root. as_os_str ( ) . to_os_string ( ) ) ,
756+ ( "ANDROID_AVD_HOME" , avd_root. as_os_str ( ) . to_os_string ( ) ) ,
757757 ] ;
758758
759759 if let Some ( java_home) = java_runtime. managed_home ( ) {
760- envs. push ( ( "JAVA_HOME" , java_home) ) ;
760+ let java_binary = JavaRuntime :: managed_java_binary ( java_home) ;
761+ envs. push ( ( "JAVA_HOME" , java_home. as_os_str ( ) . to_os_string ( ) ) ) ;
762+ envs. push ( ( "JAVACMD" , java_binary. as_os_str ( ) . to_os_string ( ) ) ) ;
763+ envs. push ( ( "PATH" , prepend_to_path_env ( & java_home. join ( "bin" ) ) ) ) ;
761764 }
762765
763766 envs
764767}
765768
769+ /// 把托管JRE的`bin`放到PATH最前面,兜底兼容只查找`java`的SDK脚本。
770+ fn prepend_to_path_env ( path : & Path ) -> OsString {
771+ let mut paths = vec ! [ path. to_path_buf( ) ] ;
772+ if let Some ( current_path) = env:: var_os ( "PATH" ) {
773+ paths. extend ( env:: split_paths ( & current_path) ) ;
774+ }
775+
776+ env:: join_paths ( paths) . unwrap_or_else ( |_| path. as_os_str ( ) . to_os_string ( ) )
777+ }
778+
766779/// 确保Android SDK工具可用的Java运行时存在。
767780///
768781/// 优先复用系统Java;如果系统没有Java,就下载轻量JRE到Simdock
@@ -902,7 +915,11 @@ async fn ensure_android_java_runtime(
902915 let _ = fs:: remove_dir_all ( & extract_dir) ;
903916
904917 let Some ( ( runtime, summary) ) = probe_managed_java ( java_root) . await else {
905- bail ! ( "Managed Java runtime was extracted but java -version failed" ) ;
918+ let java_binary = JavaRuntime :: managed_java_binary ( java_root) ;
919+ bail ! (
920+ "Managed Java runtime was extracted but {} could not run. Remove the managed Java runtime from Manage installed content and run one-click install again. If this is a company-managed Mac, ask IT to allow executing this binary." ,
921+ java_binary. display( )
922+ ) ;
906923 } ;
907924 emit_log (
908925 sender,
@@ -1095,7 +1112,10 @@ async fn accept_android_licenses(
10951112 if probe. success {
10961113 Ok ( ( ) )
10971114 } else {
1098- bail ! ( "Android SDK license acceptance failed: {}" , probe. summary( ) )
1115+ bail ! (
1116+ "Android SDK license acceptance failed: {}" ,
1117+ android_tool_failure_summary( "sdkmanager" , & probe, Some ( java_runtime) )
1118+ )
10991119 }
11001120}
11011121
@@ -1127,7 +1147,7 @@ async fn install_android_packages(
11271147 } else {
11281148 bail ! (
11291149 "sdkmanager package installation failed: {}" ,
1130- probe. summary ( )
1150+ android_tool_failure_summary ( "sdkmanager" , & probe, Some ( java_runtime ) )
11311151 )
11321152 }
11331153}
@@ -1225,13 +1245,13 @@ async fn ensure_android_avd(
12251245
12261246 bail ! (
12271247 "avdmanager failed to create Android virtual device: {}" ,
1228- fallback_probe. summary ( )
1248+ android_tool_failure_summary ( "avdmanager" , & fallback_probe, Some ( java_runtime ) )
12291249 ) ;
12301250 }
12311251
12321252 bail ! (
12331253 "avdmanager failed to create Android virtual device: {}" ,
1234- create_probe. summary ( )
1254+ android_tool_failure_summary ( "avdmanager" , & create_probe, Some ( java_runtime ) )
12351255 )
12361256}
12371257
@@ -1253,7 +1273,7 @@ async fn tool_check(
12531273 name : & str ,
12541274 path : Option < PathBuf > ,
12551275 args : & [ & str ] ,
1256- envs : & [ ( & str , & Path ) ] ,
1276+ envs : & [ ToolEnv ] ,
12571277) -> DoctorCheck {
12581278 match path {
12591279 Some ( path) => {
@@ -1268,7 +1288,7 @@ async fn tool_check(
12681288 format ! (
12691289 "{name} probe failed at {}: {}" ,
12701290 path. display( ) ,
1271- probe . summary ( )
1291+ android_tool_failure_summary ( name , & probe , None )
12721292 )
12731293 } ;
12741294
@@ -1286,6 +1306,41 @@ async fn tool_check(
12861306 }
12871307}
12881308
1309+ /// 把Android工具里常见的Java定位失败转换成可执行的修复说明。
1310+ fn android_tool_failure_summary (
1311+ tool_name : & str ,
1312+ probe : & CommandProbe ,
1313+ java_runtime : Option < & JavaRuntime > ,
1314+ ) -> String {
1315+ let summary = probe. summary ( ) ;
1316+ if !is_android_java_runtime_error ( probe) {
1317+ return summary;
1318+ }
1319+
1320+ let runtime_hint = match java_runtime {
1321+ Some ( JavaRuntime :: Managed { java_home } ) => format ! (
1322+ "Simdock tried to use managed Java at {}. Remove the managed Java runtime from Manage installed content and run one-click install again. If this is a company-managed Mac, ask IT to allow executing {}." ,
1323+ java_home. display( ) ,
1324+ JavaRuntime :: managed_java_binary( java_home) . display( )
1325+ ) ,
1326+ _ => "Simdock could not use a Java runtime for Android SDK tools. Run one-click install again so Simdock can provision managed Java. If this is a company-managed Mac, ask IT to allow Simdock's managed Java runtime under ~/Library/Application Support/com.simdock.Simdock/java-runtime." . to_string ( ) ,
1327+ } ;
1328+
1329+ format ! (
1330+ "{tool_name} could not locate or execute Java. {runtime_hint} Original output: {summary}"
1331+ )
1332+ }
1333+
1334+ /// 识别macOS和Android命令行工具输出中的Java缺失/不可执行错误。
1335+ fn is_android_java_runtime_error ( probe : & CommandProbe ) -> bool {
1336+ let output = format ! ( "{}\n {}" , probe. stdout, probe. stderr) . to_lowercase ( ) ;
1337+ output. contains ( "unable to locate a java runtime" )
1338+ || output. contains ( "no java runtime present" )
1339+ || output. contains ( "could not find java" )
1340+ || output. contains ( "could not find a valid java" )
1341+ || output. contains ( "java_home is not defined correctly" )
1342+ }
1343+
12891344/// 运行PATH中的命令并收集输出。
12901345async fn run_command ( program : & str , args : & [ & str ] ) -> CommandProbe {
12911346 finish_probe ( {
@@ -1310,13 +1365,13 @@ async fn run_path_command(program: &Path, args: &[&str]) -> CommandProbe {
13101365async fn run_path_command_with_env (
13111366 program : & Path ,
13121367 args : & [ & str ] ,
1313- envs : & [ ( & str , & Path ) ] ,
1368+ envs : & [ ToolEnv ] ,
13141369) -> CommandProbe {
13151370 finish_probe ( {
13161371 let mut command = Command :: new ( program) ;
13171372 command. args ( args) ;
13181373 for ( key, value) in envs {
1319- command. env ( key, value) ;
1374+ command. env ( * key, value) ;
13201375 }
13211376 command
13221377 } )
@@ -1327,15 +1382,15 @@ async fn run_path_command_with_env(
13271382async fn run_command_streamed (
13281383 program : & str ,
13291384 args : & [ String ] ,
1330- envs : & [ ( & str , & Path ) ] ,
1385+ envs : & [ ToolEnv ] ,
13311386 task_id : & str ,
13321387 sender : Option < & TaskSender > ,
13331388) -> CommandProbe {
13341389 let mut command = Command :: new ( program) ;
13351390 command. args ( args) ;
13361391
13371392 for ( key, value) in envs {
1338- command. env ( key, value) ;
1393+ command. env ( * key, value) ;
13391394 }
13401395
13411396 finish_probe_streamed ( command, program, args, None , task_id, sender) . await
@@ -1345,7 +1400,7 @@ async fn run_command_streamed(
13451400async fn run_path_command_streamed_with_input (
13461401 program : & Path ,
13471402 args : & [ String ] ,
1348- envs : & [ ( & str , & Path ) ] ,
1403+ envs : & [ ToolEnv ] ,
13491404 input : Option < String > ,
13501405 task_id : & str ,
13511406 sender : Option < & TaskSender > ,
@@ -1354,7 +1409,7 @@ async fn run_path_command_streamed_with_input(
13541409 command. args ( args) ;
13551410
13561411 for ( key, value) in envs {
1357- command. env ( key, value) ;
1412+ command. env ( * key, value) ;
13581413 }
13591414
13601415 let program_label = program. display ( ) . to_string ( ) ;
0 commit comments