Minor Gradle upgrade breaks test compilation - gradle

I want to incrementally upgrade the Gradle wrapper in order to improve build speed. After moving from 2.3 to 2.4 the test compilation fails with an incompatible types error and I am struggling with dependencies.
Consider this Spock test:
class DetailsSortKeySpec extends Specification {
def 'Simple Test'() {
given:
TestDetailsSortKey testDetailsSortKey = new TestDetailsSortKey(details, collationKey)
expect:
testDetailsSortKey.details.equals(details)
where:
details | collationKey
new TestDetails(id: 0) | Collator.getInstance(Locale.ENGLISH).getCollationKey('')
}
private class TestDetailsSortKey extends DetailsSortKey<TestDetails> {
TestDetailsSortKey(TestDetails details, CollationKey collationKey) {
super(details, collationKey)
}
}
}
And this Java class:
public class DetailsSortKey<T extends Details> {
private final T details;
private final CollationKey collationKey;
public DetailsSortKey(final T details, final CollationKey collationKey) {
this.details = details;
this.collationKey = Objects.requireNonNull(collationKey);
}
public final T getDetails() {
return details;
}
}
Following error message appears when running compileTestGroovy:
/var/lib/jenkins/workspace/gradle_upgrade/build/tmp/compileTestGroovy/groovy-java-stubs/com/vendor/transfer/sorting/DetailsSortKeySpec.java:25: error: incompatible types: Details cannot be converted to TestDetails
super ((com.vendor.common.transfer.Details)null, (java.text.CollationKey)null);
^
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error
startup failed:
Compilation failed; see the compiler error output for details.
1 error
FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':WEB-Commons:compileTestGroovy'.
> Compilation failed; see the compiler error output for details.
The Release Notes do not list any issues that seem to be responsible for this, so I dug through Groovy Issues that could be the root cause and for real found an issue about Generics. But the bug should already be fixed. I do not understand where and why there is a try to covert Details to TestDetails, which is just a plain and empty derivation from the Details class.
These are the used Groovy versions in both Gradle distros:
Gradle 2.3: Groovy: 2.3.9
Gradle 2.4: Groovy 2.3.10
To me, this seems exactly like the referenced Groovy bug, but that one should be fixed since 2.3.8 and should not affect this build. Furthermore, the dependencies are declared as followed in the project:
dependencies {
// mandatory dependencies for using Spock
compile "org.codehaus.groovy:groovy-all:2.4.11"
testCompile "org.spockframework:spock-core:1.1-groovy-2.4"
}
And the difference between the output of both gradlew :dependencies do only contain tools, that are mentioned in the release notes but do not belong to the test scope.
Eventually, which Groovy version is used here?

I think you are wanting to compile with a different version of Groovy than the one bundled with Gradle. See GroovyCompile.groovyClasspath
Eg:
configurations {
groovy
}
dependencies {
groovy 'org.codehaus.groovy:groovy-all:2.4.11'
compile 'org.codehaus.groovy:groovy-all:2.4.11'
testCompile('org.spockframework:spock-core:1.0-groovy-2.4') {
exclude module: 'groovy-all'
}
}
compileGroovy {
groovyClasspath = configurations.groovy
}
* Edit *
To see what version is being picked (due to dependency resolution) you can
gradle dependencies
Or (where xxx is a subproject name)
gradle xxx:dependencies
To force a specific dependency version you can
configurations.all {
resolutionStrategy {
force 'org.codehaus.groovy:groovy-all:2.4.11'
}
}
See ResolutionStrategy

Spock has a transitive dependency on groovy-all which might be conflicting with the groovy-all version you are declaring. Also you are not using Gradle's version of groovy-all (you are declaring yet another version of this). I declare those two dependencies differently, like this:
dependencies {
compile localGroovy()
testCompile('org.spockframework:spock-core:1.0-groovy-2.4') {
exclude module: 'groovy-all'
}
}

Related

Error: JavaFX runtime components are missing, and are required to run this application with Gradle example

