diff --git a/apache-maven/src/assembly/maven/bin/mvn b/apache-maven/src/assembly/maven/bin/mvn index fe04dacb0bcb..6cfcf37deb35 100755 --- a/apache-maven/src/assembly/maven/bin/mvn +++ b/apache-maven/src/assembly/maven/bin/mvn @@ -355,14 +355,11 @@ find_file_argument_basedir() { elif [ -f "${arg}" ]; then basedir=`dirname "${arg}"` basedir=`cd "$basedir" && pwd -P` - if [ ! -d "$basedir" ]; then - echo "Directory $basedir extracted from the -f/--file command-line argument ${arg} does not exist" >&2 - exit 1 - fi - else - echo "POM file ${arg} specified with the -f/--file command line argument does not exist" >&2 - exit 1 fi + # If the argument does not exist (e.g. a Cygwin/MSYS path that will be + # converted later, or a genuinely missing file), fall back to the current + # directory. The Java layer validates the path and produces the proper + # error message when the file is truly absent. break fi if [ "$arg" = "-f" -o "$arg" = "--file" ]; then diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java index 9a740753bb0f..c10c300d046c 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java @@ -137,6 +137,12 @@ public InvokerRequest parseInvocation(ParserRequest parserRequest) { // top/root try { context.topDirectory = getTopDirectory(context); + } catch (IllegalArgumentException e) { + // User-facing error (e.g. -f points to a non-existent file): report the message directly, + // without wrapping it in an internal "Error determining top directory" prefix. + context.parsingFailed = true; + context.topDirectory = context.cwd; + parserRequest.logger().error(e.getMessage()); } catch (Exception e) { context.parsingFailed = true; context.topDirectory = context.cwd; @@ -144,6 +150,10 @@ public InvokerRequest parseInvocation(ParserRequest parserRequest) { } try { context.rootDirectory = getRootDirectory(context); + } catch (IllegalArgumentException e) { + context.parsingFailed = true; + context.rootDirectory = context.cwd; + parserRequest.logger().error(e.getMessage()); } catch (Exception e) { context.parsingFailed = true; context.rootDirectory = context.cwd; diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java index e6372ccfd818..03357c5f09bb 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java @@ -36,6 +36,7 @@ import org.apache.maven.api.Constants; import org.apache.maven.api.MonotonicClock; import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.cli.InvokerException; import org.apache.maven.api.cli.InvokerRequest; import org.apache.maven.api.cli.Logger; import org.apache.maven.api.cli.mvn.MavenOptions; @@ -132,6 +133,15 @@ protected void postCommands(MavenContext context) throws Exception { } else if (context.options().strictChecksums().orElse(false)) { logger.info("Enabling strict checksum verification on all artifact downloads."); } + + // If no goals/phases were specified and there is no POM in the current directory, + // print usage (like `mvn -h`) and exit successfully instead of failing with + // "No goals have been specified for this build." + // When a POM is present the default goal (if any) may apply, so let Maven proceed normally. + if (context.options().goals().orElse(List.of()).isEmpty() && determinePom(context, context.lookup) == null) { + context.options().displayHelp(context.invokerRequest.parserRequest(), determineWriter(context)); + throw new InvokerException.ExitException(0); + } } protected void toolchains(MavenContext context, MavenExecutionRequest request) throws Exception {