9

I have a question regarding how maven calculates the classpath during building. Specifically, what controls when the "target/classes" is used and when the "jar" from a repository (local/remote) is used.

I have a project on version 1.0.0-SNAPSHOT where the artifacts have NOT been installed/deployed so there is no "jar" in some repository (remote or local) to resolve them. I want to run "generate-sources" WITHOUT installing locally (no 'mvn install' run).

The structure looks like this:

parent-prj
parent-prj/sub-prj
parent-prj/gen-src-prj <--- This depends on 'sub-prj'

When I run "mvn -am -pl parent-prj/gen-src-prj generate-sources" in order to just generate some java files, it does not work:

[ERROR] Failed to execute goal on project gen-src-prj: Could
 not resolve dependencies for project 
mygrp:gen-src-prj:jar:1.0.0-SNAPSHOT: 
Could not find artifact 
mygrp:sub-prj:jar:1.0.0-SNAPSHOT -> [Help 1]

Using debug output and adding "dependency:build-classpath" I can confirm that maven ignores the presence of "sub-prj" in the reactor and looks for a "jar" somewhere which it can't find. Yet the project is printed in the reactor summary:

[INFO] Reactor Summary:
[INFO] 
[INFO] parent-prj ..................................... SUCCESS [  0.625 s]
[INFO] sub-prj ........................................ SUCCESS [  0.018 s]
[INFO] gen-src-prj .................................... FAILURE [  0.040 s]

The interesting thing I noticed is that running the compile goal works fine! This uses sub-prj/target/classes (as shown by dependency:build-classpath) and has no trouble generating the sources and even compiling them: "mvn -am -pl parent-prj/gen-src-prj compile"

So here are the points I want to understand:

  1. Why does the compile goal work but the generate-sources doesn't work?
  2. At what point does maven decide to use the output folder of previous projects on the reactor classpath instead of looking for a jar?
  3. Is there a way for generate-sources to run directly as I want it EVEN WITHOUT having its dependencies resolved?

Regarding (3) my generation tool is a utility invoked by:

    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>1.6.0</version>
        <executions>
            <execution>
                <phase>generate-sources</phase>
                <goals>
                    <goal>java</goal>
                </goals>
            </execution>
        </executions>

The tool reads some XML in src/main/resources and generates Java files and does NOT need anything in its class-path (so there is no need for maven to resolve it).

Also note that I would be interested to understand (1) and (2) even if a solution for (3) is provided.


EDIT: Per comment request, adding full example

parent-prj/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>mygrp</groupId>
    <artifactId>parent-prj</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <packaging>pom</packaging>

    <modules>
        <module>sub-prj</module>
    <module>gen-src-prj</module>
    </modules>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.3</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.9</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

parent-prj/sub-prj/pom.xml

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
    xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>mygrp</groupId>
        <artifactId>parent-prj</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <artifactId>sub-prj</artifactId>
</project>

parent-prj/gen-src-prj/pom.xml

<?xml version="1.0" encoding="UTF-8"?> <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 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>mygrp</groupId>
        <artifactId>parent-prj</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <artifactId>gen-src-prj</artifactId>

    <dependencies>
        <dependency>
            <groupId>mygrp</groupId>
            <artifactId>sub-prj</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.6.0</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <includeProjectDependencies>false</includeProjectDependencies>
                    <includePluginDependencies>true</includePluginDependencies>
                    <mainClass>uk.co.real_logic.sbe.SbeTool</mainClass>
                    <systemProperties>
                        <systemProperty>
                            <key>sbe.output.dir</key>
                            <value>${project.build.directory}/generated-sources/java</value>
                        </systemProperty>
                        <systemProperty>
                            <key>sbe.validation.warnings.fatal</key>
                            <value>true</value>
                        </systemProperty>
                    </systemProperties>
                    <arguments>
                        <argument>${project.build.resources[0].directory}/Examples.xml</argument>
                    </arguments>
                    <workingDirectory>${project.build.directory}/generated-sources/java</workingDirectory>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>uk.co.real-logic</groupId>
                        <artifactId>sbe-tool</artifactId>
                        <version>1.7.10</version>
                    </dependency>
                </dependencies>
            </plugin>

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <id>add-source</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>target/generated-sources/java/</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

EDIT: Armed with the knowledge from the answers I have come up with this workaround that allows one to achieve the desired behaviour. I list the dependencies in a profile that is active by default, then use another profile to run generate-sources with no dependencies active, like follows:

parent-prj/gen-src-prj/pom.xml

<profiles>
    <profile>
        <id>default</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <dependencies>
            <dependency>
                <groupId>mygrp</groupId>
                <artifactId>sub-prj</artifactId>
                <version>${project.version}</version>
            </dependency>
        </dependencies>
    </profile>

    <profile>
        <id>excludeDependency</id>
        <dependencies>
        </dependencies>
    </profile>
</profiles>

To generate sources with above, use: mvn -PexcludeDependency generate-sources

