Maven:代码生成之前的部分编译

5
tl;dr-edition: 我有一个编译错误的编译文件,但是想要在编译后的target/classes文件夹中找到仍然可以编译的类。我已经将<failOnError>false</failOnError>配置好了,但是没有生成任何类,甚至没有一个与其他类无关的虚拟类,除了Object。是否有一些配置可以实现这个目标?

我有一个使用Maven构建的项目,其工作流程基本上包括以下(相关)目标:

  • ...
  • init-compile

    代码生成器(下面)使用反射为基础的配置文件,因此,在第一次尝试编译项目时,我希望尽可能多地编译,以便不会出现ClassNotFoundException。这个编译配置了<failOnError>false</failOnError>,所以构建过程可以继续进行。

    不幸的是(你可以称之为设计缺陷),该配置同时用于代码生成器(指定OWL文件和命名空间到包映射)和运行时,因此它还包含其他不需要的元素,但仍然被读取并且需要在类路径上才能成功。

  • generate-model

    在这一步中,从OWL本体生成了一些模型类,创建了使项目的其余部分完全可编译的代码。

  • default-compile

    现在,应该编译其他类,显然

  • save-model

    现在,从本体中读取实例并将其序列化到文件以供运行时使用

  • ...

附注:generate和save model都使用了maven-exec-plugin,但我真的不认为这有任何影响。

问题:

当我使用mvn -e -U clean package source:jar javadoc:jar install:install运行构建时,在generate-model目标期间失败,并出现了我试图避免的错误。 target/classes为空,因此似乎编译器没有输出它本来可以/应该处理的类的子集。是否有方法可以实现这一点?

我有两种解决方法,但我都不喜欢:

  • 在将配置文件“AST”解析为Java对象之前编辑它,以便只解析与代码生成器相关的部分(需要调整我可以访问但应被我的项目视为不可变的代码);
  • 并将init-compile目标配置为仅包括所需的类(过于死板,因为POM应/可能是使用相同模型的未来应用程序的模板)。
如果您能够想象另一种解决我问题的方式,可以从我的描述中看出来,我也很乐意听取!

也许我漏掉了什么 - 当您将generate-model目标移动到generate-sources阶段时,您的构建是否无法正常工作? - Perception
配置文件提供在类路径上,在应用程序运行时也可以找到它,忘了说了,所以 generate-model(和 init-compile 在此之前)在 process-resources 执行期间被执行。但正如上面所述,真正的问题或多或少是我需要一个初始的、不完整的编译,提供在配置中引用的类。我想我最终会采取第一种解决方法,但我仍然对如何实现这一点感兴趣... - Silly Freak
2个回答

4

首先,让我回顾一下您的问题,以确保我正确理解了它。

  • 您有一组类,其编译形式的功能是配置代码生成器和运行时。(其中的一个子集与代码生成器相关,但如果不完整配置,则生成将失败。因此,我们可以将其视为需要整个配置。)

  • 然后,您有一组将作为源代码生成的类。这些类具有对配置类的生成时间、可能是编译时间和运行时依赖。

  • 最后,您有一些其他代码,它在编译时依赖于生成的类,并在运行时依赖于生成的类和配置类。

  • 然而,您的配置类没有任何编译时依赖于生成的类或其他代码。您没有明确说明这一点,但我假设它是这样的,否则您将面临循环依赖问题。

这是我的建议: 将项目分成多模块("反应堆")项目。您当前的项目将是反应堆项目的一个模块。创建一个名为"config"或类似的新模块,并将您的配置类移动到其中。主模块依赖于它。

如果您不喜欢多模块项目,您可以通过声明编译插件的额外执行,绑定到生成源代码阶段来实现相同的效果。(您没有说明,但我假设您是在此阶段进行代码生成。如果您在POM中声明编译插件在代码生成器插件之前,Maven将按照相同的顺序执行它们。)您将使用编译插件的"include"过滤器仅编译配置类。为此,您需要将配置类放在与其余所有类不同的包中,这也是一个好习惯。


你好!感谢你的回答!理论上,拆分项目是一种选择,但我认为对于我的用例(性能测试),这有点过度设计了:我们为测试用例拥有单独的项目,因此将项目数量翻倍会很麻烦。只是想让你知道,我采用了解决方法1的变体,在这个变体中,我读取了整个配置文件,但只将其子树解析为Java对象 ;) - Silly Freak

3
有一个非常方便的解决方案 - 使用Eclipse Java编译器(EJC)代替标准的Oracle javac!ECJ相对于javac的优势之一是它允许出现错误,它尝试尽可能多地编译并保留已生成的class文件。EJC是为IDE开发的,用于高度交互式工作,其中部分编译是必须的,但也可以用作CLI或Maven插件。Plexus团队将EJC提供为方便的Maven依赖项。
编译器在Maven中是可插拔的。您可以在一个POM中定义多个编译(编译执行),并且可以为每个编写不同的编译器,为开发人员提供广泛的选择。
示例POM代码:
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.1</version>
    <executions>
        <execution>
            <id>pre-compilation</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>compile</goal>
            </goals>
            <configuration>
                <compilerId>eclipse</compilerId>
                <!-- IMPORTANT. Select EJC as compiler instead of javac -->
                <failOnError>false</failOnError>
                <!-- IMPORTANT. When ECJ is used errors are reported 
                only as warnings, it continues in compilation, 
                try to compile as much as possible, keeps already 
                generated classes in target/classes -->
            </configuration>
        </execution>
        <execution>
            <id>default-compile</id>
            <phase>compile</phase>
            <!-- in the end recompile everything with standard javac.
                 This time no compilation errors are expected or tolerated. -->
            <goals>
                <goal>compile</goal>
            </goals>
            <configuration>
                <useIncrementalCompilation>false</useIncrementalCompilation>
            </configuration>
        </execution>
    </executions>
    <dependencies>
        <dependency>
            <groupId>org.codehaus.plexus</groupId>
            <artifactId>plexus-compiler-eclipse</artifactId>
            <version>2.3</version>
        </dependency>
    </dependencies>
</plugin>

<!-- generate sources. This plugin executes in facet BETWEEN the compilations due to 
    'generate-sources' phase binding and relative position in POM -->
<plugin>
    <groupId>org.eclipse.xtext</groupId>
    <artifactId>xtext-maven-plugin</artifactId>
    <version>2.5.0</version>
    <executions>
        <execution>
            <id>generate-the-stuff</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>
</plugin>

感谢Gabriel Axel的文章http://www.gabiaxel.com/2011/10/replacing-javac-with-eclipse-compiler.html
这种方法可以解决一些棘手的“源到生成的源到源”的循环依赖问题,这些问题可能无法通过分成单独的模块来解决。
此外,我希望以最透明的方式集成源代码生成。根据生成源的依赖关系重新排列代码肯定会破坏它。我想基于逻辑设计来组织我的代码,而不是因为技术问题。
如果您的代码生成器像我的情况一样使用xtext,并且您使用了xtext-maven-plugin,例如刚发布的2.5.0版本,则不需要像上面的示例那样进行任何配置,该插件在幕后执行此操作。

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