11/*
2- * Copyright (c) 2017, 2024 , Oracle and/or its affiliates.
2+ * Copyright (c) 2017, 2025 , Oracle and/or its affiliates.
33 * Copyright (c) 2013, Regents of the University of California
44 *
55 * All rights reserved.
4747import java .util .Set ;
4848import java .util .UUID ;
4949import java .util .function .Function ;
50+ import java .util .function .Predicate ;
5051
5152import org .graalvm .launcher .AbstractLanguageLauncher ;
5253import org .graalvm .nativeimage .ImageInfo ;
@@ -530,11 +531,17 @@ private String getLauncherExecName() {
530531 * {@link #getEnv(String)} is used to retrieve $PATH.
531532 * @return The absolute path to the program or {@code null}.
532533 */
533- private String calculateProgramFullPath (String program , Function <Path , Boolean > isExecutable , String envPath ) {
534+ private String calculateProgramFullPath (String program , Predicate <Path > isExecutable , String envPath ) {
534535 Path programPath = Paths .get (program );
535536
536537 // If this is an absolute path, we are already fine.
537538 if (programPath .isAbsolute ()) {
539+ if (IS_WINDOWS ) {
540+ String resolvedProgramNameWithExtension = getProgramNameWithExtension (isExecutable , program );
541+ if (resolvedProgramNameWithExtension != null ) {
542+ return resolvedProgramNameWithExtension ;
543+ }
544+ }
538545 return program ;
539546 }
540547
@@ -553,26 +560,15 @@ private String calculateProgramFullPath(String program, Function<Path, Boolean>
553560 i = path .indexOf (File .pathSeparatorChar , previous );
554561 int end = i == -1 ? path .length () : i ;
555562 Path resolvedProgramName = Paths .get (path .substring (previous , end )).resolve (programPath );
556- if (isExecutable .apply (resolvedProgramName )) {
563+ if (isExecutable .test (resolvedProgramName )) {
557564 return resolvedProgramName .toString ();
558565 }
559566
560567 // On windows, the program name may be without the extension
561568 if (IS_WINDOWS ) {
562- String pathExtEnvvar = getEnv ("PATHEXT" );
563- if (pathExtEnvvar != null ) {
564- // default extensions are defined
565- String resolvedStr = resolvedProgramName .toString ();
566- if (resolvedStr .length () <= 3 || resolvedStr .charAt (resolvedStr .length () - 4 ) != '.' ) {
567- // program has no file extension
568- String [] pathExts = pathExtEnvvar .toLowerCase ().split (";" );
569- for (String pathExt : pathExts ) {
570- resolvedProgramName = Path .of (resolvedStr + pathExt );
571- if (isExecutable .apply (resolvedProgramName )) {
572- return resolvedProgramName .toString ();
573- }
574- }
575- }
569+ String resolvedProgramNameWithExtension = getProgramNameWithExtension (isExecutable , resolvedProgramName .toString ());
570+ if (resolvedProgramNameWithExtension != null ) {
571+ return resolvedProgramNameWithExtension ;
576572 }
577573 }
578574
@@ -598,6 +594,28 @@ private String calculateProgramFullPath(String program, Function<Path, Boolean>
598594 return programPath .toAbsolutePath ().toString ();
599595 }
600596
597+ private static String getProgramNameWithExtension (Predicate <Path > isExecutable , String programStr ) {
598+ if (isExecutable .test (Path .of (programStr ))) {
599+ return programStr ;
600+ }
601+ if (programStr .length () <= 3 || programStr .charAt (programStr .length () - 4 ) != '.' ) {
602+ // program has no file extension
603+ Path programNameWithExtension = null ;
604+ String pathExtEnvvar = getEnv ("PATHEXT" );
605+ if (pathExtEnvvar != null ) {
606+ // default extensions are defined
607+ String [] pathExts = pathExtEnvvar .toLowerCase ().split (";" );
608+ for (String pathExt : pathExts ) {
609+ programNameWithExtension = Path .of (programStr + pathExt );
610+ if (isExecutable .test (programNameWithExtension )) {
611+ return programNameWithExtension .toString ();
612+ }
613+ }
614+ }
615+ }
616+ return null ;
617+ }
618+
601619 private String [] getExecutableList () {
602620 String launcherExecName = getLauncherExecName ();
603621 if (launcherExecName != null ) {
0 commit comments