I know this has been asked multiple times... but I can't seem to find a solution.
Taken from this official guidelines example: https://openjfx.io/openjfx-docs/#gradle
I went on and added in my build.gradle :
plugins {
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.8'
}
javafx {
version = '13'
modules = ['javafx.controls']
}
repositories {
mavenCentral()
}
mainClassName = "MyImage"
jar {
manifest {
attributes "Main-Class": "$mainClassName"
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
which, by running gradle jar (or gradle build), should actually produce a jar which should include all the packages it builds it with, that is the entire javafx library.
However, when it builds successfully and then I proceed with running:
java -jar build/libs/MyImage.jar
it still throws the error:
Error: JavaFX runtime components are missing, and are required to run this application
What am I missing?
(I use JDK 11)
Many Thanks.
In Java 11 the Java launcher detects that you're extending javafx.application.Application and checks the modules are present. If you're using plain old JARs then you'll get the error
Error: JavaFX runtime components are missing, and are required to run this application
You have two choices. Setup your application to use the Java module system or the following workaround.
This workaround avoids the Java launcher check and will let the application run.
public class MyImage { // <=== note - does not extend Application
public static class YourRealApplication extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
// whatever...
}
}
public static void main(String[] args) {
Application.launch(YourRealApplication.class);
}
}

Creating a fat jar using Kotlin and Gradle - compile vs implementation?

I'm tinkering with a simple "hello world" project in Kotlin & Gradle (see below). I've added the "fat jar" collection stuff to bring in the Kotlin dependency, but when I try to run java -jar build/libs/hello-1.0-SNAPSHOT.jar I get the java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics error because the dependencies aren't available at runtime.
I've solved that problem by changing implementation to compile, which makes everything work fine. But from what I understand, we shouldn't be using compile anymore, and neither api nor implementation makes the "fat jar" collection process work, and as I look at the other options for dependencies I'm not sure which to use.
Question: what's the "right" thing to do in a case like this?
// build.gradle
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.41'
}
group 'com.example.test'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
// if I change "implementation" to "compile", running the jar works
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
}
jar {
manifest {
attributes "Main-Class": "ApplicationKt"
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
// Application.kt
fun main(args: Array<String>) {
println("hello world")
}
The compile dependency is deprecated. You should use implementation to declare your implementation dependencies, and compileClasspath to get all the compilation dependencies.

Is there a Gradle task to download testRuntime dependencies?

Is there a command that will instruct Gradle to resolve and download all testRuntime dependencies, but not run the tests?
Preferably, I want to do this without writing a custom task (such that the command can be run against any Gradle project).
For example, if my build.gradle has this dependency:
dependencies {
// ...
testRuntime "org.seleniumhq.selenium:selenium-htmlunit-driver:2.47.1"
}
The JAR files associated with selenium-htmlunit-driver are not downloaded until I run gradle test, which also runs the tests. I can download all other dependencies by running gradle testClasses, but not the testRuntime deps.
Put the following in a file called resolve.gradle
gradle.allprojects { project ->
project.task('resolveTestRuntime') {
doLast {
project.configurations.testRuntime.resolve()
}
}
}
Then run resolve.gradle as an init script
gradlew --init-script resolve.gradle resolveTestRuntime
Modifying the answer from Lance Java into a valid Init script, I was able to accomplish this with the following resolve.gradle:
apply plugin:MyInitPlugin
class MyInitPlugin implements Plugin<Gradle> {
#Override
void apply(Gradle gradle) {
gradle.allprojects{ project ->
project.task('resolveTestRuntime') {
doLast {
project.configurations.testRuntime.resolve()
}
}
}
}
}
Then running:
gradlew --init-script resolve.gradle resolveTestRuntime

Gradle get dependency programatically without configuration

Is there an option to get Maven dependency in Gradle without using custom configuration for it? For example in custom plugin to just obtain dependency provided from extension? Something like
class DependencyPlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create("deps", DepsExtension)
project.task('useDependency') {
doLast {
//use Gradle api to resolve dependency without custom configuration
project.resolve(project.deps.dependency)
}
}
}
}
class DepsExtension {
def dependency = 'custom:maven:1.0'
}
Something like this:
Configuration config = project.configurations.create('myPrivateConfig')
Dependency dep = project.dependencies.create('custom:maven:1.0') {
exclude group: 'foo', module: 'bar'
}
config.dependencies.add(dep)
Set<File> files = config.files
I do a similar thing in a gradle plugin here
References
https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/Configuration.html
https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/DependencySet.html
https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/dsl/DependencyHandler.html

