Embedding Gradle in application


(Mike Kobit) #1

Building Gradle plugins using the Gradle API is simple, especially with recent Gradle releases.

plugins {
  id 'java-gradle-plugin'
}

Or before you could use the gradleApi() in the DependencyHandler

dependencies {
  compile gradleApi()
    testCompile(group: 'org.spockframework', name: 'spock-core', version: '1.0-groovy-2.4') {
    exclude module: 'groovy-all'
  }
}

I (believe) have to exclude the groovy-all module because of different groovy versions loaded into the classpath when Gradle is running. This implementation makes sense because consumers can use different versions of Gradle/Groovy with the plugin and run it with whatever the local version is.

Now, I am trying to write a standalone application that bundles the Tooling API, and I am having trouble understanding how I am supposed to properly add a dependency.

I have a few things that I am trying to accomplish:

  1. Build application that makes use of the tooling API
  2. Use independent dependency versions of Gradle libraries in Gradle/Maven/SBT/whatever dependency resolution tool I use
  3. Bundle the libraries into a simple application (imagine like a Spring Boot app, or fat JAR)
  4. Use my own logging configuration (seems like Gradle’s SLF4J is clobbering mine right now)
model {
  tasks {
    wrapper {
      gradleVersion = '2.14-rc-5'
    }
  }
}

dependencies {
  compile gradleApi()
  compile group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.21'
// Can't use with gradleApi(), classpath errors
//  compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.4.7'
  testCompile group: 'junit', name: 'junit', version: '4.12'
  testCompile(group: 'org.spockframework', name: 'spock-core', version: '1.0-groovy-2.4') {
    exclude module: 'groovy-all'
  }
}

I’d like my application to use a different groovy version than might be bundled with the Gradle version I am using. This might be a bad decision, but I’d like to know why it is a bad decision, and why I don’t have control over excluding this dependency.

If I run ./gradlew myTask (for example, a JavaExec task with the main.runtimeClasspath) I see that there are multiple bindings on the classpath:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/mkobit/.gradle/caches/2.14-rc-5/generated-gradle-jars/gradle-api-2.14-rc-5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/mkobit/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-simple/1.7.21/be4b3c560a37e69b6c58278116740db28832232c/slf4j-simple-1.7.21.jar!/org/slf4j/impl/StaticLog]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.gradle.internal.logging.slf4j.OutputEventListenerBackedLoggerContext]

It does not seem easy to remove this Logger implementation off of the classpath. The same thing appears to be true with the Groovy dependency. These appear to all be bundled with the Gradle distribution that is being depended upon.

Is there any way to just bring in the JARs that are necessary for using the Tooling API? Is there specific JARs that I can depend on to be added to the same configurations in Gradle (running ./gradlew dependencies doesn’t show anything around the Gradle dependencies)?

Note: I didn’t try bundling everything into a fat JAR yet, so I’m unsure what would happen there


(Mark Vieira) #2

Yes. A tooling api jar is published to a public repository. This should include everything you need, that is, depending on gradleApi() should not be necessary.

repositories {
    maven {
        url 'https://repo.gradle.org/gradle/repo'
    }
}

dependencies {
    compile 'org.gradle:gradle-tooling-api:2.13'
}

(Mike Kobit) #3

Awesome! That’s exactly what I was looking for. I didn’t think to add the Gradle repository.

Do you think this would be useful to add into the Tooling API documentation?