Можно ли скомпилировать и протестировать исходные файлы Groovy для Java 6 с Gradle 5? - PullRequest
2 голосов
/ 03 мая 2019

Исходя из требований заказчика, я в настоящее время застрял на Java 6 и поэтому сейчас использую Gradle 2.14.1. Чтобы иметь возможность обновить Gradle до последней версии и при этом скомпилировать и протестировать для Java 6, я следовал инструкциям в официальном руководстве пользователя - см. Groovy Cross Compilation .

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

import groovy.transform.TypeChecked

@TypeChecked
class SomeGroovyClass {
    Optional<String> someGroovyMethod() {
        return Optional.empty()
    }
}

Но каким-то образом сборка Gradle прошла успешно. Я что-то здесь упускаю? Как я могу использовать последнюю версию Gradle и все еще компилировать и тестировать исходные файлы Groovy и исходные файлы Java для Java 6?

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

Я также создал небольшой тестовый проект, демонстрирующий мою проблему. Проект можно найти здесь - Github .

Заранее спасибо.

1 Ответ

1 голос
/ 03 мая 2019

Я живу в подобной ситуации время от времени в течение последних нескольких лет.

Чтобы понять это, я создал пример мини-проекта со следующим файлом build.gradle:

apply plugin: 'groovy'

repositories { 
  jcenter()
  mavenCentral()
}

dependencies { 
    compile "org.codehaus.groovy:groovy:2.4.15"
}



if (JavaVersion.current() != JavaVersion.VERSION_1_6) {
  // findJava6Jvm is defined in java-versions.gradle
  // and returns a gradle JavaInfo instance.
  def java6Jvm = findJava6Jvm()

  if (!rootProject.ext.has('hasPrintedJavaVersionNote')) {
    println "**************** java version notice ****************"
    println "NOTE: the gradle process and the source compilation"
    println "      are using different versions of java:"
    println ""
    println "    gradle process uses:       ${JavaVersion.current()}"
    println "    source complilation uses:  1.6"
    println ""
    println "*****************************************************"
    rootProject.ext.hasPrintedJavaVersionNote = true
  }

  sourceCompatibility = JavaVersion.VERSION_1_6
  targetCompatibility = JavaVersion.VERSION_1_6

  tasks.withType(AbstractCompile) {
    options.with {
      fork = true
      forkOptions.executable = java6Jvm.javacExecutable
    }
  }

  tasks.withType(GroovyCompile) {
    groovyOptions.with {
      fork = true
    }
  }

  tasks.withType(Javadoc) {
    executable = java6Jvm.javadocExecutable
  }

  tasks.withType(Test) {
    executable = java6Jvm.javaExecutable
  }

  tasks.withType(JavaExec) {
    executable = java6Jvm.javaExecutable
  }
}


def findJava6Jvm(Closure extraUsage = null) {
  if (JavaVersion.current().isJava6()) {
    // if we are already using java 6 to launch gradle, just return 
    // the javac for the current jvm
    return org.gradle.internal.jvm.Jvm.current()
  }

  def failOnCondition = { condition, msg -> 
    if (condition) {
      println """
      Executing the gradle build with a JDK different from java 1.6
      (i.e. java 7, 8, etc) requires that you provide the build 
      with enough information so that it can still locate a java 1.6
      jdk for source code compilation. 

      If the build can not locate a java 6 jdk, you can help it out by 
      specifying one of the following properties: 

        JAVA_HOME_6 environment variable (example: \"export JAVA_HOME_6=/usr/lib/jvm/java-6/\")
        JAVA_HOME_6 system property (-DJAVA_HOME_6=<path>)
        JAVA_HOME_6 gradle project property (-PJAVA_HOME_6=<path>)
      """.stripIndent()  

      if (extraUsage != null) {
        extraUsage()
      }
      println msg.stripIndent()        
      throw new GradleException("No 1.6.x jdk found!") 
    }
  }

  def name = 'JAVA_HOME_6'
  def custom = [System.env[name], System.properties[name], properties[name]].find { it }
  failOnCondition !custom, """
      ERROR: Please set the JAVA_HOME_6 property in one of the above specified
             ways"""

  def jdkDir = file(custom)
  failOnCondition !jdkDir.isDirectory(), """
      ERROR: The configured JAVA_HOME_6 setting:

        $custom

      does not point to a directory on the local file system.
      Please set this variable to the JAVA_HOME of a 1.6.x
      jdk"""


  def fs = File.separator
  def jdkJavac = file("$jdkDir${fs}bin${fs}javac").canonicalFile
  if( !jdkJavac.isFile() ) jdkJavac = file( jdkJavac.path + ".exe" )

  failOnCondition !jdkJavac.isFile(), """
      ERROR: Could not locate a bin/javac executable file under 
             the configured JAVA_HOME_6 setting: 

        $custom \n"""

  def process = [jdkJavac, "-version"].execute()
  process.waitForOrKill(5000)
  def version = process.err.text.readLines().first()
  failOnCondition !version?.contains('1.6.'), """
      ERROR: The configured JAVA_HOME_6 setting:

        $custom

      points at a non 1.6 jdk, 'java -version' reports $version!"""

  // after all the validations pass, reutrn the jdk javac path
  org.gradle.internal.jvm.Jvm.forHome(jdkDir)
}

