Maven 3: How to exclude generated sources from Xlint check? - java

We only allow java source code without Xlint errors. However, when sources are generated by third party tools, this is not practical. Examples of generated sources in our use-case are: JFlex, JavaCC, JAXB and annotation processors.
So the question is: how to exclude the generated sources from the Xlint checks? (see current configuration below)
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration combine.self="override">
<source>${java.version}</source>
<target>${java.version}</target>
<showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings>
<!-- Since JDK1.3 javac ignores any optimization flags -->
<optimize>true</optimize>
<debug>false</debug>
</configuration>
<executions>
<execution>
<id>default-compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<!-- everything in target/generated-sources/** should be excluded from this check -->
<compilerArgs>
<arg>-Xlint:all,-rawtypes</arg>
</compilerArgs>
</configuration>
</execution>
</executions>
</plugin>

There is no direct configuration in the maven-compiler-plugin to do that. The parameters passed are for the whole execution, so passing -Xlint:all would apply to all sources to compile.
The solution here is to compile it in two pass: first pass would compile the generated sources without any lint check, and the second pass would compile your project sources (that might depend on the generated classes). Again, the Compiler Plugin doesn't offer a way to specify a path to sources to compile: it compiles all of the sources of the current Maven project.
You have 2 solutions: use 2 executions of the Compiler Plugin with includes/excludes or split this in 2 modules.
Include/Exclude
The idea is to have 2 executions: one that would exclude your main classes (and compile the generated ones), while the other execution would include them. Note that the inclusion/exclusion mechanism works on the fully qualified name of the classes, not the directory structure; so you can't exclude src/main/java.
Assuming all of your main java source files are under the my.package package, you can have:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
<executions>
<execution> <!-- this execution excludes my main sources under my.package -->
<id>default-compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration> <!-- no lint check -->
<excludes>
<exclude>my/package/**/*.java</exclude>
</excludes>
</configuration>
</execution>
<execution> <!-- this execution includes my main sources under my.package -->
<id>compile-main</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration> <!-- adds lint check -->
<includes>
<include>my/package/**/*.java</include>
</includes>
<showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings>
<compilerArgs>
<arg>-Xlint:all,-rawtypes</arg>
</compilerArgs>
</configuration>
</execution>
</executions>
</plugin>
This works because the first execution overrides the default-compile execution that Maven launches automatically on the compile phase, so it makes sure that the generated classes are compiled first.
Using two modules
As such, you need to split this in 2 modules, where the first module would generate the sources and compile them, while the second module would depend on the first one. Create a multi-module Maven project and have a parent my-parent with 2 modules:
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>my.groupId</groupId>
<artifactId>my-parent</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<modules>
<module>my-generating-module</module>
<module>my-module</module>
</modules>
</project>
You could add a <pluginManagement> block here to define default configuration for all the modules using it, like <source> and <target>.
The first module, my-generating-module, is responsible for generating and compiling the sources without any lint check. By default, showWarnings is false so you can keep the default configuration.
Then, in the second module, you can have a dependency on this first one, adding the lint check. Since this will only compile your project sources (the classes generated were already compiled and packaged in the other module), you won't have any warnings for those.

Related

Ignore maven execution block which uses toolchain if ~/.m2/toolchains.xml is not present

