Maven release plugin: specify java compiler version

前端 未结 4 1269
清酒与你
清酒与你 2020-12-30 05:01

I have this project made of multiple jars and war to make an ear. I build everything in snapshot and it works great. Then I made a release for every single project and saw t

相关标签:
4条回答
  • 2020-12-30 05:13

    --- SPOILER ALERT ---

    The short answer is that in order to compile source to an older version, you need to supply both the -source option as well as the -bootclasspath. See this article. And if you want to compile source to a newer version, you need to set <source>, <target>, <compilerVersion>, <fork>, and <executable> on the compiler plugin, and set <jvm> on the surefire plugin...

    And now for the story...

    I ran into the same problem. It turns out that compiling a previous version may not be as easy as setting <source> and <target>. My specific case is that I have a Java 1.7 JDK and I my class has an incompatibility with 1.7 (they added a new method to an interface that I am implementing). When I tried to compile it, the compiler gave me an error message stating that I had not implemented the interface method. Anyway, I tried setting the compiler plugin:

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>
    

    but when I ran the build, I got the same error. So I ran maven in debug and saw this:

    [INFO] [DEBUG] Command line options:
    [INFO] [DEBUG] -d C:\... -nowarn -target 1.6 -source 1.6 -encoding UTF-8
    

    Note that the ... is put in place of actual arguments for the sake of brevity

    in the output. The message starting with -d is the actual full compile argument list. So, if you remove the -nowarn flag and paste the rest after javac on the command line you can see the actual output from the compiler:

    javac -d C:\... -target 1.6 -source 1.6 -encoding UTF-8
    warning: [options] bootstrap class path not set in conjunction with -source 1.6
    

    This prints out the handy-dandy warning bootstrap class path not set in conjunction with -source 1.6. A little googling on that turns up this article which states:

    To use javac from JDK N to cross-compiler to an older platform version, the correct practice is to:

    • Use the older -source setting.
    • Set the bootclasspath to compile against the rt.jar (or equivalent) for the older platform.

    If the second step is not taken, javac will dutifully use the old language rules combined with new libraries, which can result in class files that do not work on the older platform since references to non-existent methods can get included.

    Now referencing the maven documentation for the compiler plugin gives:

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.5.1</version>
        <configuration>
          <compilerArguments>
            <verbose />
            <bootclasspath>${java.home}\lib\rt.jar</bootclasspath>
          </compilerArguments>
        </configuration>
      </plugin>
    

    Which you can then combine with your earlier configuration to get:

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
          <encoding>UTF-8</encoding>
          <bootclasspath>${java.home}\lib\rt.jar</bootclasspath>
        </configuration>
      </plugin>
    

    And now you just need to make the ${java.home} variable available to your mvn (through -D system properties, or through plain old environment variables, or you could get really fancy and stuff it in a java 6 profile in your user settings).

    Now just run your build and go grab a cold beer while it chugs away...

    ---- EDIT ----

    One last thing... Including rt.jar in your bootclasspath is always required, however, I discovered that more may be needed on a case by case basis. I had to include jce.jar (in the same directory as rt.jar) because my app was doing crypto work.

    ---- EDIT 2 ----

    For grins, I tried it the other direction. Instead of running maven with java 7 compiling for java 6, I ran maven with java 6 compiling for java 7. The first attempt is pretty straight forward:

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.5.1</version>
        <configuration>
          <source>1.7</source>
          <target>1.7</target>
          <fork>true</fork>
          <verbose>true</verbose>
          <compilerVersion>1.7</compilerVersion>
          <executable>${JAVA_7_HOME}/bin/javac</executable>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>
    

    Basically, I set my <source> and <target> to 1.7, but that clearly would not be enough because 6 cant compile 7 code. So back to the compiler plugin, there is actually an example page describing what needs to be done. Namely, you need to <fork> off a new process using the java 7 <executable>. So now I think I'm all set. Time to fire up the build...

    C:\Projects\my-project>mvn package
    ...
    Caused by: java.lang.UnsupportedClassVersionError: mypackage.StupidTest : Unsup
    ported major.minor version 51.0
    ...
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD FAILURE
    [INFO] ------------------------------------------------------------------------
    

    What the heck is UnsupportedClassVersionError? A closer look tells us its the maven-surefire-plugin that is failing. So I try just mvn compile and sure enough I get a success as the surefire plugin never fired up. So I run mvn -X package and notice this gem:

    Forking command line: cmd.exe /X /C ""C:\Program Files\Java\jdk1.6.0_29\jre\bin\
    java" -jar C:\Projects\my-project\target\surefire\surefirebooter2373372991878002
    398.jar C:\Projects\my-project\target\surefire\surefire1861974777102399530tmp C:
    \Projects\my-project\target\surefire\surefire4120025668267754796tmp"
    

    Ok, so its running java 6. Why? The documentation for surefire gives this:

    jvm:
    Option to specify the jvm (or path to the java executable) to use with the forking 
    options. For the default, the jvm will be a new instance of the same VM as the one 
    used to run Maven. JVM settings are not inherited from MAVEN_OPTS.
    

    Since we ran mvn with a java 6 VM its forking a java 6 VM for its unit tests. So setting this option appropriately:

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.12</version>
        <configuration>
          <jvm>${JAVA_7_HOME}/bin/java</jvm>
        </configuration>
      </plugin>
    

    And firing up the build...

    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    
    0 讨论(0)
  • 2020-12-30 05:20

    You can verify the version a class is compiled for by using the javap utility included with the JDK. From the command line:

    javap -verbose MyClass
    

    In the output of javap look for "minor version" and "major version" about ten lines down.

    Then use this table:

    major       minor       Java platform version
    45          3           1.0
    45          3           1.1
    46          0           1.2
    47          0           1.3
    48          0           1.4
    49          0           1.5
    50          0           1.6
    

    The .class size difference may be due to compile version but also may be due to other compile options like compiling with debug information (line numbers in stacktraces).

    0 讨论(0)
  • 2020-12-30 05:38

    Just a note, if a wrong version is picked up, you will get an error something like

    xxxx is not supported in -source 1.5
    [ERROR] (use -source 7 or higher to enable yyyy)
    

    During maven release, maven release plugin (sets/defaults to) the compiler version 1.5. To ensure it picks up the correct version specify the properties.

    <properties>
     <maven.compiler.source>1.7</maven.compiler.source>
     <maven.compiler.target>1.7</maven.compiler.target>
    </properties>
    
    0 讨论(0)
  • 2020-12-30 05:39

    Could it be that the release plugin is compiling in 1.6 or other, explaining the classes size difference ?

    Can't be IMO. The Maven Release Plugin doesn't compile anything, it just triggers a phase that will itself trigger the compile phase and the Maven Compiler Plugin. In other words, the Maven Compiler Plugin and its settings will be used.

    You can use the following commands to control what is happening exactly:

    mvn help:active-profiles -Prelease
    

    to check profiles. And

    mvn help:effective-pom -Prelease
    

    to check the effective pom.

    0 讨论(0)
提交回复
热议问题