在maven构建中使用Eclipse Java编译器(ecj)

18
Eclipse使用自己的编译器(ECJ)来编译Java代码。在Eclipse中调试编译的程序更容易,因为可以立即应用简单的代码更改(通过热部署)。
另一方面,Maven默认使用Oracle JDK生成不同的字节码,防止在Eclipse调试会话中进行热部署。
因此,如果我计划调试程序,我想要在我的Maven构建中使用Eclipse ECJ编译器。对我而言,一个方便的方法是创建一个“ecj”配置文件:
- 编译发布版
$ mvn package
  • 编译启用热代码替换的快照

    $ mvn -P ecj package
    
  • 同时,可以在 settings.xml 或者甚至是Eclipse项目属性中指定配置文件的激活。

    我的问题是:

    1. 这是正确的方法吗?
    2. 如何进行配置?
    3. 可以使用Maven工具链来实现吗?
    2个回答

    20
    Eclipse Java编译器(ecj)相对于标准的javac编译器有很多优势。它速度快,可以配置更多警告和错误,提高代码质量。编译器中最有趣的事情之一是添加了编译器内部的null类型:通过使用@Nullable和@NotNull注释来注释您的代码,您可以强制Eclipse编译器在编译时检查空访问,而不是运行时。严格应用此方法可以教您编写更安全的代码(通过防止空值),并防止测试或生产期间的NPE异常。
    在Maven中使用Eclipse编译器并不难,但网络上存在很多错误信息和过时信息,这会导致很多混淆。我希望这可以帮助澄清事情。
    要使Maven使用ecj编译器,您需要使用plexus-compiler-eclipse插件,而不使用其他任何东西。典型的配置如下:
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
            </plugin>
    
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <compilerId>eclipse</compilerId>
                    <source>${source.jdk.version}</source>
                    <target>${target.jdk.version}</target>
                    <!-- Passing arguments is a trainwreck, see https://issues.apache.org/jira/browse/MCOMPILER-123 -->
                    <compilerArguments>
                        <properties>${project.basedir}/.settings/org.eclipse.jdt.core.prefs</properties>
                    </compilerArguments>
                    <showWarnings>true</showWarnings>
                    <showDeprecation>true</showDeprecation>
                </configuration>
    
                <dependencies>
                    <dependency>
                        <groupId>org.codehaus.plexus</groupId>
                        <artifactId>plexus-compiler-eclipse</artifactId>
                        <version>2.8.3</version>
                    </dependency>
    
                    <dependency>
                        <groupId>org.eclipse.jdt</groupId>
                        <artifactId>ecj</artifactId>
                        <version>3.13.101</version>
                    </dependency>
                </dependencies>
            </plugin>
    </pluginManagement>
    

    将此部分放在父/根pom的pluginManagement或build部分中。

    现在让我们解释不同的部分;)

    maven-compiler-plugin需要是最新版本。source和target参数定义要用于源代码和字节码的java版本,并且通常相同。

    向编译器传递参数是一场灾难。请参见下面的单独部分。在此示例中,我使用了属性设置,允许我提供详细的设置,例如编译时我希望拥有哪些错误和警告。通过在参数内使用${project.basedir}变量,我可以为每个项目设置这些设置:每个项目都需要存在.settings/org.eclipse.jdt.core.prefs文件(这恰好是Eclipse IDE留下其编译器设置的位置)。

    对plexus-codehaus-eclipse的依赖定义了知道如何运行Eclipse编译器的插件。2.8.3版本是撰写本文时的最新版本,但该版本存在一些问题。版本2.8.4应该会附带一个重写的编译器接口,修复了很多问题,但撰写本文时,该版本仍在进行中。您可以在此处找到有关该插件的详细信息,因此可以跟踪新发布/代码更改的进度。

    另一个重要的依赖是org.eclipse.jdt:ecj依赖项:它指定了要使用的ecj编译器的确切版本。您应该始终指定它,否则在插件决定在您发布之前的某一天使用另一个版本的编译器时,构建稳定性将会受到影响;) ecj编译器要使用的版本号有点问题。您可以从发布列表中找到版本号,然后检查此Maven存储库是否有类似的内容。但是,这个存储库只包含旧版本。当您需要更近期的版本时,显然应该在此处查看 - 这是Eclipse当前推送其版本的地方。这个新的存储库放弃了早期存储库中易于识别的版本号;它使用像3.1x.x这样的版本号,如上所示。 Eclipse通常每年发布一个主要版本,以及一两个修复版本。 3.13.x号码中的第二部分对应于Eclipse平台项目内部版本控制用于发布的版本控制。很难获得列表,但至少这些是已知的:
    Version    Eclipse Release      Compiler Version
    3.13.0     Oxygen Release       4.7
    3.13.50    Oxygen 1a            4.7.1a
    3.13.100   Oxygen R2            4.7.2
    

    版本号始终以3开头,13或多或少是发布的“年份”。因此,当13是氧气(2017年,4.7)时,14可能会是Photon(2018年,4.8)。

    plexus-compiler-eclipse插件的版本:2.8.4之前

    在2.8.4之前的版本中,plexus-compiler-plugin使用内部API启动Eclipse编译器。这导致许多功能不能正常工作,因为该内部API不解释ecj编译器的通常命令行参数。这使得它很难使用,并且一些功能不受支持。以下是限制列表:

    • 注释处理未实现。任何配置都将被静默忽略。

    • 通过使用<compilerArguments>标签添加特定参数很困难,因为实现存在多个问题:

    • 编译器mojo似乎会向输入此处的所有参数添加破折号。然而,此插件版本使用的内部API需要没有破折号的参数。因此,插件会再次删除它们。 由于这里的参数不是真正的命令行ecj参数,因此很难知道要使用哪些参数:有关详细信息,请查看Eclipse源代码中的Compiler.java类和CompilerOptions.java类。

    • 该插件确实接受一些参数,但这些参数由插件本身解释,然后“转换”为内部API。

    此插件在<compilerArguments>标签中接受以下参数:

    • <properties>filename</properties>:定义一个属性文件,该文件将传递给编译器的-properties参数。可以通过查看Eclipse项目中的.settings/org.eclipse.jdt.core.prefs文件来找到此文件格式的示例:该文件存储编译器的配置。它包含警告、错误和信息消息以及编译器兼容性设置的设置。

    • <errorsAsWarnings>whatever</errorsAsWarnings>。当这个有效时,插件将忽略编译器生成的任何错误,并将它们报告为警告。当然,编译仍然失败,所以根据错误,可能已经写入/更新了.class文件或没有。这由插件本身处理:它只是将所有错误更改为警告,并告诉世界编译工作正常。

    从2.8.4版本开始

    plexus-compiler-eclipse插件的2.8.4版本已经被大部分重写。它现在使用ECJ编译器的公共API,这几乎就是ECJ编译器本身。例如,这意味着插件现在可以做ECJ能做的一切(如注释处理),并且输入在标签中的参数现在会传递给编译器,这意味着您应该能够使用ecj的帮助页面找到有趣的参数来添加。

    与以前的版本一样,此版本还要求您从所有参数名称中删除“-”;在将参数名称添加到ecj命令行之前,破折号会自动添加回去。

    这个版本支持 Maven 定义的注解处理;通过将所需部分添加到编译块中,您可以运行您的注解处理器。例如:

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>${maven-compiler-plugin.version}</version>
        <configuration>
            <annotationProcessors>
                <annotationProcessor>db.annotationprocessing.EntityAnnotationProcessor</annotationProcessor>
            </annotationProcessors>
            <annotationProcessorPaths>
                <dependency>
                    <groupId>to.etc.domui</groupId>
                    <artifactId>property-annotations-processor</artifactId>
                    <version>1.2-SNAPSHOT</version>
                </dependency>
            </annotationProcessorPaths>
        </configuration>
    
        <dependencies>
            <dependency>
                <groupId>to.etc.domui</groupId>
                <artifactId>property-annotations-processor</artifactId>
                <version>1.2-SNAPSHOT</version>
            </dependency>
        </dependencies>
    </plugin>
    

    这部分可能看起来不完整,因为根本没有提到plexus-compiler-eclipse插件,但请记住,在Maven中,该配置继承:在这种情况下,父POM包含上面的部分,而这只是为这个POM的项目添加了一些配置。


    我使用最新版本的plexus-compiler-eclipse 2.10.0。而且ecj也是最新版本的3.28.0。然而apt无法工作。我使用谷歌自动服务apt生成spi配置。使用ecj时,没有生成任何内容,但javac可以正常工作。我调试插件,并发现apt路径已经正确地通过参数“processorpath”发送到了ecj。 - vipcxj
    @vipcxj 我使用注解处理器测试了这些版本,看起来可以正常工作。 - fjalvingh
    这是一个程序相关的内容:错误报告。要触发此错误,您需要使用jdk >= 9,并且apt应该配置为注释路径条目,而不仅仅是将其放在依赖项中。然后-processorPath将被发送到ecj,但被忽略了。看起来ecj将使用classpath作为processormodulepath,因此如果classpath上没有apt,则会被忽略。 - vipcxj
    将 com.google.auto.service:auto-service-annotations:1.0-rc7 放入 dependencies 中,并将 com.google.auto.service:auto-service:1.0-rc7 放入 configuration.annotationProcessorPaths 中。 - vipcxj

    19

    可以 更改 maven-compiler-plugin 使用的默认 javac 编译器。Eclipse 编译器包含在 plexus-compiler-eclipse 构件中,并通过将 maven-compiler-plugin 的 compilerId 属性设置为 eclipse 来声明。

    如果要激活此更改以用于自定义配置文件,则可以使用以下配置:

    <profile>
      <id>ecj</id>
      <build>
        <plugins>
          <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.6.0</version>
            <configuration>
              <compilerId>eclipse</compilerId>
            </configuration>
            <dependencies>
              <dependency>
                <groupId>org.codehaus.plexus</groupId>
                <artifactId>plexus-compiler-eclipse</artifactId>
                <version>2.8.1</version>
              </dependency>
            </dependencies>
          </plugin>
        </plugins>
      </build>
    </profile>
    

    该插件在 plexus-compiler GitHub 存储库 中维护。版本2.8.1 使用JDT 3.11.1.v20150902-1521,但您也可以通过添加对org.eclipse.tycho:org.eclipse.jdt.core 的依赖来使用自己的版本。


    非常好用!谢谢! - Boris Brodski
    2
    很好,它对你有用。以防万一:我找不到 plexus-compiler-eclipse 的维护位置,因此我不确定它们是否提供与最新版本的 ecj 集成(我能找到的最新集成是 20140604 的 ecj)。如果以上方法出现任何问题,JDT FAQ 也有一些提示(建议使用 tycho-compiler-jdt 进行任务):https://wiki.eclipse.org/JDT/FAQ#Can_I_use_JDT_outside_Eclipse_to_compile_Java_code.3F - Stephan Herrmann
    它能工作,但在我的情况下,我还必须将<source>1.8</source><target>1.8</target>放在<compilerId>eclipse</compilerId>下才能正常工作。 - Trieu Nguyen

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接