I'm working on an open-source library which must work correctly on jre7. Since java 9 has been released, we decided to provide our modules with module-info.java files so that users can make use of them if they prefer to use the library with sdk 9 on jre9 (in this case they'll need to manually put in pom.xml this piece of code:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>9</source>
<target>9</target>
</configuration>
</plugin>
I've tried to implement the solution provided by maven (https://maven.apache.org/plugins/maven-compiler-plugin/examples/module-info.html) to compile the entire code with jdk7 and to compile only module-info.java files with jdk9. Here is a piece of pom.xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<executions>
<execution>
<id>default-compile</id>
<configuration>
<!-- compile everything to ensure module-info contains right entries -->
<!-- required when JAVA_HOME is JDK 8 or below -->
<jdkToolchain>
<version>9</version>
</jdkToolchain>
<release>9</release>
</configuration>
</execution>
<execution>
<id>base-compile</id>
<goals>
<goal>compile</goal>
</goals>
<!-- recompile everything for target VM except the module-info.java -->
<configuration>
<excludes>
<exclude>module-info.java</exclude>
</excludes>
</configuration>
</execution>
</executions>
<!-- defaults for compile and testCompile -->
<configuration>
<!-- jdkToolchain required when JAVA_HOME is JDK 9 or above -->
<jdkToolchain>
<version>[1.7,9)</version>
</jdkToolchain>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-toolchains-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<goals>
<goal>toolchain</goal>
</goals>
</execution>
</executions>
<configuration>
<toolchains>
<jdk>
<version>9</version>
</jdk>
</toolchains>
</configuration>
</plugin>
And I've properly configured ~/.m2/toolchains.xml file. It works perfectly well. But the thing is we need to skip the first execution block, where everything's compiled with jdk9 with module-info, because we don't want to make our users create toolchains.xml file and set up java 9. So what I am probably looking for is the workaround to skip the following code if toolchains.xml is not present in ~/.m2 directory and only execute the second execution block, in which module-info.java files are ignored. Is that even possible? I realise we can still keep two branches (one private with two executional blocks and use of toolchains and another public, which ignores module-info.java files completely, but it doesn't seem an elegant solution.
<execution>
<id>default-compile</id>
<configuration>
<!-- compile everything to ensure module-info contains right entries -->
<!-- required when JAVA_HOME is JDK 8 or below -->
<jdkToolchain>
<version>9</version>
</jdkToolchain>
<release>9</release>
</configuration>
</execution>
You can activate/deactivate certain parts of a pom using Maven profiles.
Wouldn't it be better to provide two different versions of your library, e.g. with version numbers 1.0.0-JDK7 and 1.0.0-JDK9. Then the consumer of your library can choose the right one for their purposes.

How can I execute a plug-in, declared on parent pom.xml, on a spesific child pom.xml in Maven?

I am working on a Java EE project that consists of a parent project, and a list of sub-projects (modules). I have declared and configured a plug-in on the parent project's pom.xml within a <pluginmanagement> tag as follows:
Parent pom.xml
...
<pluginManagement>
<plugins>
<!-- inmem-db-plugin -->
<plugin>
<groupId>com.btmatthews.maven.plugins.inmemdb</groupId>
<artifactId>inmemdb-maven-plugin</artifactId>
<version>${version.inmemdb-plugin}</version>
<inherited>true</inherited>
<executions>
<execution>
<id>runDB</id>
<goals>
<goal>run</goal>
</goals>
<configuration>
<monitorKey>inmemdb</monitorKey>
<monitorPort>11527</monitorPort>
<daemon>true</daemon>
<type>derby</type>
<database>localDB</database>
<username>${user}</username>
<password>${pass}</password>
<sources>
<script>
<sourceFile> create - data.sql </sourceFile>
<sourceFile> create - table.sql </sourceFile>
</script>
</sources>
</configuration>
</execution>
<execution>
<id>stopDB</id>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
...
And then I have referenced it on the child's pom.xml file:
...
<build>
<plugins>
<plugin>
<groupId>com.btmatthews.maven.plugins.inmemdb</groupId>
<artifactId>inmemdb-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
...
From what I have read on the internet, this seems to be the right way of making sure that the specific plug-in will be used only on the specific module that i have referenced it.
But, When I run the mvn install command, the plug-in that it is needed to run wont show up at all. Is there anything else that it is needed to be done, in order for the plug-in to run on a spesific module only?
Thanks in advance.
UPDATE: I replaced the wrong <phase> values with valid ones, but still when I enclose the plugin with <pluginmanagement> the plugin wont run at all.
The parent > child configuration looks correct i.e. define the plugin in pluginManagement in the parent and then engage it in the child by including it build/plugins. But this looks incorrect to me: <phase>pre-test</phase>, pre-test is not a valid phase in the Maven lifecycle and hooking your plugin up to a non existent phase will prevent it from being run. I think the correct plugin configuration is:
<plugin>
<groupId>com.btmatthews.maven.plugins.inmemdb</groupId>
<artifactId>inmemdb-maven-plugin</artifactId>
<version>${version.inmemdb-plugin}</version>
<inherited>true</inherited>
<executions>
<execution>
<id>runDB</id>
<goals>
<goal>run</goal>
</goals>
<configuration>
...
</configuration>
</execution>
</executions>
</plugin>
I suspect you might have been using the 'phases' pre-test and post-test to start and stop the in-memory DB as/when your test phase starts and stops? If so, then the correct Maven phases are:
pre-integration-test
post-integration-test

