tl;dr-edition: I have a compilation I know that will fail, but want the subset of classes that are still compilable in my target/classes
folder after compilation. I have configured <failOnError>false</failOnError>
, but no classes are generated, not even a dummy class that is independent of any other classes except Object
. Is there some configuration to achieve this?
I have a maven-powered project whose workflow consists of basically the following (relevant) goals:
...
init-compile
The code generator (below) uses a config that is reflection-based, so, in a first pass, I want to try compile as much of the project as possible so that no ClassNotFoundExceptions are thrown there. This compilation is configured with <failOnError>false</failOnError>
so that the build continues.
Unfortunately (you could call it a design flaw), the config is used both for code generation (specifying the OWL file and namespace to package mappings) and at runtime, so it also contains other elements that are not needed for the code generator, but are still read and therefore needed on the classpath to succeed.
generate-model
In this step, some model classes are generated from an OWL-Ontology, creating the code that makes the rest of the project completely compileable.
default-compile
Now, the rest of the classes should be compiled, obviously
save-model
Now, the instances from the ontology are read and serialized to a file for runtime
...
Side note: both generate and save model use maven-exec-plugin
, but I sincerely don't think that matters at all.
Question:
When I run my build with mvn -e -U clean package source:jar javadoc:jar install:install
, it fails during the generate-model
goal with the errors I'm trying to avoid. target/classes
is empty, so it seems that the compiler doesn't spit out the subset of classes it could/should have been able to process. Is there a way to achieve this?
I have two workarounds in mind which I both don't like:
init-compile
goal to only include the needed classes (too inflexible, because the POM should/could be a template for future applications using the same model).If you can imagine another way to work around my problem that you can see from my description, I would be glad to hear them, too!
4) Apache Maven doesn't support incremental builds very well. Because of that fact, most people use mvn clean compile instead of mvn compile for example. This is pretty time consuming and can be improved a lot. The goal is to make mvn compile , mvn verify , mvn install , ...
<annotationProcessorPaths>The detection itself depends on the configuration of annotationProcessors. Each classpath element is specified using their Maven coordinates (groupId, artifactId, version, classifier, type). Transitive dependencies are added automatically.
mvn clean: Cleans the project and removes all files generated by the previous build. mvn compile: Compiles source code of the project. mvn test-compile: Compiles the test source code.
Full name: org.apache.maven.plugins:maven-compiler-plugin:3.10.1:testCompile. Description: Compiles application test sources.
First, let me recapitulate your problem to be sure I've grasped it properly.
You have a set of classes whose function, in their compiled form, is to configure both the code generator and the runtime. (A subset of these are relevant to the code generator, but generation will fail unless the complete configuration is present. So we can treat it as though the entire configuration were necessary.)
You then have a set of classes which will be generated as source code. These have a generation-time, perhaps a compile-time, and a run-time dependency upon the configuration classes.
Finally you have some other code which has a compile-time dependency upon the generated classes, and a runtime dependency upon both the generated classes and the configuration classes.
However, your configuration classes don't have any compile-time dependencies upon the generated classes nor upon the other code. You don't explicitly say this, but I'm assuming it, otherwise you've got a circular dependency problem.
Here's my suggestion: Divide the project into a multi-module ("reactor") project. Your current project will be a module of the reactor project. Create a new module called "config" or similar and move your config classes into it. Have the main module depend upon it.
If you don't like multi-module projects, you could achieve the same thing by declaring an extra execution of the compile plugin, bound to the generate-sources phase. (You don't say, but I assume you're doing the code generation in this phase. If you declare the compile plugin before the code generator plugin within the POM, Maven will execute them in the same order.) You would use the "include" filter of the compile plugin to compile only the config classes. For this, you would need to have the config classes in a separate package from all the rest, which is good practice anyway.
There is one highly convenient solution - use Eclipse Java Compiler (EJC) instead of standard Oracle javac! One of ECJ advantages over javac is that it is permissible of errors, it tries to compile as much as possible and keeps already generated class files. EJC was developed for use in IDE, for highly interactive work where partial compilation is a must, but it can also be used as CLI or Maven plugin. Plexus guys provide EJC as a handy Maven dependency.
Compilers are pluggable in Maven. You can have multiple compilation (compilation executions) defined in one POM and you can use different compilers for each giving the developer broad range of options.
Example POM code:
<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 tricky "source to generated source to source" circular dependencies, potentially unsolvable by splitting into separate modules.
Also, I wanted to integrate source generation in the most transparent way. Having to reshuffle the code based on dependencies on generated sources would definitively break it. I want to group my code based on logical design not because of technicalities.
If you code generator happens to xtext, like in my case, and you use xtext-maven-plugin, let's say freshly released 2.5.0, you don't have to configure nothing like in the example above, the plugin does exactly that under the hood.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With