Skip to content

Print usage when no goals and no POM are specified - #13269

Open
gnodet wants to merge 3 commits into
apache:masterfrom
gnodet:fix/mng-print-usage-no-args
Open

gnodet wants to merge 3 commits into
apache:masterfrom
gnodet:fix/mng-print-usage-no-args

Conversation

@gnodet

@gnodet gnodet commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Problem

When a user types mvn without any arguments and there is no pom.xml in the current directory, Maven currently fails with:

[ERROR] No goals have been specified for this build. You must specify a valid lifecycle phase or a goal...
[ERROR] BUILD FAILURE

This is surprising and unhelpful — especially for new users. Tools like java and javac print usage in this situation.

The discussion on the dev list ("Print usage when no arguments are given" Sep 2026) confirmed broad agreement that the current behavior should be improved, with the key constraint that default goals must still work.

Additionally, when the -f/--file option points to a non-existent file, Maven previously printed the same error message twice in different formats:

POM file X does not exist
[ERROR] Error executing Maven.
[ERROR] Error determining top directory
[ERROR] Caused by: POM file X ... does not exist

Solution

1. Print usage when no goals and no POM are present

Override postCommands() in MavenInvoker to detect the following narrow condition:

  1. No goals or lifecycle phases were given on the command line.
  2. No POM file is present in the current directory (or via -f).

When both are true, print the same help output as mvn -h and exit with code 0.

If a POM is found, Maven proceeds as before: the default goal (if any) is executed, or NoGoalSpecifiedException is reported as today. Existing behavior for all normal project builds is unchanged.

2. Clean up the -f missing-file error message

When getTopDirectory() throws an IllegalArgumentException because the -f/--file argument points to a non-existent path, the exception message is already self-explanatory ("POM file X does not exist"). It is now logged directly instead of being wrapped in the opaque "Error determining top directory" prefix and then repeated as a cause chain. The output is now:

[ERROR] Error executing Maven.
[ERROR] POM file X specified with the -f/--file command line argument does not exist

Why this approach

  • No shell script changes needed — shell-script patching would break default goals and require identical changes to mvn.cmd, mvnDebug, etc.
  • No full model resolution — we reuse determinePom() which is a cheap file-existence check via ModelProcessor.locateExistingPom(). No parent POM downloads, no DI warmup.
  • Right hook point — postCommands() fires after lookup() (DI is ready, context.lookup is available), so determinePom() can be called safely.
  • Consistent exit code — exits 0, matching the mvn -h convention.

When a user runs 'mvn' without any arguments and there is no pom.xml
in the current directory, Maven currently fails with a confusing
BUILD FAILURE / NoGoalSpecifiedException error. This is surprising
and unhelpful: the user likely just typed 'mvn' by mistake or wants
to explore what Maven can do.

This commit improves the UX by detecting this specific situation early
in MavenInvoker.postCommands() — after DI is fully set up but before
the build is attempted — and printing the usage (same output as mvn -h)
then exiting with code 0, instead of failing.

The check is deliberately narrow: it only fires when BOTH conditions
are true:
  1. No goals or lifecycle phases were specified on the command line.
  2. No POM file is present in the current directory (or via -f).

If a POM is found, Maven proceeds as before: the default goal (if any)
is executed, or the existing NoGoalSpecifiedException is reported.
This preserves existing behavior for all normal project builds.

Fixes: https://github.com/apache/maven/issues/XXXXX
When the -f/--file option points to a non-existent file, Maven previously
printed redundant information:

  POM file X does not exist            (bare, no prefix - source TBD)
  [ERROR] Error executing Maven.
  [ERROR] Error determining top directory
  [ERROR] Caused by: POM file X does not exist

The 'Error determining top directory' wrapper and 'Caused by:' repetition
added noise without helping the user understand what went wrong.

Now, when getTopDirectory() throws an IllegalArgumentException (a user-
facing error with a self-explanatory message), we log the message directly
instead of wrapping it. The output is now:

  [ERROR] Error executing Maven.
  [ERROR] POM file X specified with the -f/--file command line argument does not exist

The same pattern is applied to getRootDirectory() for consistency.
The shell script's find_file_argument_basedir() already validated the -f
argument and printed an error, but the exit 1 was inside a subshell so
it did not stop the main script from launching the JVM, which then
printed the same error again through the Java logging infrastructure.

Fix: propagate the subshell exit code through find_maven_basedir() and
bail out before launching the JVM if the -f argument is invalid.