Maven aggregation build uses different java than building a child module on its own

I noticed an unexpected behavior. I have an aggregation POM for the purposes of aggregated goals execution.
When I execute the build task, the modules included in aggregation POM have their javadoc generated. The javadoc generation is defined in parent POM (not the same as aggregation POM) like this:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
The child module has its own POM, in which an aspectJ plugin is defined like this: (this is how the build tag actually looks like as a whole in the child POM:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.6</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<complianceLevel>1.7</complianceLevel>
<verbose>true</verbose>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<dependencies>
</dependencies>
</plugin>
</plugins>
</build>
Now, when I run the aggregation build, it generates javadocs with Java 1.8 (there is no mention about which java to use in aggregation POM):
Constructing Javadoc information...
Standard Doclet version 1.8.0
Building tree for all the packages and classes...
...
12 errors
16 warnings
But when I run build just on the child module, it will use Java 1.7.0_45
Constructing Javadoc information...
Standard Doclet version 1.7.0_45
Building tree for all the packages and classes...
...
2 warnings
Why this inconsistency? I would expect that the build should work the same. Am I doing something wrong? How does one configure the aggregation build to use the configuration of the child module (with its POM), which to me seems the way which should be default.
My assumption is that aggregation build is the same thing as running "install" goal on each child module manually as if on a seperate project. Is this assumption wrong? (seems like it is, since this behaviour) If yes, what is actually happening?
If information are missing (parts of pom configurations) please comment.

How to exclude java classes from being compiled in maven with annotation

I already have a working solution where I can specify with maven which classes to not compile when using a particular maven profile.
But I would like to use a general solution and use an annotation instead
The current solution that I have is like
<plugin>
<!-- Exclude some web services used only for internal testing -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<optimize>true</optimize>
<excludes>
<exclude>**/something/*ClassPattern.java</exclude>
</excludes>
<testExcludes>
<exclude>**/something/*ClassPatternTest.java</exclude>
</testExcludes>
</configuration>
</plugin>
But Some thing like
#NotCompiledForProduction
would be rather nice on top of a class.
It seems to me that this might be hard (or impossible to do) without changing maven's behaviour. That is not the scope here. And this kind of annotation
You cannot (I assume) use an annotation to determine what source code gets presented to the java compiler, because you need to compile the source code in the first place to process the annotation.
It seems like you need to create different modules in your maven project: one that generates a jar file with the production code, and one module that generates a jar file with testing implementation with a dependency on the production artifact.
If the code really does need to be in the same maven module, then the code should always be compiled. You can however use maven-jar-plugin to create multiple artifacts at the package phase: the default artifactId.jar, and an artifactId-test-lib.jar artifact. You can do this by specifying multiple executions for the plugin, and using <includes> and <excludes> to split the jar files as required.
you can try this...
<build> <plugins>
<!-- Run annotation processors on src/main/java sources -->
<plugin>
<groupId>org.bsc.maven</groupId>
<artifactId>maven-processor-plugin</artifactId>
<executions>
<execution>
<id>process</id>
<goals>
<goal>process</goal>
</goals>
<phase>generate-sources</phase>
</execution>
</executions>
</plugin>
<!-- Disable annotation processors during normal compilation -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgument>-proc:none</compilerArgument>
</configuration>
</plugin>
</plugins> </build>

Generating sources by running a project's java class in Maven

I'm converting a largish Ant build to Maven. As part of the Ant build, we have several steps which created Java classes by invoking one of the project's classes, simplified as:
javac SomeGenerator.java
java SomeGenerator generated # generate classes in generated/
javac generated/*.java
I've split each generator in its own Maven module, but I have the problem of not being able to run the generator since it's not yet compiled in the generate-sources phase.
I've tried something similar to
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.1.1</version>
<executions>
<execution>
<id>generate-model</id>
<goals>
<goal>java</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<mainClass>DTOGenerator</mainClass>
<arguments>
<argument>${model.generated.dir}</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
Which sadly does not work, for the reasons outlined above. Splitting the code generators into two projects each, one for compiling the generator and another for generating the DTOs seems overkill.
What alternatives are there?
Using Maven 2.2.1.
You can execute the maven-compile-plugin in the generate-sources phase. Just add another execution before the existing execution and configure it so that it just picks up the sources for the generator.
Or split the project in two: build the generator with a separate POM and include the generator library as a dependency to the POM that's generating the sources.
Personally I would split the project. Keeps the build files cleaner and easier to maintain.
I didn't want to have 2 different projects, so I tried to setup Maven for adding the generated compiled code to the final jar package.
This is the working solution I've used:
In process-classes phase (executed just after the compile phase):
exec-maven-plugin for executing a main class able to generate my source files in target/generated-sources/java folder (in my specific case I used the Roaster library for source code generation);
build-helper-maven-plugin for adding the generated sources in the correct location
In prepare-package phase:
maven-compiler-plugin, in order to detect the changes and recompile the module
maven-jar-plugin for producing the jar package
This is my pom.xml:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>com.example.MyClassWriter</mainClass>
<arguments>
<argument>${project.basedir}</argument>
<argument>${project.build.directory}</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.build.directory}/generated-sources/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<executions>
<execution>
<phase>prepare-package</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<phase>prepare-package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
In order to do this in one project, there are 3 steps:
Compile generator code
We can do it in generate-sources phase, using maven-compiler-plugin. You can also exclude other source files.
Run generator to generate code
We can do it in process-sources phase, using exec-maven-plugin.
Compile project
Below is the key part of pom.xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
<executions>
<execution>
<id>compile-generator</id>
<phase>generate-sources</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<includes>
<include>source/file/of/generator/*.java</include>
</includes>
<excludes>
<exclude>other/source/files/*.java</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>generate-codes</id>
<goals>
<goal>java</goal>
</goals>
<phase>process-sources</phase>
<configuration>
<mainClass>your.main.class.of.generator</mainClass>
</configuration>
</execution>
</executions>
</plugin>
We faced the same problem. We wanted to respect Maven's behavior as closely as possible, to have no problems with plugins and so on... Fighting Maven is just too expensive!
We realized that the update frequency of the generated code was usually very different from the one of the code that we manually write, so separating the code had very good performance characteristics for the build. So we accepted to have our generated classes as a dependency of the manually written.
We adopted the following structure, that had just one little change from a regular maven config, a change in the source directory.
Parent project : Generations
We created a parent project for all our generations.
It has a JAR type if it contains code to be compiled, otherwise POM.
There we have our generating code, in /src.
It can compile in /target as usual.
It runs the generation, each generator producing code in a sub-directory of /target.
Note: if you want several generated results in the same jar, just put them in the same sub-directory.
Child jar projects : Generateds
It is a subdirectory of the Generations project.
It has a JAR type.
The source directory points to the sub-directory in the parent's target.
<sourceDirectory>../target/generated1</sourceDirectory>
It compiles normally in its own /target directory.
That structure allows us to :
have as little modification to the standard maven layout as possible, so every maven command and plugin keeps working nicely.
scale nicely if you have several generators,
scale nicely if you want to generate several jars (we had a case wsdl2java where one generator produced code that should be split into several jars ; each child generated project would have the same source directory, but would be configured with an <includes> to handle only some of the classes).
I posted a minimal working setup here https://github.com/baloise/inlinesourcecodegenerator
It uses build-helper compiler and exec plugins and has all code in the same project.

Categories