mkarneim / pojobuilder

A Java Code Generator for Pojo Builders
Other
334 stars 44 forks source link

Mixed maven scala/java build fails if java sources refer to Scala classes #136

Closed andrea-rockt closed 7 years ago

andrea-rockt commented 7 years ago

PojoBuilder fails if java sources refer to Scala classes built in same project (The java source file where it is failing contains no @GeneratePojoBuilder annotation.

Maven phases:

generate-sources => here pojo builder scans the java sources process-resources => here scala compiles mixed java and scala project compile => here javac compiles the rest

Moving scala compilation to the initialize phase works (pojo builder has all the scala built classes) but i'm not able to use generated sources from scala

I don't know if processing sources with missing types is an annotation processor limitation or a PojoBuilder one.

The maven configuration for a mixed scala/java project i'm using is this one:

<plugin>
    <groupId>net.alchim31.maven</groupId>
    <artifactId>scala-maven-plugin</artifactId>
    <version>3.2.2</version>
    <inherited>true</inherited>
    <executions>
        <execution>
            <id>scala-compile-first</id>
            <phase>process-resources</phase>
            <goals>
                <goal>add-source</goal>
                <goal>compile</goal>
            </goals>
        </execution>
        <execution>
            <id>scala-test-compile</id>
            <phase>process-test-resources</phase>
            <goals>
                <goal>testCompile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

This is the configuration i'm using to perform scala build in initialize phase as a workaround

<plugin>
    <groupId>net.alchim31.maven</groupId>
    <artifactId>scala-maven-plugin</artifactId>
    <version>3.2.2</version>
    <inherited>true</inherited>
    <executions>
        <execution>
            <id>scala-compile-first</id>
            <phase>initialize</phase>
            <goals>
                <goal>add-source</goal>
                <goal>compile</goal>
            </goals>
        </execution>
        <execution>
            <id>scala-test-compile</id>
            <phase>process-test-resources</phase>
            <goals>
                <goal>testCompile</goal>
            </goals>
        </execution>
    </executions>
</plugin>
mkarneim commented 7 years ago

Hi Andrea,

I am just a rookie with Maven and Scala, but I managed to set up a small Maven project having Java and Scala classes. Since I could successfully compile it, I can't reproduce your issue.

Here is my setup:

pom.xml

<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>net.karneim</groupId>
  <artifactId>pojobuilder-samples</artifactId>
  <version>1-SNAPSHOT</version>
  <packaging>jar</packaging>
  <name>PojoBuilder samples</name>

  <description>Sample usages of PojoBuilder</description>

  <dependencies>
    <dependency>
      <groupId>net.karneim</groupId>
      <artifactId>pojobuilder</artifactId>
      <version>[3.4.2,)</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-library</artifactId>
      <version>2.11.7</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <!-- If you need to keep the generated sources in a specific directory
        outside of the 'target' directory, then configure the 'generatedSourcesDirectory'
        of the 'maven-compiler-plugin'. Otherwise just remove this 'plugin' element
        completely. -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
          <generatedSourcesDirectory>${basedir}/src/generated/java</generatedSourcesDirectory>
        </configuration>
      </plugin>
      <plugin>
        <groupId>net.alchim31.maven</groupId>
        <artifactId>scala-maven-plugin</artifactId>
        <version>3.2.2</version>
        <inherited>true</inherited>
        <executions>
            <execution>
                <id>scala-compile-first</id>
                <phase>process-resources</phase>
                <goals>
                    <goal>add-source</goal>
                    <goal>compile</goal>
                </goals>
            </execution>
            <execution>
                <id>scala-test-compile</id>
                <phase>process-test-resources</phase>
                <goals>
                    <goal>testCompile</goal>
                </goals>
            </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

src/main/java/sample/Contact.java

package sample;

import net.karneim.pojobuilder.GeneratePojoBuilder;

@GeneratePojoBuilder
public class Contact {
    public String name;
    public int age;

    public void sayHello(String[] args) {
      HelloWorld.main(args);
    }
}

src/main/java/sample/CallScala.java

package sample;

public class CallScala {
  public static void main(String[] args) {
    HelloWorld.main(args);
    Contact c = new Contact();

  }
}

src/main/scala/HelloWorld.scala

package sample {
  object HelloWorld {
    def main(args: Array[String]): Unit = {
      println("Hello, world!")
    }
  }
}

In the terminal I just executed this:

mvn clean compile
andrea-rockt commented 7 years ago

Hi Michael,

Thank you for your time.

I forgot to mention that i'm triggering annotation processing via maven-processor plugin (disabling javac own annotation processing round).

With this setup annotation processing is happening in generate-sources phase (before scala sources are compiled).

I will edit your minimal example and try to reproduce the error (maybe creating a new repository that you can clone)

andrea-rockt commented 7 years ago

Hi,

here you can find the repo where i managed to reproduce the issue:

https://github.com/andrea-rockt/pojo-builder-issue

changing

<id>scala-compile-first</id>
<phase>process-resources</phase>

to

<id>scala-compile-first</id>
<phase>initialize</phase>

works

mkarneim commented 7 years ago

I just forked your reproducer, but it did not work out-of-the-box (either way).

In both cases I got the following error:

[ERROR] diagnostic: pojo-builder-issue/src/main/java/sample/CallScala.java:3: 
error: package sample.anothernamespace does not exist
import sample.anothernamespace.HelloWorld;

So I fixed the source code (please see https://github.com/mkarneim/pojo-builder-issue/tree/fix). Now it compiles, but, as you said, only if scala-compile-first is set to initialize. If you set it to process-resources you get the following error:

[ERROR] Failed to execute goal org.bsc.maven:maven-processor-plugin:3.1.0:process (process-messages) on project pojobuilder-samples: Error executing: error during compilation -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.bsc.maven:maven-processor-plugin:3.1.0:process (process-messages) on project pojobuilder-samples: Error executing
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:212)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:307)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:193)
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:106)
    at org.apache.maven.cli.MavenCli.execute(MavenCli.java:863)
    at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:288)
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:199)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
Caused by: org.apache.maven.plugin.MojoExecutionException: Error executing
    at org.bsc.maven.plugin.processor.AbstractAnnotationProcessorMojo.execute(AbstractAnnotationProcessorMojo.java:371)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:134)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:207)
    ... 20 more