Gradle custom plugin : add dependency from extension object

I'm trying to write a plugin which adds dependencies to project.dependencies according to informations gathered in the plugin extension object. But it seems to be impossible.
Indeed, the data from extension object is only available in a new task or in project.afterEvaluate closure, but dependencies added in those places are ignored.
The following code tries to add the dependency in afterEvaluate but the dependency is ignored :
apply plugin: MyPlugin
myplugin {
version '1.0'
}
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create('myplugin', MyPluginExtension)
project.afterEvaluate {
def version = project.myplugin.version
project.dependencies.add("compile", "org.foo:bar:$version") // --> ignored
}
}
}
class MyPluginExtension {
def version
}
In the following code the dependency injection works but I don't have access to the extension object :
apply plugin: MyPlugin
myplugin {
version '1.0'
}
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create('myplugin', MyPluginExtension)
def version = project.myplugin.version // == null
project.dependencies.add("compile", "org.foo:bar:$version") // --> fail because $version is null
}
}
class MyPluginExtension {
def version
}
Is there a solution ?
Update: I managed to figure this out since my original answer. The way do this is to add a DependencyResolutionListener in which you add the dependencies and then remove the listener so it doesn't try to add them on later resolution steps.
compileDeps = project.getConfigurations().getByName("compile").getDependencies()
project.getGradle().addListener(new DependencyResolutionListener() {
#Override
void beforeResolve(ResolvableDependencies resolvableDependencies) {
compileDeps.add(project.getDependencies().create("org.foo:bar:$version"))
project.getGradle().removeListener(this)
}
#Override
void afterResolve(ResolvableDependencies resolvableDependencies) {}
})
I have a working example of a plugin that uses this here
Original Answer:
This is also late but for anyone dropping in. With the latest gradle (2.6 at the time of writing), you can add a DependencyResolutionListener and add any dependencies before dependencies are resolved.
project.getGradle().addListener(new DependencyResolutionListener() {
#Override
void beforeResolve(ResolvableDependencies resolvableDependencies) {
depsToAdd.each { dep ->
compileConfig.getDependencies()
.add(project.getDependencies().create(dep))
}
}
#Override
void afterResolve(ResolvableDependencies resolvableDependencies) {
}
})
However, as of this writing I was having some issues getting this to work with Android Studio IDE. The problem is tracked in my question here
I originally implemented this solution using the DependencyResolutionListener approach by Saad. However, the listener itself is called only when something iterates over the configuration associated with the dependency. For example, if you want to dynamically add a dependency to compile, you have to make sure that something later on does something like:
project.configurations.compile.each {
...
}
But this is something that happens as a matter of course, since compile is a known configuration for any project that uses the java plugin. However, if you are using a custom configuration (as I was), then the listener approach won't work unless you explicitly iterate over your custom configuration.
I was able to find a better way to do this, and within afterEvaluate as the OP originally wanted. I'm using a custom configuration here, but I don't see a reason why it wouldn't work for compile either:
project.afterEvaluate {
def version = project.myPlugin.version
project.configurations.myConfig.dependencies.add(
project.dependencies.add("myConfig", "org.foo:bar:$version")
)
}
Of course, at some point something still has to iterate over the dependencies for them to actually get resolved.
The easiest way to do this:
project.dependencies {
delegate.compile("com.android.support:appcompat-v7:25.0.1")
}
Don't know if that's still relevant, but you can workaround this by explicitly adding your compile configuration to Java classpath in doFirst:
variant.javaCompile.doFirst {
variant.javaCompile.classpath += project.configurations.myconfiguration
}

Resources