Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build: Rework shadow plugin configuration #32409

Merged
merged 27 commits into from
Aug 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,21 +325,19 @@ common configurations in our build and how we use them:

<dl>
<dt>`compile`</dt><dd>Code that is on the classpath at both compile and
runtime. If the [`shadow`][shadow-plugin] plugin is applied to the project then
this code is bundled into the jar produced by the project.</dd>
runtime.</dd>
<dt>`runtime`</dt><dd>Code that is not on the classpath at compile time but is
on the classpath at runtime. We mostly use this configuration to make sure that
we do not accidentally compile against dependencies of our dependencies also
known as "transitive" dependencies".</dd>
<dt>`compileOnly`</dt><dd>Code that is on the classpath at comile time but that
<dt>`compileOnly`</dt><dd>Code that is on the classpath at compile time but that
should not be shipped with the project because it is "provided" by the runtime
somehow. Elasticsearch plugins use this configuration to include dependencies
that are bundled with Elasticsearch's server.</dd>
<dt>`shadow`</dt><dd>Only available in projects with the shadow plugin. Code
that is on the classpath at both compile and runtime but it *not* bundled into
the jar produced by the project. If you depend on a project with the `shadow`
plugin then you need to depend on this configuration because it will bring
along all of the dependencies you need at runtime.</dd>
<dt>`bundle`</dt><dd>Only available in projects with the shadow plugin,
dependencies with this configuration are bundled into the jar produced by the
build. Since IDEs do not understand this configuration we rig them to treat
dependencies in this configuration as `compile` dependencies.</dd>
<dt>`testCompile`</dt><dd>Code that is on the classpath for compiling tests
that are part of this project but not production code. The canonical example
of this is `junit`.</dd>
Expand Down
39 changes: 13 additions & 26 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import org.elasticsearch.gradle.LoggedExec
import org.elasticsearch.gradle.Version
import org.elasticsearch.gradle.VersionCollection
import org.elasticsearch.gradle.VersionProperties
import org.elasticsearch.gradle.plugin.PluginBuildPlugin
import org.gradle.plugins.ide.eclipse.model.SourceFolder
import org.gradle.util.GradleVersion
import org.gradle.util.DistributionLocator
Expand Down Expand Up @@ -304,7 +305,7 @@ subprojects {
// org.elasticsearch:elasticsearch must be the last one or all the links for the
// other packages (e.g org.elasticsearch.client) will point to server rather than
// their own artifacts.
if (project.plugins.hasPlugin(BuildPlugin)) {
if (project.plugins.hasPlugin(BuildPlugin) || project.plugins.hasPlugin(PluginBuildPlugin)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PluginBuildPlugin extends BuildPlugin, maybe it should apply it instead? Then we only need the one check.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that'd be cleaner but I didn't want to do it while messing around with shadow stuff.

String artifactsHost = VersionProperties.elasticsearch.isSnapshot() ? "https://snapshots.elastic.co" : "https://artifacts.elastic.co"
Closure sortClosure = { a, b -> b.group <=> a.group }
Closure depJavadocClosure = { shadowed, dep ->
Expand All @@ -322,13 +323,6 @@ subprojects {
*/
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.
Expand All @@ -345,16 +339,16 @@ subprojects {
project.configurations.compile.dependencies
.findAll()
.toSorted(sortClosure)
.each({ c -> depJavadocClosure(hasShadow, c) })
.each({ c -> depJavadocClosure(false, c) })
project.configurations.compileOnly.dependencies
.findAll()
.toSorted(sortClosure)
.each({ c -> depJavadocClosure(hasShadow, c) })
.each({ c -> depJavadocClosure(false, c) })
if (hasShadow) {
project.configurations.shadow.dependencies
project.configurations.bundle.dependencies
.findAll()
.toSorted(sortClosure)
.each({ c -> depJavadocClosure(false, c) })
.each({ c -> depJavadocClosure(true, c) })
}
}
}
Expand Down Expand Up @@ -523,25 +517,18 @@ allprojects {
allprojects {
/*
* IntelliJ and Eclipse don't know about the shadow plugin so when we're
* in "IntelliJ mode" or "Eclipse mode" add "runtime" dependencies
* eveywhere where we see a "shadow" dependency which will cause them to
* reference shadowed projects directly rather than rely on the shadowing
* to include them. This is the correct thing for it to do because it
* doesn't run the jar shadowing at all. This isn't needed for the project
* in "IntelliJ mode" or "Eclipse mode" switch "bundle" dependencies into
* regular "compile" dependencies. This isn't needed for the project
* itself because the IDE configuration is done by SourceSets but it is
* *is* needed for projects that depends on the project doing the shadowing.
* Without this they won't properly depend on the shadowed project.
*/
if (isEclipse || isIdea) {
configurations.all { Configuration configuration ->
dependencies.all { Dependency dep ->
if (dep instanceof ProjectDependency) {
if (dep.getTargetConfiguration() == 'shadow') {
configuration.dependencies.add(project.dependencies.project(path: dep.dependencyProject.path, configuration: 'runtime'))
}
}
}
}
project.plugins.withType(ShadowPlugin).whenPluginAdded {
project.afterEvaluate {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be done when the bundle configuration is added, in BuildPlugin, instead of in an afterEvaluate?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BuildPlugin doesn't have isEclipse or isIdea but I can do this after the BuildPlugin is added.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BuildPlugin doesn't have isEclipse or isIdea

Maybe it should? It seems odd to have to do this extra work, it should really be part of the base setup.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll use it if it is defined.

project.configurations.compile.extendsFrom project.configurations.bundle
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ class BuildPlugin implements Plugin<Project> {
}
project.pluginManager.apply('java')
project.pluginManager.apply('carrotsearch.randomized-testing')
// these plugins add lots of info to our jars
configureConfigurations(project)
configureJars(project) // jar config must be added before info broker
// these plugins add lots of info to our jars
project.pluginManager.apply('nebula.info-broker')
project.pluginManager.apply('nebula.info-basic')
project.pluginManager.apply('nebula.info-java')
Expand All @@ -91,8 +92,8 @@ class BuildPlugin implements Plugin<Project> {

globalBuildInfo(project)
configureRepositories(project)
configureConfigurations(project)
project.ext.versions = VersionProperties.versions
configureSourceSets(project)
configureCompile(project)
configureJavadoc(project)
configureSourcesJar(project)
Expand Down Expand Up @@ -421,8 +422,10 @@ class BuildPlugin implements Plugin<Project> {
project.configurations.compile.dependencies.all(disableTransitiveDeps)
project.configurations.testCompile.dependencies.all(disableTransitiveDeps)
project.configurations.compileOnly.dependencies.all(disableTransitiveDeps)

project.plugins.withType(ShadowPlugin).whenPluginAdded {
project.configurations.shadow.dependencies.all(disableTransitiveDeps)
Configuration bundle = project.configurations.create('bundle')
bundle.dependencies.all(disableTransitiveDeps)
}
}

Expand Down Expand Up @@ -556,37 +559,27 @@ class BuildPlugin implements Plugin<Project> {
publications {
nebula(MavenPublication) {
artifacts = [ 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)
}
}
}
}
}
}
}
}

/**
* Add dependencies that we are going to bundle to the compile classpath.
*/
static void configureSourceSets(Project project) {
project.plugins.withType(ShadowPlugin).whenPluginAdded {
['main', 'test'].each {name ->
SourceSet sourceSet = project.sourceSets.findByName(name)
if (sourceSet != null) {
sourceSet.compileClasspath += project.configurations.bundle
}
}
}
}

/** Adds compiler settings to the project */
static void configureCompile(Project project) {
if (project.compilerJavaVersion < JavaVersion.VERSION_1_10) {
Expand Down Expand Up @@ -764,9 +757,16 @@ class BuildPlugin implements Plugin<Project> {
* better to be safe
*/
mergeServiceFiles()
/*
* Bundle dependencies of the "bundled" configuration.
*/
configurations = [project.configurations.bundle]
}
// Make sure we assemble the shadow jar
project.tasks.assemble.dependsOn project.tasks.shadowJar
project.artifacts {
apiElements project.tasks.shadowJar
}
}
}

Expand Down Expand Up @@ -873,13 +873,8 @@ class BuildPlugin implements Plugin<Project> {
exclude '**/*$*.class'

project.plugins.withType(ShadowPlugin).whenPluginAdded {
/*
* If we make a shaded jar we test against it.
*/
// Test against a shadow jar if we made one
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
}
Expand All @@ -905,26 +900,6 @@ class BuildPlugin implements Plugin<Project> {
additionalTest.dependsOn(project.tasks.testClasses)
project.check.dependsOn(additionalTest)
});

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) {
Expand All @@ -936,7 +911,7 @@ class BuildPlugin implements Plugin<Project> {
it.group.startsWith('org.elasticsearch') == false
} - project.configurations.compileOnly
project.plugins.withType(ShadowPlugin).whenPluginAdded {
project.dependencyLicenses.dependencies += project.configurations.shadow.fileCollection {
project.dependencyLicenses.dependencies += project.configurations.bundle.fileCollection {
it.group.startsWith('org.elasticsearch') == false
}
}
Expand All @@ -947,7 +922,7 @@ class BuildPlugin implements Plugin<Project> {
deps.runtimeConfiguration = project.configurations.runtime
project.plugins.withType(ShadowPlugin).whenPluginAdded {
deps.runtimeConfiguration = project.configurations.create('infoDeps')
deps.runtimeConfiguration.extendsFrom(project.configurations.runtime, project.configurations.shadow)
deps.runtimeConfiguration.extendsFrom(project.configurations.runtime, project.configurations.bundle)
}
deps.compileOnlyConfiguration = project.configurations.compileOnly
project.afterEvaluate {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,10 @@ public class PluginBuildPlugin extends BuildPlugin {
from pluginMetadata // metadata (eg custom security policy)
/*
* If the plugin is using the shadow plugin then we need to bundle
* "shadow" things rather than the default jar and dependencies so
* we don't hit jar hell.
* that shadow jar.
*/
from { project.plugins.hasPlugin(ShadowPlugin) ? project.shadowJar : project.jar }
from { project.plugins.hasPlugin(ShadowPlugin) ? project.configurations.shadow : project.configurations.runtime - project.configurations.compileOnly }
from project.configurations.runtime - project.configurations.compileOnly
// extra files for the plugin to go into the zip
from('src/main/packaging') // TODO: move all config/bin/_size/etc into packaging
from('src/main') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

package org.elasticsearch.gradle.precommit

import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin
import org.elasticsearch.gradle.LoggedExec
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.OutputFile
Expand All @@ -39,6 +40,9 @@ public class JarHellTask extends LoggedExec {
public JarHellTask() {
project.afterEvaluate {
FileCollection classpath = project.sourceSets.test.runtimeClasspath
if (project.plugins.hasPlugin(ShadowPlugin)) {
classpath += project.configurations.bundle
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since compile extends from bundle, shouldn't this be in the test runtime classpath already?

Copy link
Member Author

@nik9000 nik9000 Aug 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compile only extends from bundle when we're configuring an IDE.

}
inputs.files(classpath)
dependsOn(classpath)
description = "Runs CheckJarHell on ${classpath}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.elasticsearch.gradle.precommit;

import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin
import org.apache.tools.ant.BuildEvent;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.BuildListener;
Expand Down Expand Up @@ -82,6 +83,11 @@ public class ThirdPartyAuditTask extends AntTask {
configuration = project.configurations.findByName('testCompile')
}
assert configuration != null
if (project.plugins.hasPlugin(ShadowPlugin)) {
Configuration original = configuration
configuration = project.configurations.create('thirdPartyAudit')
configuration.extendsFrom(original, project.configurations.bundle)
}
if (compileOnly == null) {
classpath = configuration
} else {
Expand Down
14 changes: 7 additions & 7 deletions client/rest-high-level/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ dependencies {
* Everything in the "shadow" configuration is *not* copied into the
* shadowJar.
*/
shadow "org.elasticsearch:elasticsearch:${version}"
shadow "org.elasticsearch.client:elasticsearch-rest-client:${version}"
shadow "org.elasticsearch.plugin:parent-join-client:${version}"
shadow "org.elasticsearch.plugin:aggs-matrix-stats-client:${version}"
shadow "org.elasticsearch.plugin:rank-eval-client:${version}"
shadow "org.elasticsearch.plugin:lang-mustache-client:${version}"
compile project(':x-pack:protocol')
compile "org.elasticsearch:elasticsearch:${version}"
compile "org.elasticsearch.client:elasticsearch-rest-client:${version}"
compile "org.elasticsearch.plugin:parent-join-client:${version}"
compile "org.elasticsearch.plugin:aggs-matrix-stats-client:${version}"
compile "org.elasticsearch.plugin:rank-eval-client:${version}"
compile "org.elasticsearch.plugin:lang-mustache-client:${version}"
bundle project(':x-pack:protocol')

testCompile "org.elasticsearch.client:test:${version}"
testCompile "org.elasticsearch.test:framework:${version}"
Expand Down
2 changes: 1 addition & 1 deletion qa/ccs-unavailable-clusters/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ apply plugin: 'elasticsearch.rest-test'
apply plugin: 'elasticsearch.test-with-dependencies'

dependencies {
testCompile project(path: ':client:rest-high-level', configuration: 'shadow')
testCompile "org.elasticsearch.client:elasticsearch-rest-high-level-client:${version}"
}
5 changes: 3 additions & 2 deletions x-pack/docs/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ buildRestTests.expectedUnconvertedCandidates = [
]

dependencies {
testCompile project(path: xpackModule('core'), configuration: 'shadow')
// "org.elasticsearch.plugin:x-pack-core:${version}" doesn't work with idea because the testArtifacts are also here
testCompile project(path: xpackModule('core'), configuration: 'default')
testCompile project(path: xpackModule('core'), configuration: 'testArtifacts')
testCompile project(path: xpackProject('plugin').path, configuration: 'testArtifacts')
}
Expand Down Expand Up @@ -309,7 +310,7 @@ setups['farequote_datafeed'] = setups['farequote_job'] + '''
"job_id":"farequote",
"indexes":"farequote"
}
'''
'''
setups['ml_filter_safe_domains'] = '''
- do:
xpack.ml.put_filter:
Expand Down
2 changes: 1 addition & 1 deletion x-pack/license-tools/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apply plugin: 'elasticsearch.build'

dependencies {
compile project(path: xpackModule('core'), configuration: 'shadow')
compile "org.elasticsearch.plugin:x-pack-core:${version}"
compile "org.elasticsearch:elasticsearch:${version}"
testCompile "org.elasticsearch.test:framework:${version}"
}
Expand Down
Loading