Caused by: java.lang.Exception: error during compilation
    at org.bsc.maven.plugin.processor.AbstractAnnotationProcessorMojo.executeWithExceptionsHandled(AbstractAnnotationProcessorMojo.java:628)
    at org.bsc.maven.plugin.processor.AbstractAnnotationProcessorMojo.execute(AbstractAnnotationProcessorMojo.java:363)
    ... 22 more

And that's bad, since the error message is not very helpful.

Well, since I am no Maven guy, I have no understanding about the difference between process-resources and initialize. What exactly does that mean? Can't you just go with initialize, since this is working?

However, given you really needed process-resources (for whatever reason), I wanted to know if the problem is PojoBuilder-specific. To check that I removed all the PojoBuilder stuff and added Google's Auto-Value instead (please see https://github.com/mkarneim/pojo-builder-issue/tree/auto-value). And voilá, the problem still persists. And again you can solve it by setting scala-compile-first to initialize.

So this means this issue is not PojoBuilder-specific but rather is caused by something else. I guess I can't really help you with this...

Comments?

andrea-rockt commented 7 years ago

I agree with you that the issue is non pojobuilder specific (sorry for wasting your time) but maybe this issue could serve as a reference to others with the same problem.

Thanks again for your time

I just forked your reproducer, but it did not work out-of-the-box (either way). [ERROR] diagnostic: pojo-builder-issue/src/main/java/sample/CallScala.java:3: error: package sample.anothernamespace does not exist import sample.anothernamespace.HelloWorld;

This error says that Annotation Processing failed in CallScala.java during annotation processing, the reason i opened a issue here is that CallScala should not be processed (no pojobuilder annotation, so i thought that pojobuilder was performing something strange parsing imports).

Annotation processing should take place in the generate-sources phase, before scala and java sources get compiled, i cannot go with initialize because then i will not have generated builders available from the scala side.

Maybe the problem is in the annotation processing framework (maybe it cannot deal with imports from scala side when processing annotations before class files are available)

one of the examples shows that this is not working even if i perform the add-sources goal of scala compiler plugin in the initialize phase and compile it in the generate-resources phase (an hack to force scala sources to be built before java sources, the scala compiler understands java source files but not the other way around)

Here is the maven lifecycle for reference:

validate

validate the project is correct and all necessary information is available.

initialize

initialize build state, e.g. set properties or create directories.

generate-sources

generate any source code for inclusion in compilation.

process-sources

process the source code, for example to filter any values.

generate-resources

generate resources for inclusion in the package.

process-resources

copy and process the resources into the destination directory, ready for packaging.

compile

compile the source code of the project.

process-classes

post-process the generated files from compilation, for example to do bytecode enhancement on Java classes.

generate-test-sources

generate any test source code for inclusion in compilation.

process-test-sources

process the test source code, for example to filter any values.

generate-test-resources

create resources for testing.

process-test-resources

copy and process the resources into the test destination directory.

test-compile

compile the test source code into the test destination directory

process-test-classes

post-process the generated files from test compilation, for example to do bytecode enhancement on Java classes. For Maven 2.0.5 and above.

test

run tests using a suitable unit testing framework. These tests should not require the code be packaged or deployed.