Alexandros
  • 2,097
  • 20
  • 27
  • First it would be helpful having a full project which shows the behaviour you are describing... – khmarbaise May 11 '18 at 08:57
  • Added the POM files. Make sure you "touch gen-src-prj/src/main/resources/Examples.xml" and also do NOT run 'mvn install' – Alexandros May 11 '18 at 09:14
  • You can try simple "mvn generate-sources" versus "mvn compile". Former does not works, latter does work fine. – Alexandros May 11 '18 at 09:18

2 Answers2

5

Maven can reference only output generated in current Session (during currently executing shell command). It uses the most "mature" place to look for the "output":

  • If compile is run - the classes end up in the target/classes dir, thus other modules can reference that
  • If package is run - then target/*.jar is created and this jar file ends up in the classpath instead
  • If install is run - then jar file ends up in the local repository - which is what ends up on the classpath

So there are 3 factors that impede your task:

  • maven-exec-plugin requires dependency resolution (as pointed out by @mondaka)
  • Your module1 references module2
  • generate-sources is run before the compilation. Thus module2 is not yet prepared to be used as a dependency.

So if you want to do it your way - you'll have to run at least compile phase each time you use anything from the Default Lifecycle. Or you could write your own plugin that doesn't require dependency resolution.

Koray Tugay
  • 22,894
  • 45
  • 188
  • 319
Stanislav Bashkyrtsev
  • 14,470
  • 7
  • 42
  • 45
  • While I understand this is debatable (whether it is a bug or not) I decided to accept modaka's answer because it explains why the behaviour is manifested (requiresDependencyResolution setting). – Alexandros May 20 '18 at 07:01
  • But generate-source phase is executed too when you run "mvn compile". The question is: Why it does not fail in this situation? – mondaka May 20 '18 at 13:23
  • 1
    Because first all the commands are executed for the 1st module in the reactor, then for the 2nd module and so on. When you run `compile` - 1st module winds up with the target/classes, and _only after that_ 2nd module kicks in and it sees those classes. – Stanislav Bashkyrtsev May 20 '18 at 14:57
  • Ok, thats absolutly true. But probably the plugins maven architecture should have an alternative to this in order to do it configurable (with a property like includeProjectDependencies). – mondaka May 21 '18 at 07:34
  • There are alternatives when _writing_ a plugin - don't use `requiresDependencyResolution` at all or replace it with `requireDependencyCollection`. But this particular plugin chose not to change anything. Probably because it does require dependency resolution. So if you choose to use a plugin that requires dependency resolution, you should bind this plugin to phases where this mechanism is available. If you want Maven to make this a configuration option - you can open a feature request or send them a PR. – Stanislav Bashkyrtsev May 21 '18 at 07:40
  • I know that. This particular plugin probabily have chosen this alternative because some "exec:java" executions need it (for example if you need to execute a java main that have any depency with another class in your project). But although this option is configurable, you can't disable completly de project dependency resolution (witch fails in early phases). – mondaka May 21 '18 at 08:08
3

This problem is related to an open maven bug:

https://issues.apache.org/jira/browse/MNG-3283

The issue says: "The problem only occurs when a plugin binds itself to the generate-sources phase and has @requiresDependencyResolution".

I have checked that exec-maven-plugin Mojo have indeed requiresDependencyResolution = ResolutionScope.TEST. You can see that on https://github.com/mojohaus/exec-maven-plugin/blob/master/src/main/java/org/codehaus/mojo/exec/ExecJavaMojo.java

Then, your only option is to use compile or process-classes phases. This is a Major open bug from 2007...

mondaka
  • 362
  • 3
  • 12
  • 1
    I wouldn't say it's a bug (even though the reporter thought so). The behaviour makes perfect sense. Dependencies simply can't be resolved at early phases. – Stanislav Bashkyrtsev May 19 '18 at 23:59
  • Its a bug because it work correctly if you target a later phase at the outset. That is: maven phase generate-source fails when you target generate-source phase. But the generate-source phase doesn't fail if you target the compile phase... although we know that the generate-source phase is executed when you target the compile phase and this is really confusing. – mondaka May 20 '18 at 13:02
  • But it fails not because there are issues in plugin. It fails because users configure this plugin on the phases where it's not supposed to work. – Stanislav Bashkyrtsev May 20 '18 at 15:00
  • But this plugin have not any restriction about any phase. And in this example has been configured with false – mondaka May 21 '18 at 07:17
  • Do you know a way to restrict a plugin from running on some phases? I don't. Also, the option that you mentioned doesn't relate to this topic - it's about whether to include project dependencies into classpath of the plugin. – Stanislav Bashkyrtsev May 21 '18 at 07:36
  • I think if such a plugin have a property to provide the option to not include project dependencies, then the plugins users will think that the dependency will not be resolved during the plugin execution. If that happens, it is a little bit confusing (ofc, you can use the plugin documentation to explain this). – mondaka May 21 '18 at 08:00