Maven: partial compilation before code generation

tl; dr-edition: I have a compilation that I know that will fail, but I want a subset of the classes that are still compiled in my target/classes folder after compilation. I set up <failOnError>false</failOnError> , but classes are not generated, not even a dummy class, which is independent of any other classes except Object . Is there any configuration to achieve this?


I have a project with maven support, the workflow of which consists mainly of the following (relevant) goals:

  • ...
  • init-compile

    The code generator (below) uses reflection-based configuration, so in the first pass I want to try to compile as much of the project as possible so that ClassNotFoundExceptions is not selected. This compilation is configured using <failOnError>false</failOnError> so that the build continues.

    Unfortunately (you can call this a design error), config is used both for code generation (with an OWL file and namespace for map matching) and at runtime, therefore it also contains other elements that are not needed for the generator code. but are still readable and therefore necessary for the path to the class in order to succeed.

  • generate-model

    At this point, some model classes are generated from the OWL ontology, creating code that makes the entire project fully compiled.

  • default-compile

    Now the rest of the classes should be compiled, obviously

  • save-model

    Now ontology instances are read and serialized to a file for runtime

  • ...

Side note: both generating and retaining the use of the maven-exec-plugin model, but I sincerely do not think this is important at all.

Question:

When I run my build using mvn -e -U clean package source:jar javadoc:jar install:install , it fails during the generate-model target with errors that I am trying to avoid. target/classes empty, so it seems that the compiler is not spitting out a subset of the classes that it could / should have handled. Is there any way to achieve this?

I have two workarounds that I don't like:

  • Editing the "AST" configuration file before parsing it into Java objects, so that only the part related to the code generator is analyzed (code setting is required, which I have access to, but should be considered unchanged by my project);
  • and set up the init-compile target to only include the necessary classes (too inflexible, since POM should / may be a template for future applications using the same model).

If you can imagine another way around my problem, which you can see from my description, I would be happy to hear them too!

+4
source share
2 answers

First, let me repeat your problem to make sure that I understand it correctly.

  • You have a set of classes whose function in their compiled form is to configure the code generator and runtime. (A subset of them is related to the code generator, but generation will not succeed if the complete configuration is missing. Therefore, we can consider this as though the entire configuration was necessary.)

  • Then you have a set of classes that will be generated as source code. They have a generation time, possibly compilation time, and a dependency on runtime on configuration classes.

  • Finally, you have other code that has a dependency on compilation time on the generated classes and a dependency on runtime on both classes and configuration classes.

  • However, your configuration classes do not have any dependencies at compile time on generated classes or other code. You are not explicitly talking about this, but I assume it is, otherwise you have a circular dependency problem.

Here is my suggestion: Divide the project into a multi-module ("reactor") project. Your current project will become a reactor design module. Create a new module called "config" or similar and move your configuration classes into it. The main module depends on this.

If you do not like multi-module projects, you can achieve the same by declaring an additional execution of the compilation plugin associated with the source-generation phase. (You do not say, but I assume that you are doing code generation at this point. If you declare the compilation plugin before the POM code generator plugin, Maven will execute them in the same order.) You would use the “enable” compilation plugin filter to compile configuration classes only. To do this, you will need to have configuration classes in a separate package from everything else, which is good practice anyway.

+4
source

There is one very convenient solution - use the Eclipse Java Compiler (EJC) instead of the standard Oracle javac! One of the advantages of ECJ over javac is that it is error-prone, it tries to compile as much as possible and saves the already generated class files. The EJC was designed for use in the IDE for highly interactive work, where partial compilation is required, but it can also be used as a CLI or Maven plugin. Plexus guys provide EJC as a convenient dependency on Maven.

Compilers connect to Maven. You can have several compilations (compilation) defined in one POM, and you can use different compilers for each, giving the developer a wide range of options.

POM code example:

 <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> 

Credits to Gabriel Axel for his article http://www.gabiaxel.com/2011/10/replacing-javac-with-eclipse-compiler.html

This approach can solve some complex “sources for the generated sources into the original” circular dependencies, potentially unsolvable by separation into separate modules.

In addition, I wanted to integrate source generation in the most transparent way. The need to rearrange the code based on dependencies on the generated sources will ultimately violate it. I want to group my code based on logical design, not because of technical features.

If the code generator works with xtext, as in my case, and you use the xtext-maven-plugin, say, the recently released 2.5.0, you do not need to configure anything, as in the above example, the plugin is exactly under the hood.

+2
source

All Articles