prepare-package

perform any operations necessary to prepare a package before the actual packaging. This often results in an unpacked, processed version of the package. (Maven 2.1 and above)

package

take the compiled code and package it in its distributable format, such as a JAR.

pre-integration-test

perform actions required before integration tests are executed. This may involve things such as setting up the required environment.

integration-test

process and deploy the package if necessary into an environment where integration tests can be run.

post-integration-test

perform actions required after integration tests have been executed. This may including cleaning up the environment.

verify

run any checks to verify the package is valid and meets quality criteria.

install

install the package into the local repository, for use as a dependency in other projects locally.

deploy

done in an integration or release environment, copies the final package to the remote repository for sharing with other developers and projects.

pom-notworking-addsources-before.xml

[INFO] ------------------------------------------------------------------------
[INFO] Building PojoBuilder samples 1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ pojobuilder-samples ---
[INFO] Deleting D:\github\andrea-rockt\pojo-builder-issue\target
[INFO]
[INFO] --- scala-maven-plugin:3.2.2:add-source (scala-initialize-first) @ pojobuilder-samples ---
[INFO] Add Source directory: D:\github\andrea-rockt\pojo-builder-issue\src\main\scala
[INFO] Add Test Source directory: D:\github\andrea-rockt\pojo-builder-issue\src\test\scala
[INFO]
[INFO] --- maven-processor-plugin:3.1.0:process (process-messages) @ pojobuilder-samples ---
[INFO] diagnostic: Note: [PojoBuilder] Started
[INFO] diagnostic: Note: [PojoBuilder] Processing annotations (round 1)
[INFO] diagnostic: Note: [PojoBuilder] Generated class sample.ContactBuilder
[INFO] diagnostic: Note: [PojoBuilder] Processing annotations (round 2)
[ERROR] diagnostic: D:\github\andrea-rockt\pojo-builder-issue\src\main\java\sample\CallScala.java:3: error: package sample.anothernamespace does not exist
import sample.anothernamespace.HelloWorld;
                              ^
[ERROR] diagnostic: D:\github\andrea-rockt\pojo-builder-issue\src\main\java\sample\Contact.java:4: error: package sample.anothernamespace does not exist
import sample.anothernamespace.HelloWorld;
                              ^
[INFO] diagnostic: Note: [PojoBuilder] Finished (70 ms)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------

pom-notworking-withpojobuilder.xml

[INFO] ------------------------------------------------------------------------
[INFO] Building PojoBuilder samples 1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ pojobuilder-samples ---
[INFO] Deleting D:\github\andrea-rockt\pojo-builder-issue\target
[INFO]
[INFO] --- maven-processor-plugin:3.1.0:process (process-messages) @ pojobuilder-samples ---
[INFO] diagnostic: Note: [PojoBuilder] Started
[INFO] diagnostic: Note: [PojoBuilder] Processing annotations (round 1)
[INFO] diagnostic: Note: [PojoBuilder] Generated class sample.ContactBuilder
[INFO] diagnostic: Note: [PojoBuilder] Processing annotations (round 2)
[ERROR] diagnostic: D:\github\andrea-rockt\pojo-builder-issue\src\main\java\sample\CallScala.java:3: error: package sample.anothernamespace does not exist
import sample.anothernamespace.HelloWorld;
                              ^
[ERROR] diagnostic: D:\github\andrea-rockt\pojo-builder-issue\src\main\java\sample\Contact.java:4: error: package sample.anothernamespace does not exist
import sample.anothernamespace.HelloWorld;
                              ^
[INFO] diagnostic: Note: [PojoBuilder] Finished (70 ms)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------

pom-working-nopojobuilder.xml

[INFO] ------------------------------------------------------------------------
[INFO] Building PojoBuilder samples 1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ pojobuilder-samples ---
[INFO] Deleting D:\github\andrea-rockt\pojo-builder-issue\target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ pojobuilder-samples ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\github\andrea-rockt\pojo-builder-issue\src\main\resources
[INFO]
[INFO] --- scala-maven-plugin:3.2.2:add-source (scala-compile-first) @ pojobuilder-samples ---
[INFO] Add Source directory: D:\github\andrea-rockt\pojo-builder-issue\src\main\scala
[INFO] Add Test Source directory: D:\github\andrea-rockt\pojo-builder-issue\src\test\scala
[INFO]
[INFO] --- scala-maven-plugin:3.2.2:compile (scala-compile-first) @ pojobuilder-samples ---
[INFO] D:\github\andrea-rockt\pojo-builder-issue\src\main\java:-1: info: compiling
[INFO] D:\github\andrea-rockt\pojo-builder-issue\src\main\scala:-1: info: compiling
[INFO] Compiling 3 source files to D:\github\andrea-rockt\pojo-builder-issue\target\classes at 1498550130125
[INFO] prepare-compile in 0 s
[INFO] compile in 1 s
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ pojobuilder-samples ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 2 source files to D:\github\andrea-rockt\pojo-builder-issue\target\classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

