Skip to content

Maven multi-line <arg> does not work on Windows when <fork>ed #4256

@commonquail

Description

@commonquail

https://errorprone.info/docs/flags#maven discusses limitations of configuring Error Prone in Maven and demonstrates a way to split arguments across multiple lines. However, while that example works outside of Windows it does not work on Windows iff the compiler was forked, and I can find no variant that does work.

This is not a defect of Error Prone, however, Error Prone's documentation ought perhaps to expand its disclaimer -- especially because peculiarities of the error make it difficult to troubleshoot.

The error manifests in Maven 3.9.6 with maven-compiler-plugin 3.10.0 through 3.12.1, current latest, independently of whether Maven Wrapper is used. The error manifests only when forking the compiler (e.g. <fork>true</fork>) and appears to be an artifact of how Maven orchestrates forking on Windows. I'm sure that other versions of Maven are also affected.

The sample POM below minimally reproduces the issue together with Error Prone, such that simply toggling the user property maven.compiler.fork causes compilation to fail:

> foreach ($fork in 'false', 'true') {
     .\mvnw.cmd clean compile -X "-Dmaven.compiler.fork=$fork"
}
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] error: invalid flag: -Xep:DeadException:WARN
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.823 s
[INFO] Finished at: 2024-01-21T12:16:07+01:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.12.1:compile (default-compile) on project y-u-no-compile: Compilation failure
[ERROR] error: invalid flag: -Xep:DeadException:WARN
[ERROR]
[ERROR] -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.12.1:compile (default-compile) on project y-u-no-compile: Compilation failure
error: invalid flag: -Xep:DeadException:WARN

    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute2 (MojoExecutor.java:333)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute (MojoExecutor.java:316)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:212)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:174)
    at org.apache.maven.lifecycle.internal.MojoExecutor.access$000 (MojoExecutor.java:75)
    at org.apache.maven.lifecycle.internal.MojoExecutor$1.run (MojoExecutor.java:162)
    at org.apache.maven.plugin.DefaultMojosExecutionStrategy.execute (DefaultMojosExecutionStrategy.java:39)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:159)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:105)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:73)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:53)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:118)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:261)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:173)
    at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:101)
    at org.apache.maven.cli.MavenCli.execute (MavenCli.java:906)
    at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:283)
    at org.apache.maven.cli.MavenCli.main (MavenCli.java:206)
    at jdk.internal.reflect.DirectMethodHandleAccessor.invoke (DirectMethodHandleAccessor.java:103)
    at java.lang.reflect.Method.invoke (Method.java:580)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:283)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:226)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:407)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:348)
    at jdk.internal.reflect.DirectMethodHandleAccessor.invoke (DirectMethodHandleAccessor.java:103)
    at java.lang.reflect.Method.invoke (Method.java:580)
    at org.apache.maven.wrapper.BootstrapMainStarter.start (BootstrapMainStarter.java:52)
    at org.apache.maven.wrapper.WrapperExecutor.execute (WrapperExecutor.java:161)
    at org.apache.maven.wrapper.MavenWrapperMain.main (MavenWrapperMain.java:73)
Caused by: org.apache.maven.plugin.compiler.CompilationFailureException: Compilation failure

The defect can easily be shown without Error Prone but a successful compilation is a good contrast and is easier to show with Error Prone.

When compiling with -X, whether in-process or forked, Maven always reports that its compiler arguments include instructions like

... -XDcompilePolicy=simple -Xplugin:ErrorProne \
  -Xep:DeadException:WARN \
  -Xep:GuardedBy:OFF

That is, <arg>s contents verbatim.

Under the right circumstances the -X argument will also generate a target/javac.bat file and a sibling file that contains (some of the) javac arguments. These files are generated only when maven-compiler-plugin progresses a forked build far enough -- in-process builds never generate these files, and forked builds can encounter failures early enough in the process that the files are not generated.

From the generated arguments file we can see that what the forked compiler actually executes is

"-XDcompilePolicy=simple"
"-Xplugin:ErrorProne /
  -Xep:DeadException:WARN /
  -Xep:GuardedBy:OFF"

That is, \ has been replaced with /. This looks suspiciously like an errant path separator normalization! We can also manually restore the original \s inside the arguments file and observe that compilation then succeeds:

...\target> .\javac.bat

...\target>cmd.exe /X /C "..."
...\src\main\java\T.java:1: warning: [DefaultPackage] Java classes shouldn't use default package
public class T { }
^
    (see https://errorprone.info/bugpattern/DefaultPackage)
1 warning

POM:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>example.multi-line-fork-crash</groupId>
  <artifactId>y-u-no-compile</artifactId>
  <version>1.0-SNAPSHOT</version>
  <properties>
    <maven.compiler.release>21</maven.compiler.release>
    <maven-compiler-plugin.version>3.12.1</maven-compiler-plugin.version>
    <error-prone.version>2.24.1</error-prone.version>
  </properties>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.12.1</version>
        <configuration>
          <compilerArgs>
            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
            <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
            <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
            <arg>-XDcompilePolicy=simple</arg>
            <arg>
              -Xplugin:ErrorProne \
              -Xep:DeadException:WARN \
              -Xep:GuardedBy:OFF</arg>
          </compilerArgs>
          <annotationProcessorPaths>
            <path>
              <groupId>com.google.errorprone</groupId>
              <artifactId>error_prone_core</artifactId>
              <version>${error-prone.version}</version>
            </path>
          </annotationProcessorPaths>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

.mvn/jvm.confg:

--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED

src/main/java/T.java:

public class T { }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions