Build: Move shadow customizations into common code (#32014)

Moves the customizations to the build to produce nice shadow jars and
javadocs into common build code, mostly BuildPlugin with a little into
the root build.gradle file. This means that any project that applies the
shadow plugin will automatically be set up just like the high level rest
client:
* The non-shadow jar will not be built
* The shadow jar will not have a "classifier"
* Tests will run against the shadow jar
* Javadoc will include all of the shadowed classes
* Service files in `META-INF/services` will be merged
This commit is contained in:
Nik Everett 2018-07-17 14:20:41 -04:00 committed by GitHub
parent 1c63eb1081
commit 1b97652a4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 153 additions and 173 deletions

View File

@ -17,17 +17,6 @@
* under the License.
*/
buildscript {
repositories {
maven {
url 'https://plugins.gradle.org/m2/'
}
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
}
}
apply plugin: 'elasticsearch.build'
// order of this section matters, see: https://github.com/johnrengelman/shadow/issues/336
@ -81,10 +70,6 @@ thirdPartyAudit.excludes = [
'org.openjdk.jmh.util.Utils'
]
shadowJar {
classifier = 'benchmarks'
}
runShadow {
executable = new File(project.runtimeJavaHome, 'bin/java')
}

View File

@ -17,7 +17,7 @@
* under the License.
*/
import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin
import org.apache.tools.ant.taskdefs.condition.Os
import org.apache.tools.ant.filters.ReplaceTokens
import org.elasticsearch.gradle.BuildPlugin
@ -303,18 +303,55 @@ subprojects {
if (project.plugins.hasPlugin(BuildPlugin)) {
String artifactsHost = VersionProperties.elasticsearch.isSnapshot() ? "https://snapshots.elastic.co" : "https://artifacts.elastic.co"
Closure sortClosure = { a, b -> b.group <=> a.group }
Closure depJavadocClosure = { dep ->
if (dep.group != null && dep.group.startsWith('org.elasticsearch')) {
Project upstreamProject = dependencyToProject(dep)
if (upstreamProject != null) {
project.javadoc.dependsOn "${upstreamProject.path}:javadoc"
String artifactPath = dep.group.replaceAll('\\.', '/') + '/' + dep.name.replaceAll('\\.', '/') + '/' + dep.version
project.javadoc.options.linksOffline artifactsHost + "/javadoc/" + artifactPath, "${upstreamProject.buildDir}/docs/javadoc/"
Closure depJavadocClosure = { shadowed, dep ->
if (dep.group == null || false == dep.group.startsWith('org.elasticsearch')) {
return
}
Project upstreamProject = dependencyToProject(dep)
if (upstreamProject == null) {
return
}
if (shadowed) {
/*
* Include the source of shadowed upstream projects so we don't
* have to publish their javadoc.
*/
project.evaluationDependsOn(upstreamProject.path)
project.javadoc.source += upstreamProject.javadoc.source
/*
* Do not add those projects to the javadoc classpath because
* we are going to resolve them with their source instead.
*/
project.javadoc.classpath = project.javadoc.classpath.filter { f ->
false == upstreamProject.configurations.archives.artifacts.files.files.contains(f)
}
/*
* Instead we need the upstream project's javadoc classpath so
* we don't barf on the classes that it references.
*/
project.javadoc.classpath += upstreamProject.javadoc.classpath
} else {
// Link to non-shadowed dependant projects
project.javadoc.dependsOn "${upstreamProject.path}:javadoc"
String artifactPath = dep.group.replaceAll('\\.', '/') + '/' + dep.name.replaceAll('\\.', '/') + '/' + dep.version
project.javadoc.options.linksOffline artifactsHost + "/javadoc/" + artifactPath, "${upstreamProject.buildDir}/docs/javadoc/"
}
}
project.configurations.compile.dependencies.findAll().toSorted(sortClosure).each(depJavadocClosure)
project.configurations.compileOnly.dependencies.findAll().toSorted(sortClosure).each(depJavadocClosure)
boolean hasShadow = project.plugins.hasPlugin(ShadowPlugin)
project.configurations.compile.dependencies
.findAll()
.toSorted(sortClosure)
.each({ c -> depJavadocClosure(hasShadow, c) })
project.configurations.compileOnly.dependencies
.findAll()
.toSorted(sortClosure)
.each({ c -> depJavadocClosure(hasShadow, c) })
if (hasShadow) {
project.configurations.shadow.dependencies
.findAll()
.toSorted(sortClosure)
.each({ c -> depJavadocClosure(false, c) })
}
}
}
}

View File

@ -104,6 +104,7 @@ dependencies {
compile 'de.thetaphi:forbiddenapis:2.5'
compile 'org.apache.rat:apache-rat:0.11'
compile "org.elasticsearch:jna:4.5.1"
compile 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
testCompile "junit:junit:${props.getProperty('junit')}"
}

View File

@ -19,6 +19,7 @@
package org.elasticsearch.gradle
import com.carrotsearch.gradle.junit4.RandomizedTestingTask
import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin
import org.apache.tools.ant.taskdefs.condition.Os
import org.eclipse.jgit.lib.Constants
import org.eclipse.jgit.lib.RepositoryBuilder
@ -36,12 +37,14 @@ import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.artifacts.ModuleVersionIdentifier
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.artifacts.ResolvedArtifact
import org.gradle.api.artifacts.SelfResolvingDependency
import org.gradle.api.artifacts.dsl.RepositoryHandler
import org.gradle.api.execution.TaskExecutionGraph
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
import org.gradle.api.publish.maven.tasks.GenerateMavenPom
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.compile.GroovyCompile
import org.gradle.api.tasks.compile.JavaCompile
@ -498,7 +501,41 @@ class BuildPlugin implements Plugin<Project> {
}
}
}
project.plugins.withType(ShadowPlugin).whenPluginAdded {
project.publishing {
publications {
nebula(MavenPublication) {
artifact project.tasks.shadowJar
artifactId = project.archivesBaseName
/*
* Configure the pom to include the "shadow" as compile dependencies
* because that is how we're using them but remove all other dependencies
* because they've been shaded into the jar.
*/
pom.withXml { XmlProvider xml ->
Node root = xml.asNode()
root.remove(root.dependencies)
Node dependenciesNode = root.appendNode('dependencies')
project.configurations.shadow.allDependencies.each {
if (false == it instanceof SelfResolvingDependency) {
Node dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
dependencyNode.appendNode('scope', 'compile')
}
}
// Be tidy and remove the element if it is empty
if (dependenciesNode.children.empty) {
root.remove(dependenciesNode)
}
}
}
}
}
}
}
}
/** Adds compiler settings to the project */
@ -660,6 +697,28 @@ class BuildPlugin implements Plugin<Project> {
}
}
}
project.plugins.withType(ShadowPlugin).whenPluginAdded {
/*
* When we use the shadow plugin we entirely replace the
* normal jar with the shadow jar so we no longer want to run
* the jar task.
*/
project.tasks.jar.enabled = false
project.tasks.shadowJar {
/*
* Replace the default "shadow" classifier with null
* which will leave the classifier off of the file name.
*/
classifier = null
/*
* Not all cases need service files merged but it is
* better to be safe
*/
mergeServiceFiles()
}
// Make sure we assemble the shadow jar
project.tasks.assemble.dependsOn project.tasks.shadowJar
}
}
/** Returns a closure of common configuration shared by unit and integration tests. */
@ -744,6 +803,18 @@ class BuildPlugin implements Plugin<Project> {
}
exclude '**/*$*.class'
project.plugins.withType(ShadowPlugin).whenPluginAdded {
/*
* If we make a shaded jar we test against it.
*/
classpath -= project.tasks.compileJava.outputs.files
classpath -= project.configurations.compile
classpath -= project.configurations.runtime
classpath += project.configurations.shadow
classpath += project.tasks.shadowJar.outputs.files
dependsOn project.tasks.shadowJar
}
}
}
@ -766,7 +837,26 @@ class BuildPlugin implements Plugin<Project> {
additionalTest.dependsOn(project.tasks.testClasses)
test.dependsOn(additionalTest)
});
return test
project.plugins.withType(ShadowPlugin).whenPluginAdded {
/*
* We need somewhere to configure dependencies that we don't wish
* to shade into the jar. The shadow plugin creates a "shadow"
* configuration which is *almost* exactly that. It is never
* bundled into the shaded jar but is used for main source
* compilation. Unfortunately, by default it is not used for
* *test* source compilation and isn't used in tests at all. This
* change makes it available for test compilation.
*
* Note that this isn't going to work properly with qa projects
* but they have no business applying the shadow plugin in the
* firstplace.
*/
SourceSet testSourceSet = project.sourceSets.findByName('test')
if (testSourceSet != null) {
testSourceSet.compileClasspath += project.configurations.shadow
}
}
}
private static configurePrecommit(Project project) {

View File

@ -18,11 +18,13 @@
*/
package org.elasticsearch.gradle.plugin
import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin
import nebula.plugin.info.scm.ScmInfoPlugin
import org.elasticsearch.gradle.BuildPlugin
import org.elasticsearch.gradle.NoticeTask
import org.elasticsearch.gradle.test.RestIntegTestTask
import org.elasticsearch.gradle.test.RunTask
import org.gradle.api.InvalidUserDataException
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.Task
@ -46,6 +48,18 @@ public class PluginBuildPlugin extends BuildPlugin {
@Override
public void apply(Project project) {
super.apply(project)
project.plugins.withType(ShadowPlugin).whenPluginAdded {
/*
* We've not tested these plugins together and we're fairly sure
* they aren't going to work properly as is *and* we're not really
* sure *why* you'd want to shade stuff in plugins. So we throw an
* exception here to make you come and read this comment. If you
* have a need for shadow while building plugins then know that you
* are probably going to have to fight with gradle for a while....
*/
throw new InvalidUserDataException('elasticsearch.esplugin is not '
+ 'compatible with com.github.johnrengelman.shadow');
}
configureDependencies(project)
// this afterEvaluate must happen before the afterEvaluate added by integTest creation,
// so that the file name resolution for installing the plugin will be setup

View File

@ -17,18 +17,6 @@
* under the License.
*/
buildscript {
repositories {
maven {
url 'https://plugins.gradle.org/m2/'
}
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
}
}
apply plugin: 'elasticsearch.build'
// build an uberjar with all benchmarks
apply plugin: 'com.github.johnrengelman.shadow'

View File

@ -21,17 +21,6 @@ import org.elasticsearch.gradle.precommit.PrecommitTasks
import org.elasticsearch.gradle.test.RestIntegTestTask
import org.gradle.api.internal.provider.Providers
buildscript {
repositories {
maven {
url 'https://plugins.gradle.org/m2/'
}
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
}
}
apply plugin: 'elasticsearch.build'
apply plugin: 'elasticsearch.rest-test'
apply plugin: 'nebula.maven-base-publish'
@ -45,49 +34,6 @@ archivesBaseName = 'elasticsearch-rest-high-level-client'
Task copyRestSpec = RestIntegTestTask.createCopyRestSpecTask(project, Providers.FALSE)
test.dependsOn(copyRestSpec)
publishing {
publications {
nebula(MavenPublication) {
artifact shadowJar
artifactId = archivesBaseName
/*
* Configure the pom to include the "shadow" as compile dependencies
* because that is how we're using them but remove all other dependencies
* because they've been shaded into the jar.
*/
pom.withXml { XmlProvider xml ->
Node root = xml.asNode()
root.remove(root.dependencies)
Node dependenciesNode = root.appendNode('dependencies')
project.configurations.shadow.allDependencies.each {
if (false == it instanceof SelfResolvingDependency) {
Node dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
dependencyNode.appendNode('scope', 'compile')
}
}
}
}
}
}
/*
* We need somewhere to configure dependencies that we don't wish to shade
* into the high level REST client. The shadow plugin creates a "shadow"
* configuration which is *almost* exactly that. It is never bundled into
* the shaded jar but is used for main source compilation. Unfortunately,
* by default it is not used for *test* source compilation and isn't used
* in tests at all. This change makes it available for test compilation.
* A change below makes it available for testing.
*/
sourceSets {
test {
compileClasspath += configurations.shadow
}
}
dependencies {
/*
* Everything in the "shadow" configuration is *not* copied into the
@ -124,48 +70,3 @@ forbiddenApisMain {
signaturesURLs += [PrecommitTasks.getResource('/forbidden/http-signatures.txt')]
signaturesURLs += [file('src/main/resources/forbidden/rest-high-level-signatures.txt').toURI().toURL()]
}
shadowJar {
classifier = null
mergeServiceFiles()
}
// We don't need normal jar, we use shadow jar instead
jar.enabled = false
assemble.dependsOn shadowJar
javadoc {
/*
* Bundle all of the javadoc from all of the shaded projects into this one
* so we don't *have* to publish javadoc for all of the "client" jars.
*/
configurations.compile.dependencies.all { Dependency dep ->
Project p = dependencyToProject(dep)
if (p != null) {
evaluationDependsOn(p.path)
source += p.sourceSets.main.allJava
}
}
}
/*
* Use the jar for testing so we have tests of the bundled jar.
* Use the "shadow" configuration for testing because we need things
* in it.
*/
test {
classpath -= compileJava.outputs.files
classpath -= configurations.compile
classpath -= configurations.runtime
classpath += configurations.shadow
classpath += shadowJar.outputs.files
dependsOn shadowJar
}
integTestRunner {
classpath -= compileJava.outputs.files
classpath -= configurations.compile
classpath -= configurations.runtime
classpath += configurations.shadow
classpath += shadowJar.outputs.files
dependsOn shadowJar
}

View File

@ -1,15 +1,3 @@
buildscript {
repositories {
maven {
url 'https://plugins.gradle.org/m2/'
}
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
}
}
apply plugin: 'elasticsearch.build'
apply plugin: 'nebula.maven-base-publish'
apply plugin: 'nebula.maven-scm'
@ -49,7 +37,6 @@ dependencyLicenses {
}
shadowJar {
classifier = null
relocate 'com.fasterxml', 'org.elasticsearch.fasterxml'
}
@ -70,26 +57,3 @@ artifacts {
nodeps nodepsJar
archives shadowJar
}
publishing {
publications {
nebula(MavenPublication) {
artifact shadowJar
pom.withXml {
// Nebula is mistakenly including all dependencies that are already shadowed into the shadow jar
asNode().remove(asNode().dependencies)
}
}
}
}
assemble.dependsOn shadowJar
// Use the jar for testing so the tests are more "real"
test {
classpath -= compileJava.outputs.files
classpath -= configurations.compile
classpath -= configurations.runtime
classpath += shadowJar.outputs.files
dependsOn shadowJar
}