pom-working-scala-initialize.xml

[INFO] ------------------------------------------------------------------------
[INFO] Building PojoBuilder samples 1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ pojobuilder-samples ---
[INFO] Deleting D:\github\andrea-rockt\pojo-builder-issue\target
[INFO]
[INFO] --- scala-maven-plugin:3.2.2:add-source (scala-compile-first) @ pojobuilder-samples ---
[INFO] Add Source directory: D:\github\andrea-rockt\pojo-builder-issue\src\main\scala
[INFO] Add Test Source directory: D:\github\andrea-rockt\pojo-builder-issue\src\test\scala
[INFO]
[INFO] --- scala-maven-plugin:3.2.2:compile (scala-compile-first) @ pojobuilder-samples ---
[INFO] D:\github\andrea-rockt\pojo-builder-issue\src\main\java:-1: info: compiling
[INFO] D:\github\andrea-rockt\pojo-builder-issue\src\main\scala:-1: info: compiling
[INFO] Compiling 3 source files to D:\github\andrea-rockt\pojo-builder-issue\target\classes at 1498550176932
[INFO] prepare-compile in 0 s
[INFO] compile in 1 s
[INFO]
[INFO] --- maven-processor-plugin:3.1.0:process (process-messages) @ pojobuilder-samples ---
[INFO] diagnostic: Note: [PojoBuilder] Started
[INFO] diagnostic: Note: [PojoBuilder] Processing annotations (round 1)
[INFO] diagnostic: Note: [PojoBuilder] Generated class sample.ContactBuilder
[INFO] diagnostic: Note: [PojoBuilder] Processing annotations (round 2)
[INFO] diagnostic: Note: [PojoBuilder] Finished (80 ms)
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ pojobuilder-samples ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\github\andrea-rockt\pojo-builder-issue\src\main\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ pojobuilder-samples ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 3 source files to D:\github\andrea-rockt\pojo-builder-issue\target\classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

Another try i made is to use AutoValue (as you suggested). Same problem as pojo builder, i'm convinced too that this is annotation processing specific and not PojoBuilder specific. Thank you.

pom-notworking-withautovalue.xml

[INFO] ------------------------------------------------------------------------
[INFO] Building PojoBuilder samples 1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ pojobuilder-samples ---
[INFO] Deleting D:\github\andrea-rockt\pojo-builder-issue\target
[INFO]
[INFO] --- maven-processor-plugin:3.1.0:process (process-messages) @ pojobuilder-samples ---
[ERROR] diagnostic: D:\github\andrea-rockt\pojo-builder-issue\src\main\java\sample\CallScala.java:3: error: package sample.anothernamespace does not exist
import sample.anothernamespace.HelloWorld;
                              ^
[ERROR] diagnostic: D:\github\andrea-rockt\pojo-builder-issue\src\main\java\sample\Contact.java:4: error: package sample.anothernamespace does not exist
import sample.anothernamespace.HelloWorld;
                              ^
[ERROR] error on execute: use -X to have details
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.690 s
[INFO] Finished at: 2017-06-27T10:21:18+02:00
[INFO] Final Memory: 32M/1963M
[INFO] ------------------------------------------------------------------------
mkarneim commented 7 years ago

Alright, thank you for clarification. You said:

Annotation processing should take place in the generate-sources phase, before scala and java sources get compiled, i cannot go with initialize because then i will not have generated builders available from the scala side.

I think the facts are different:

andrea-rockt commented 7 years ago

maven-processor-plugin is spinning up javac only to perform annotation processing in generate-sources phase (also this allow me to have a great integration with eclipse via m2e).

An interesting experiment would be to try eclipse jdt compiler (that should be more permissive about not well formed code) and see if annotation processing in this scenario works.

Having developed annotation processors before i'm aware of the processing rounds but i thought that the annotation processing could cope with incomplete symbols (and that the round mechanism was inspired by this) but never tried it myself.

i'll give up for now.

Thanks for this great library and for your support

shatestest commented 5 years ago

I have scala project , wanted to convert into java...but few classes i wanted to kept as is ...i.e. com/main/scala/* ...i followed above instruction but my scala classes are not been compiled.

chansonzhang commented 3 years ago

Hi,

here you can find the repo where i managed to reproduce the issue:

https://github.com/andrea-rockt/pojo-builder-issue

changing

<id>scala-compile-first</id>
<phase>process-resources</phase>

to

<id>scala-compile-first</id>
<phase>initialize</phase>

works

In my situation, replace <phase>compile</phase> with <phase>initialize</phase> works.