Плагин релиза Maven: укажите версию java-компилятора

У меня есть этот проект из нескольких банок и войны, чтобы сделать ухо. Я создаю все в моментальном снимке, и он отлично работает. Затем я сделал выпуск для каждого отдельного проекта и увидел, что банки и война немного отличаются по размеру, чем снимки.

Сравнивая файл с файлом, я понял, что все файлы .class находятся там, но немного больше или больше, не более 40 байтов.

Я могу заставить компиляцию использовать java 1.5, используя этот тег в Maven:

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target> 
                </configuration>

Я использую этот тег для плагина выпуска:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-release-plugin</artifactId>
    <version>2.0</version>
    <configuration>
        <releaseProfiles>release</releaseProfiles>
        <goals>deploy</goals>
    </configuration>
</plugin>

Может ли быть, что плагин release компилируется в версии 1.6 или другой, объясняя разницу в размерах классов? Если это так, я могу сделать плагин release скомпилирован в 1.5?

Спасибо за ваш вклад.

Ответы

Ответ 1

--- ПРЕДУПРЕЖДЕНИЕ SPOILER ---

Короткий ответ заключается в том, что для того, чтобы скомпилировать исходный код для более старой версии, вам необходимо указать как параметр -source так и -bootclasspath. См. в этой статье. И если вы хотите скомпилировать исходный код для более новой версии, вам нужно установить <source>, <target>, <compilerVersion>, <fork> и <executable> в плагине компилятора и установить <jvm> в плагин surefire...

И теперь для истории...

Я столкнулся с той же проблемой. Оказывается, компиляция предыдущей версии может быть не такой простой, как установка <source> и <target>. В моем конкретном случае у меня есть Java 1.7 JDK, и у меня класс несовместим с 1.7 (они добавили новый метод для интерфейса, который я реализую). Когда я попытался скомпилировать его, компилятор дал мне сообщение об ошибке, в котором говорилось, что я не реализовал метод интерфейса. Во всяком случае, я попытался установить плагин компилятора:

  <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>

но когда я запустил сборку, я получил ту же ошибку. Итак, я побежал maven в отладке и увидел это:

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

Обратите внимание, что... для краткости вместо фактических аргументов вместо

на выходе. Сообщение, начинающееся с -d, является фактическим полным списком параметров компиляции. Итак, если вы удалите флаг -nowarn и вставьте остальные после javac в командной строке, вы увидите фактический вывод компилятора:

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

Это выводит путь к классу bootstrap, который не задан вместе с -source 1.6. Немного googling на этом появляется в этой статье, в которой говорится:

Использовать javac из JDK N для перекрестного компилятора на более старую платформу версии, правильная практика заключается в следующем:

  • Используйте параметр old-source.
  • Установите bootclasspath для компиляции с rt.jar(или эквивалентом) для старой платформы.

Если второй шаг не выполняется, javac будет покорно использовать старый языковые правила в сочетании с новыми библиотеками, что может привести к классу файлы, которые не работают на старой платформе, поскольку ссылки на несуществующие методы могут быть включены.

Теперь ссылаясь на документацию maven для плагина компилятора, вы получите:

  <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>

Что вы можете комбинировать с более ранней конфигурацией, чтобы получить:

  <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>

И теперь вам просто нужно сделать переменную ${java.home} доступной для ваших свойств mvn (через -D или через обычные старые переменные окружения, или вы можете получить действительно причудливые вещи и наполнить их в профиле java 6 у своего пользователя настройки).

Теперь просто запустите свою сборку и поймите, возьмите холодное пиво, пока оно пробивается...

---- EDIT ----

Последнее:... В том числе rt.jar в вашем bootclasspath всегда требуется, однако, я обнаружил, что больше может понадобиться в каждом конкретном случае. Я должен был включить jce.jar(в том же каталоге, что и rt.jar), потому что мое приложение выполняло криптографическую работу.

---- ИЗМЕНИТЬ 2 ----

Для усмешек я попробовал это в другом направлении. Вместо запуска maven с компиляцией java 7 для java 6 я запускал maven с компиляцией java 6 для java 7. Первая попытка довольно проста:

  <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>

В принципе, я установил мои <source> и <target> в 1.7, но этого явно не хватило бы потому, что 6 не удалось скомпилировать код 7. Итак, вернемся к плагину компилятора, на самом деле примерная страница, описывающая, что нужно делать. А именно, вам нужно <fork> отключить новый процесс, используя java 7 <executable>. Так что теперь я думаю, что все готово. Время для запуска сборки...

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] ------------------------------------------------------------------------

Какая черта UnsupportedClassVersionError? Более пристальный взгляд говорит нам о своем плагине maven-surefire, который терпит неудачу. Поэтому я стараюсь просто mvn compile и, конечно же, добился успеха, так как плагин surefire никогда не запускался. Поэтому я запускаю mvn -X package и замечаю этот камень:

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"

Хорошо, так работает java 6. Почему? Документация для surefire дает следующее:

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.

Так как мы запускали mvn с java 6 VM, он разворачивал java 6 VM для своих модульных тестов. Поэтому установите этот параметр соответствующим образом:

  <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>

И запуск сборки...

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

Ответ 2

Вы можете проверить версию, скомпилированную классом, с помощью утилиты javap, включенной в JDK. Из командной строки:

javap -verbose MyClass

На выходе javap найдите "малую версию" и "главную версию" около десяти строк.

Затем используйте эту таблицу:

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

Разница в размере .class может быть связана с компиляционной версией, но также может быть связана с другими параметрами компиляции, такими как компиляция с информацией об отладке (номера строк в stacktraces).

Ответ 3

Может ли быть, что плагин release компилируется в версии 1.6 или другой, объясняя разницу в размерах классов?

Не может быть ИМО. Модуль Maven Release Plugin не компилирует ничего, он просто запускает фазу, которая сама запускает фазу compile и Maven Compiler Plugin. Другими словами, будет использоваться плагин Maven Compiler и его настройки.

Вы можете использовать следующие команды для управления тем, что происходит точно:

mvn help:active-profiles -Prelease

для проверки профилей. И

mvn help:effective-pom -Prelease

чтобы проверить эффективную pom.

Ответ 4

Просто заметьте, если неправильная версия поднята, вы получите сообщение об ошибке

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

Во время релиза maven плагин maven release (устанавливает/по умолчанию) компилятор версии 1.5. Чтобы обеспечить правильную версию, укажите свойства.

<properties>
 <maven.compiler.source>1.7</maven.compiler.source>
 <maven.compiler.target>1.7</maven.compiler.target>
</properties>