и поместил класс в вашем вопросе в файл по адресу:

java-version-experiment ~> tree src/
src/
└── main
    └── groovy
        └── com
            └── somepackage
                └── SomeGroovyClass.groovy

Учитывая вышесказанное, когда я запускаю gradle-компиляцию моего источника с использованием java 8:

java-version-experiment ~>  export JAVA_HOME_6=/usr/lib/jvm/java-6_121-oracle

java-version-experiment ~>  setjava java-8-oracle 
PATH updated - JAVA_HOME=/usr/lib/jvm/java-8-oracle

java-version-experiment ~> gradle -v

------------------------------------------------------------
Gradle 4.10.2
------------------------------------------------------------

Build time:   2018-09-19 18:10:15 UTC
Revision:     b4d8d5d170bb4ba516e88d7fe5647e2323d791dd

Kotlin DSL:   1.0-rc-6
Kotlin:       1.2.61
Groovy:       2.4.15
Ant:          Apache Ant(TM) version 1.9.11 compiled on March 23 2018
JVM:          1.8.0_201 (Oracle Corporation 25.201-b09)
OS:           Linux 4.18.0-17-generic amd64


java-version-experiment ~>  gradle build

> Configure project :
**************** java version notice ****************
NOTE: the gradle process and the source compilation
      are using different versions of java:

    gradle process uses:       1.8
    source complilation uses:  1.6

*****************************************************

BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed

java-version-experiment ~>  

, кажется, что класс скомпилирован с использованием java 6. В linux вы можете проверитьэто с помощью шестнадцатеричного редактора командной строки для файлов классов:

~> od -j 7 -N 1 -t d1 build/classes/groovy/main/com/somepackage/SomeGroovyClass.class 
0000007   50
0000010
~> 

, который, по сути, извлекает байт 7 (с нуля) из файла класса, где 50 - это то, что нас интересует. Соответствующая версия Javaчисла:

Java 6 uses major version 50
Java 7 uses major version 51
Java 8 uses major version 52
Java 9 uses major version 53
Java 10 uses major version 54
Java 11 uses major version 55

другими словами, мне кажется, что использование forkOptions.executable в задаче компиляции работает, и классы действительно компилируются с использованием Java 6.

Однако этоМне также кажется, что утечки классов.Под этим я подразумеваю, что кажется, что даже если вы компилируете с использованием исполняемого файла java 6, путь к классу java 8 и API-интерфейсы просачиваются в процесс компиляции.

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

Любые гуру-грейдеры, пожалуйста, не стесняйтесь звонить сюда, мне было бы очень интересно разобраться в этом вопросе.

<< edit >>

Обнаружил следующее:

Компиляция и тестирование для Java 6 или Java 7

Компилятор Groovy всегда будетвыполняться с той же версией Java, которая использовалась для запуска Gradle.Вы должны установить sourceCompatibility и targetCompatibility на 1.6 или 1.7.Если у вас также есть исходные файлы Java, вы можете выполнить те же шаги, что и для подключаемого модуля Java, чтобы убедиться, что используется правильный компилятор Java.

в документации по gradle по адресу:

https://docs.gradle.org/current/userguide/groovy_plugin.html

Так что это кажется невозможным для заводной.Седьмой байт в файле классов, кажется, управляется настройкой:

targetCompatibility = JavaVersion.VERSION_1_6

, т.е. даже при использовании чистой Java 8 и установке targetCompatibility мы получаем 50 в байте основной версии класса.

<< edit 2 >>

проверил, что это работает для файлов Java.Добавлен java-файл под src/main/java/com/somepackage/SomeJavaClass.java с тем же файлом сборки, что и выше, для настройки сценария компиляции с двумя виртуальными машинами.

Результат:

gradle clean build

> Configure project :
**************** java version notice ****************
NOTE: the gradle process and the source compilation
      are using different versions of java:

    gradle process uses:       1.8
    source complilation uses:  1.6

*****************************************************

> Task :compileJava FAILED
/home/mbjarland/projects/java-version-experiment/src/main/java/com/somepackage/SomeJavaClass.java:5: cannot find symbol
symbol  : class Optional
location: class com.somepackage.SomeJavaClass
    public Optional<String> someJavaMethod() {
           ^
/home/mbjarland/projects/java-version-experiment/src/main/java/com/somepackage/SomeJavaClass.java:6: cannot find symbol
symbol  : variable Optional
location: class com.somepackage.SomeJavaClass
        return Optional.empty();
               ^
2 errors

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':compileJava'.
> Compilation failed with exit code 1; see the compiler error output for details.

BUILD FAILED in 1s
2 actionable tasks: 2 executed

- это то, что вы ожидаете.

Заключение после долгого исследования: это работает для файлов Java, как и ожидалось, невозможно для файлов Groovy.

...