Also add the [ERROR] prefix to the shell-side error messages to match
Maven's standard logging format.

@gnodet-bot gnodet-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid approach — three cleanly separated concerns, each independently correct.

1. Print usage on mvn with no goals and no POM (MavenInvoker.postCommands): The guard goals().orElse(List.of()).isEmpty() && determinePom(...) == null is precise — exits 0 with help only when no POM is found, preserving default-goal behavior when a POM exists. Uses the exact same displayHelp + ExitException(0) pattern as helpOrVersionAndMayExit, and fires after lookup() so DI is ready for determinePom().

2. Clean -f error messages (BaseParser): Catching IllegalArgumentException before the generic catch (Exception) lets the already-descriptive message from getTopDirectory() ("POM file X ... does not exist") print directly instead of being wrapped in "Error determining top directory". Same pattern applied symmetrically to getRootDirectory.

3. Shell exit code propagation (mvn): Previously find_file_argument_basedir's exit 1 only left the subshell — now captured via rc=$?; [ $rc -ne 0 ] && exit $rc and re-checked after find_maven_basedir. The echo → printf '[ERROR] ...' switch adds consistency with Maven's Java-side error formatting and better POSIX portability.

No issues found.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

@raupach-e2n

Copy link
Copy Markdown

I would like to suggest a better phrasing. Instead of

POM file X does not exist
[ERROR] Error executing Maven.
[ERROR] Error determining top directory
[ERROR] Caused by: POM file X ... does not exist

Maybe this is sufficient:

[ERROR] Caused by: pom.xml does not exist

@ascheman

Copy link
Copy Markdown
Contributor

Looks good, and it lines up with the dev@ consensus on the "Print usage when no arguments are given" thread — POM present → the default goal (if any) still runs, otherwise print usage. Keying the Java check on POM presence rather than trying to detect a default goal also neatly side-steps the "detecting a default goal needs the built model, very early in startup" concern raised there. 👍

One cross-platform gap: the [ERROR] reformatting of the -f/--file messages landed in the Unix mvn script but not in the Windows launcher mvn.cmd, which carries the same two messages (:process_file_arg):

echo POM file "%FILE_ARG%" specified the -f/--file command-line argument does not exist >&2
echo Directory "%POM_DIR%" extracted from the -f/--file command-line argument "%FILE_ARG%" does not exist >&2

So Windows users still get the un-prefixed form. Those should become:

echo [ERROR] POM file "%FILE_ARG%" specified with the -f/--file command-line argument does not exist >&2
echo [ERROR] Directory "%POM_DIR%" extracted from the -f/--file command-line argument "%FILE_ARG%" does not exist >&2

(also fixes a stray specified the → specified with the, matching the Unix wording). I'd have made these one-click suggestions, but mvn.cmd isn't part of this PR's diff, so GitHub won't let me anchor a suggestion there — if you add the file they can be applied directly.

The harder half of your Unix change needs no Windows counterpart, though: the subshell-exit propagation (so a bad -f aborts instead of continuing and printing the error twice) has nothing to fix in mvn.cmd — it already aborts on a bad -f via goto error, so it never had the double-error behavior. Only the [ERROR] prefix is missing there.

@ascheman

Copy link
Copy Markdown
Contributor

Heads-up: CI is red on this PR — initial-build fails at the test-mvn-path-conversion exec (project apache-maven), and it's caused by this change.

All the path-conversion assertions pass (ok - POSIX/CYGWIN/MINGW/MSYS …); the script then aborts (it runs under set -e) at the first -f scenario:

output=`run_mvn_with_args CYGWIN_NT-10.0 "$stub_dir" -f /cygdrive/c/temp/pom.xml validate`

Root cause: this PR makes a non-existent -f/--file a fatal early exit in the launcher — find_file_argument_basedir's exit 1 is now propagated up (the correct fix for the double-error). Previously that "POM file … does not exist" message was printed but non-fatal, so the launcher continued. test-mvn-path-conversion.sh passes simulated, non-existent -f paths — it only checks path conversion, with java stubbed, so the file never had to exist — and under set -e the now-fatal exit aborts the test before it can assert the conversion.

The behavior change is right; the test just needs to catch up — e.g. touch a real file at each -f path the test passes (or point them at an existing stub) so the existence check passes and the conversion assertions still run. (-s scenarios are unaffected — settings files aren't existence-checked in the launcher.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants