From 9fb80d3827ff968024cb6628df6aa105264a7b7d Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 14 May 2020 18:56:59 -0700 Subject: [PATCH] Move publishing configuration to a separate plugin (#56727) This is another part of the breakup of the massive BuildPlugin. This PR moves the code for configuring publications to a separate plugin. Most of the time these publications are jar files, but this also supports the zip publication we have for integ tests. --- buildSrc/build.gradle | 2 +- .../elasticsearch/gradle/BuildPlugin.groovy | 208 +----------------- .../gradle/plugin/PluginBuildPlugin.groovy | 21 -- .../gradle/ElasticsearchJavaPlugin.java | 135 ++++++++++++ .../elasticsearch/gradle/PublishPlugin.java | 157 +++++++++++++ .../gradle/precommit/PomValidationPlugin.java | 3 +- .../org/elasticsearch/gradle/util/Util.java | 30 +++ .../elasticsearch.publish.properties | 1 + .../testKit/elasticsearch.build/build.gradle | 3 + client/rest-high-level/build.gradle | 2 +- client/rest/build.gradle | 2 +- client/sniffer/build.gradle | 2 +- distribution/archives/build.gradle | 34 +-- libs/cli/build.gradle | 2 +- libs/core/build.gradle | 2 +- libs/geo/build.gradle | 2 +- libs/nio/build.gradle | 2 +- libs/secure-sm/build.gradle | 2 +- libs/ssl-config/build.gradle | 2 +- libs/x-content/build.gradle | 2 +- modules/lang-painless/spi/build.gradle | 2 +- plugins/transport-nio/build.gradle | 2 +- rest-api-spec/build.gradle | 2 +- server/build.gradle | 2 +- test/build.gradle | 2 +- x-pack/plugin/core/build.gradle | 2 +- x-pack/plugin/identity-provider/build.gradle | 2 +- x-pack/plugin/security/build.gradle | 2 +- x-pack/plugin/sql/jdbc/build.gradle | 2 +- 29 files changed, 351 insertions(+), 281 deletions(-) create mode 100644 buildSrc/src/main/java/org/elasticsearch/gradle/PublishPlugin.java create mode 100644 buildSrc/src/main/resources/META-INF/gradle-plugins/elasticsearch.publish.properties diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 9cd6ab122a4..33ddf6780a8 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -157,7 +157,7 @@ if (project == rootProject) { // to enforce precommit checks like forbidden apis, as well as setup publishing if (project != rootProject) { apply plugin: 'elasticsearch.build' - apply plugin: 'nebula.maven-base-publish' + apply plugin: 'elasticsearch.publish' allprojects { targetCompatibility = 10 diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy index 5b745fca812..3fde4909778 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy @@ -111,8 +111,7 @@ class BuildPlugin implements Plugin { ) } project.pluginManager.apply('elasticsearch.java') - configureJars(project) - configureJarManifest(project) + project.pluginManager.apply('elasticsearch.publish') // apply global test task failure listener project.rootProject.pluginManager.apply(TestFailureReportingPlugin) @@ -121,9 +120,6 @@ class BuildPlugin implements Plugin { configureRepositories(project) project.extensions.getByType(ExtraPropertiesExtension).set('versions', VersionProperties.versions) - configureJavadoc(project) - configureSourcesJar(project) - configurePomGeneration(project) configurePrecommit(project) configureDependenciesInfo(project) configureFips140(project) @@ -304,207 +300,7 @@ class BuildPlugin implements Plugin { throw new GradleException(message) } } - - /**Configuration generation of maven poms. */ - static void configurePomGeneration(Project project) { - // have to defer this until archivesBaseName is set - project.afterEvaluate { - project.pluginManager.withPlugin('maven-publish') { - PublishingExtension publishing = project.extensions.getByType(PublishingExtension) - publishing.publications.withType(MavenPublication) { MavenPublication publication -> - publication.artifactId = project.convention.getPlugin(BasePluginConvention).archivesBaseName - } - } - } - - project.plugins.withType(MavenPublishPlugin).whenPluginAdded { - TaskProvider generatePomTask = project.tasks.register("generatePom") { Task task -> - task.dependsOn 'generatePomFileForNebulaPublication' - } - - maybeConfigure(project.tasks, LifecycleBasePlugin.ASSEMBLE_TASK_NAME) { assemble -> - assemble.dependsOn(generatePomTask) - } - - project.tasks.withType(GenerateMavenPom).configureEach({ GenerateMavenPom pomTask -> - pomTask.destination = { "${project.buildDir}/distributions/${project.convention.getPlugin(BasePluginConvention).archivesBaseName}-${project.version}.pom" } - } as Action) - - PublishingExtension publishing = project.extensions.getByType(PublishingExtension) - - project.pluginManager.withPlugin('com.github.johnrengelman.shadow') { - MavenPublication publication = publishing.publications.maybeCreate('shadow', MavenPublication) - ShadowExtension shadow = project.extensions.getByType(ShadowExtension) - shadow.component(publication) - // Workaround for https://github.com/johnrengelman/shadow/issues/334 - // Here we manually add any project dependencies in the "shadow" configuration to our generated POM - publication.pom.withXml(this.&addScmInfo) - publication.pom.withXml { xml -> - Node root = xml.asNode() - root.appendNode('name', project.name) - root.appendNode('description', project.description) - Node dependenciesNode = (root.get('dependencies') as NodeList).get(0) as Node - project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME).allDependencies.each { dependency -> - if (dependency instanceof ProjectDependency) { - def dependencyNode = dependenciesNode.appendNode('dependency') - dependencyNode.appendNode('groupId', dependency.group) - dependencyNode.appendNode('artifactId', dependency.getDependencyProject().convention.getPlugin(BasePluginConvention).archivesBaseName) - dependencyNode.appendNode('version', dependency.version) - dependencyNode.appendNode('scope', 'compile') - } - } - } - generatePomTask.configure({ Task t -> t.dependsOn = ['generatePomFileForShadowPublication'] } as Action) - } - } - - // Add git origin info to generated POM files - project.pluginManager.withPlugin('nebula.maven-base-publish') { - PublishingExtension publishing = project.extensions.getByType(PublishingExtension) - MavenPublication nebulaPublication = (MavenPublication) publishing.publications.getByName('nebula') - nebulaPublication.pom.withXml(this.&addScmInfo) - } - } - - private static void addScmInfo(XmlProvider xml) { - Node root = xml.asNode() - root.appendNode('url', PluginBuildPlugin.urlFromOrigin(BuildParams.gitOrigin)) - Node scmNode = root.appendNode('scm') - scmNode.appendNode('url', BuildParams.gitOrigin) - } - - static void configureJavadoc(Project project) { - // remove compiled classes from the Javadoc classpath: http://mail.openjdk.java.net/pipermail/javadoc-dev/2018-January/000400.html - final List classes = new ArrayList<>() - project.tasks.withType(JavaCompile).configureEach { JavaCompile javaCompile -> - classes.add(javaCompile.destinationDir) - } - project.tasks.withType(Javadoc).configureEach { Javadoc javadoc -> - // only explicitly set javadoc executable if compiler JDK is different from Gradle - // this ensures better cacheability as setting ths input to an absolute path breaks portability - if (Files.isSameFile(BuildParams.compilerJavaHome.toPath(), Jvm.current().getJavaHome().toPath()) == false) { - javadoc.executable = new File(BuildParams.compilerJavaHome, 'bin/javadoc') - } - javadoc.classpath = javadoc.getClasspath().filter { f -> - return classes.contains(f) == false - } - /* - * Generate docs using html5 to suppress a warning from `javadoc` - * that the default will change to html5 in the future. - */ - (javadoc.options as CoreJavadocOptions).addBooleanOption('html5', true) - } - // ensure javadoc task is run with 'check' - project.pluginManager.withPlugin('lifecycle-base') { - project.tasks.named(LifecycleBasePlugin.CHECK_TASK_NAME).configure { it.dependsOn(project.tasks.withType(Javadoc)) } - } - configureJavadocJar(project) - } - - /** Adds a javadocJar task to generate a jar containing javadocs. */ - static void configureJavadocJar(Project project) { - TaskProvider javadocJarTask = project.tasks.register('javadocJar', Jar, { Jar jar -> - jar.archiveClassifier.set('javadoc') - jar.group = 'build' - jar.description = 'Assembles a jar containing javadocs.' - jar.from(project.tasks.named(JavaPlugin.JAVADOC_TASK_NAME)) - } as Action) - maybeConfigure(project.tasks, BasePlugin.ASSEMBLE_TASK_NAME) { Task t -> - t.dependsOn(javadocJarTask) - } - } - - static void configureSourcesJar(Project project) { - TaskProvider sourcesJarTask = project.tasks.register('sourcesJar', Jar, { Jar jar -> - jar.archiveClassifier.set('sources') - jar.group = 'build' - jar.description = 'Assembles a jar containing source files.' - jar.from(project.extensions.getByType(SourceSetContainer).getByName(SourceSet.MAIN_SOURCE_SET_NAME).allSource) - } as Action) - maybeConfigure(project.tasks, BasePlugin.ASSEMBLE_TASK_NAME) { Task t -> - t.dependsOn(sourcesJarTask) - } - } - - /** Adds additional manifest info to jars */ - static void configureJars(Project project) { - ExtraPropertiesExtension ext = project.extensions.getByType(ExtraPropertiesExtension) - ext.set('licenseFile', null) - ext.set('noticeFile', null) - project.tasks.withType(Jar).configureEach { Jar jarTask -> - // we put all our distributable files under distributions - jarTask.destinationDirectory.set(new File(project.buildDir, 'distributions')) - // fixup the jar manifest - jarTask.doFirst { - // this doFirst is added before the info plugin, therefore it will run - // after the doFirst added by the info plugin, and we can override attributes - JavaVersion compilerJavaVersion = BuildParams.compilerJavaVersion - jarTask.manifest.attributes( - 'Build-Date': BuildParams.buildDate, - 'Build-Java-Version': BuildParams.compilerJavaVersion) - } - } - // add license/notice files - project.afterEvaluate { - project.tasks.withType(Jar).configureEach { Jar jarTask -> - if (ext.has('licenseFile') == false || ext.get('licenseFile') == null || ext.has('noticeFile') == false || ext.get('noticeFile') == null) { - throw new GradleException("Must specify license and notice file for project ${project.path}") - } - - File licenseFile = ext.get('licenseFile') as File - File noticeFile = ext.get('noticeFile') as File - - jarTask.metaInf { CopySpec spec -> - spec.from(licenseFile.parent) { CopySpec from -> - from.include licenseFile.name - from.rename { 'LICENSE.txt' } - } - spec.from(noticeFile.parent) { CopySpec from -> - from.include noticeFile.name - from.rename { 'NOTICE.txt' } - } - } - } - } - project.pluginManager.withPlugin('com.github.johnrengelman.shadow') { - project.tasks.getByName(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME).configure { ShadowJar shadowJar -> - /* - * Replace the default "-all" classifier with null - * which will leave the classifier off of the file name. - */ - shadowJar.archiveClassifier.set((String) null) - /* - * Not all cases need service files merged but it is - * better to be safe - */ - shadowJar.mergeServiceFiles() - } - // Add "original" classifier to the non-shadowed JAR to distinguish it from the shadow JAR - project.tasks.getByName(JavaPlugin.JAR_TASK_NAME).configure { Jar jar -> - jar.archiveClassifier.set('original') - } - // Make sure we assemble the shadow jar - project.tasks.named(BasePlugin.ASSEMBLE_TASK_NAME).configure { Task task -> - task.dependsOn 'shadowJar' - } - } - } - - static void configureJarManifest(Project project) { - project.pluginManager.apply('nebula.info-broker') - project.pluginManager.apply('nebula.info-basic') - project.pluginManager.apply('nebula.info-java') - project.pluginManager.apply('nebula.info-jar') - - project.plugins.withId('nebula.info-broker') { InfoBrokerPlugin manifestPlugin -> - manifestPlugin.add('Module-Origin') { BuildParams.gitOrigin } - manifestPlugin.add('Change') { BuildParams.gitRevision } - manifestPlugin.add('X-Compile-Elasticsearch-Version') { VersionProperties.elasticsearch } - manifestPlugin.add('X-Compile-Lucene-Version') { VersionProperties.lucene } - manifestPlugin.add('X-Compile-Elasticsearch-Snapshot') { VersionProperties.isElasticsearchSnapshot() } - } - } - + private static configurePrecommit(Project project) { TaskProvider precommit = PrecommitTasks.create(project, true) project.tasks.named(LifecycleBasePlugin.CHECK_TASK_NAME).configure { it.dependsOn(precommit) } diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy index 1027d4bae40..1f14b01acf0 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy @@ -241,28 +241,7 @@ class PluginBuildPlugin implements Plugin { project.artifacts.add('zip', bundle) } - /** Adds a task to move jar and associated files to a "-client" name. */ - - static final Pattern GIT_PATTERN = Pattern.compile(/git@([^:]+):([^\.]+)\.git/) - - /** Find the reponame. */ - static String urlFromOrigin(String origin) { - if (origin == null) { - return null // best effort, the url doesnt really matter, it is just required by maven central - } - if (origin.startsWith('https')) { - return origin - } - Matcher matcher = GIT_PATTERN.matcher(origin) - if (matcher.matches()) { - return "https://${matcher.group(1)}/${matcher.group(2)}" - } else { - return origin // best effort, the url doesnt really matter, it is just required by maven central - } - } - /** Configure the pom for the main jar of this plugin */ - protected static void addNoticeGeneration(Project project, PluginPropertiesExtension extension) { File licenseFile = extension.licenseFile if (licenseFile != null) { diff --git a/buildSrc/src/main/java/org/elasticsearch/gradle/ElasticsearchJavaPlugin.java b/buildSrc/src/main/java/org/elasticsearch/gradle/ElasticsearchJavaPlugin.java index 388d0563b2d..4db65c606e9 100644 --- a/buildSrc/src/main/java/org/elasticsearch/gradle/ElasticsearchJavaPlugin.java +++ b/buildSrc/src/main/java/org/elasticsearch/gradle/ElasticsearchJavaPlugin.java @@ -20,6 +20,8 @@ package org.elasticsearch.gradle; import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin; +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar; +import nebula.plugin.info.InfoBrokerPlugin; import org.elasticsearch.gradle.info.BuildParams; import org.elasticsearch.gradle.info.GlobalBuildInfoPlugin; import org.elasticsearch.gradle.test.ErrorReportingTestListener; @@ -35,24 +37,34 @@ import org.gradle.api.artifacts.ModuleDependency; import org.gradle.api.artifacts.ProjectDependency; import org.gradle.api.artifacts.ResolutionStrategy; import org.gradle.api.file.FileCollection; +import org.gradle.api.plugins.BasePlugin; +import org.gradle.api.plugins.ExtraPropertiesExtension; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.api.tasks.bundling.Jar; import org.gradle.api.tasks.compile.CompileOptions; import org.gradle.api.tasks.compile.GroovyCompile; import org.gradle.api.tasks.compile.JavaCompile; +import org.gradle.api.tasks.javadoc.Javadoc; import org.gradle.api.tasks.testing.Test; +import org.gradle.external.javadoc.CoreJavadocOptions; import org.gradle.internal.jvm.Jvm; +import org.gradle.language.base.plugins.LifecycleBasePlugin; import java.io.File; import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; import static org.elasticsearch.gradle.util.GradleUtils.maybeConfigure; +import static org.elasticsearch.gradle.util.Util.toStringable; /** * A wrapper around Gradle's Java plugin that applies our common configuration. @@ -68,6 +80,9 @@ public class ElasticsearchJavaPlugin implements Plugin { configureCompile(project); configureInputNormalization(project); configureTestTasks(project); + configureJars(project); + configureJarManifest(project); + configureJavadoc(project); } /** @@ -382,4 +397,124 @@ public class ElasticsearchJavaPlugin implements Plugin { }); }); } + + /** Adds additional manifest info to jars */ + static void configureJars(Project project) { + ExtraPropertiesExtension ext = project.getExtensions().getExtraProperties(); + ext.set("licenseFile", null); + ext.set("noticeFile", null); + project.getTasks() + .withType(Jar.class) + .configureEach( + jarTask -> { + // we put all our distributable files under distributions + jarTask.getDestinationDirectory().set(new File(project.getBuildDir(), "distributions")); + // fixup the jar manifest + jarTask.doFirst( + t -> { + // this doFirst is added before the info plugin, therefore it will run + // after the doFirst added by the info plugin, and we can override attributes + jarTask.getManifest() + .attributes( + Map.of( + "Build-Date", + BuildParams.getBuildDate(), + "Build-Java-Version", + BuildParams.getCompilerJavaVersion() + ) + ); + } + ); + } + ); + // add license/notice files + project.afterEvaluate(p -> project.getTasks().withType(Jar.class).configureEach(jarTask -> { + File licenseFile = (File) ext.get("licenseFile"); + File noticeFile = (File) ext.get("noticeFile"); + if (licenseFile == null || noticeFile == null) { + throw new GradleException("Must specify license and notice file for project"); + } + + jarTask.metaInf(spec -> { + spec.from(licenseFile.getParent(), from -> { + from.include(licenseFile.getName()); + from.rename(s -> "LICENSE.txt"); + }); + spec.from(noticeFile.getParent(), from -> { + from.include(noticeFile.getName()); + from.rename(s -> "NOTICE.txt"); + }); + }); + })); + project.getPluginManager().withPlugin("com.github.johnrengelman.shadow", p -> { + project.getTasks() + .withType(ShadowJar.class) + .configureEach( + shadowJar -> { + /* + * Replace the default "-all" classifier with null + * which will leave the classifier off of the file name. + */ + shadowJar.getArchiveClassifier().set((String) null); + /* + * Not all cases need service files merged but it is + * better to be safe + */ + shadowJar.mergeServiceFiles(); + } + ); + // Add "original" classifier to the non-shadowed JAR to distinguish it from the shadow JAR + project.getTasks().named(JavaPlugin.JAR_TASK_NAME, Jar.class).configure(jar -> jar.getArchiveClassifier().set("original")); + // Make sure we assemble the shadow jar + project.getTasks().named(BasePlugin.ASSEMBLE_TASK_NAME).configure(task -> task.dependsOn("shadowJar")); + }); + } + + private static void configureJarManifest(Project project) { + project.getPlugins().withType(InfoBrokerPlugin.class).whenPluginAdded(manifestPlugin -> { + manifestPlugin.add("Module-Origin", toStringable(BuildParams::getGitOrigin)); + manifestPlugin.add("Change", toStringable(BuildParams::getGitRevision)); + manifestPlugin.add("X-Compile-Elasticsearch-Version", toStringable(VersionProperties::getElasticsearch)); + manifestPlugin.add("X-Compile-Lucene-Version", toStringable(VersionProperties::getLucene)); + manifestPlugin.add( + "X-Compile-Elasticsearch-Snapshot", + toStringable(() -> Boolean.toString(VersionProperties.isElasticsearchSnapshot())) + ); + }); + + project.getPluginManager().apply("nebula.info-broker"); + project.getPluginManager().apply("nebula.info-basic"); + project.getPluginManager().apply("nebula.info-java"); + project.getPluginManager().apply("nebula.info-jar"); + } + + private static void configureJavadoc(Project project) { + project.getTasks().withType(Javadoc.class).configureEach(javadoc -> { + // only explicitly set javadoc executable if compiler JDK is different from Gradle + // this ensures better cacheability as setting ths input to an absolute path breaks portability + Path compilerJvm = BuildParams.getCompilerJavaHome().toPath(); + Path gradleJvm = Jvm.current().getJavaHome().toPath(); + try { + if (Files.isSameFile(compilerJvm, gradleJvm) == false) { + javadoc.setExecutable(compilerJvm.resolve("bin/javadoc").toString()); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + // remove compiled classes from the Javadoc classpath: + // http://mail.openjdk.java.net/pipermail/javadoc-dev/2018-January/000400.html + javadoc.setClasspath(Util.getJavaMainSourceSet(project).get().getCompileClasspath()); + /* + * Generate docs using html5 to suppress a warning from `javadoc` + * that the default will change to html5 in the future. + */ + CoreJavadocOptions javadocOptions = (CoreJavadocOptions) javadoc.getOptions(); + javadocOptions.addBooleanOption("html5", true); + }); + // ensure javadoc task is run with 'check' + project.getTasks() + .named(LifecycleBasePlugin.CHECK_TASK_NAME) + .configure(t -> t.dependsOn(project.getTasks().withType(Javadoc.class))); + } } diff --git a/buildSrc/src/main/java/org/elasticsearch/gradle/PublishPlugin.java b/buildSrc/src/main/java/org/elasticsearch/gradle/PublishPlugin.java new file mode 100644 index 00000000000..857f5c73316 --- /dev/null +++ b/buildSrc/src/main/java/org/elasticsearch/gradle/PublishPlugin.java @@ -0,0 +1,157 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.gradle; + +import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin; +import com.github.jengelman.gradle.plugins.shadow.ShadowExtension; +import groovy.util.Node; +import groovy.util.NodeList; +import org.elasticsearch.gradle.info.BuildParams; +import org.elasticsearch.gradle.util.Util; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.Task; +import org.gradle.api.XmlProvider; +import org.gradle.api.artifacts.ProjectDependency; +import org.gradle.api.plugins.BasePlugin; +import org.gradle.api.plugins.BasePluginConvention; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.publish.PublishingExtension; +import org.gradle.api.publish.maven.MavenPublication; +import org.gradle.api.publish.maven.tasks.GenerateMavenPom; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.bundling.Jar; +import org.gradle.language.base.plugins.LifecycleBasePlugin; + +import java.util.concurrent.Callable; + +import static org.elasticsearch.gradle.util.GradleUtils.maybeConfigure; + +public class PublishPlugin implements Plugin { + + @Override + public void apply(Project project) { + project.getPluginManager().apply("nebula.maven-base-publish"); + + configureJavadocJar(project); + configureSourcesJar(project); + configurePomGeneration(project); + } + + private static String getArchivesBaseName(Project project) { + return project.getConvention().getPlugin(BasePluginConvention.class).getArchivesBaseName(); + } + + /**Configuration generation of maven poms. */ + private static void configurePomGeneration(Project project) { + + TaskProvider generatePomTask = project.getTasks().register("generatePom"); + + maybeConfigure(project.getTasks(), LifecycleBasePlugin.ASSEMBLE_TASK_NAME, assemble -> assemble.dependsOn(generatePomTask)); + + project.getTasks().withType(GenerateMavenPom.class).configureEach(pomTask -> pomTask.setDestination(new Callable() { + @Override + public String call() throws Exception { + return String.format( + "%s/distributions/%s-%s.pom", + project.getBuildDir(), + getArchivesBaseName(project), + project.getVersion() + ); + } + })); + + PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class); + + project.getPluginManager().withPlugin("com.github.johnrengelman.shadow", plugin -> { + MavenPublication publication = publishing.getPublications().maybeCreate("shadow", MavenPublication.class); + ShadowExtension shadow = project.getExtensions().getByType(ShadowExtension.class); + shadow.component(publication); + // Workaround for https://github.com/johnrengelman/shadow/issues/334 + // Here we manually add any project dependencies in the "shadow" configuration to our generated POM + publication.getPom().withXml(xml -> { + Node root = xml.asNode(); + root.appendNode("name", project.getName()); + root.appendNode("description", project.getDescription()); + Node dependenciesNode = (Node) ((NodeList) root.get("dependencies")).get(0); + project.getConfigurations().getByName(ShadowBasePlugin.getCONFIGURATION_NAME()).getAllDependencies().all(dependency -> { + if (dependency instanceof ProjectDependency) { + Node dependencyNode = dependenciesNode.appendNode("dependency"); + dependencyNode.appendNode("groupId", dependency.getGroup()); + ProjectDependency projectDependency = (ProjectDependency) dependency; + String artifactId = getArchivesBaseName(projectDependency.getDependencyProject()); + dependencyNode.appendNode("artifactId", artifactId); + dependencyNode.appendNode("version", dependency.getVersion()); + dependencyNode.appendNode("scope", "compile"); + } + }); + }); + }); + + // Add git origin info to generated POM files + publishing.getPublications().withType(MavenPublication.class, publication -> { + publication.getPom().withXml(PublishPlugin::addScmInfo); + + // have to defer this until archivesBaseName is set + project.afterEvaluate(p -> publication.setArtifactId(getArchivesBaseName(project))); + + generatePomTask.configure( + t -> t.dependsOn(String.format("generatePomFileFor%sPublication", Util.capitalize(publication.getName()))) + ); + }); + + } + + private static void addScmInfo(XmlProvider xml) { + Node root = xml.asNode(); + root.appendNode("url", Util.urlFromOrigin(BuildParams.getGitOrigin())); + Node scmNode = root.appendNode("scm"); + scmNode.appendNode("url", BuildParams.getGitOrigin()); + } + + /** Adds a javadocJar task to generate a jar containing javadocs. */ + private static void configureJavadocJar(Project project) { + project.getPlugins().withId("elasticsearch.java", p -> { + TaskProvider javadocJarTask = project.getTasks().register("javadocJar", Jar.class); + javadocJarTask.configure(jar -> { + jar.getArchiveClassifier().set("javadoc"); + jar.setGroup("build"); + jar.setDescription("Assembles a jar containing javadocs."); + jar.from(project.getTasks().named(JavaPlugin.JAVADOC_TASK_NAME)); + }); + maybeConfigure(project.getTasks(), BasePlugin.ASSEMBLE_TASK_NAME, t -> t.dependsOn(javadocJarTask)); + }); + } + + static void configureSourcesJar(Project project) { + project.getPlugins().withId("elasticsearch.java", p -> { + TaskProvider sourcesJarTask = project.getTasks().register("sourcesJar", Jar.class); + sourcesJarTask.configure(jar -> { + jar.getArchiveClassifier().set("sources"); + jar.setGroup("build"); + jar.setDescription("Assembles a jar containing source files."); + SourceSet mainSourceSet = Util.getJavaMainSourceSet(project).get(); + jar.from(mainSourceSet.getAllSource()); + }); + maybeConfigure(project.getTasks(), BasePlugin.ASSEMBLE_TASK_NAME, t -> t.dependsOn(sourcesJarTask)); + }); + } +} diff --git a/buildSrc/src/main/java/org/elasticsearch/gradle/precommit/PomValidationPlugin.java b/buildSrc/src/main/java/org/elasticsearch/gradle/precommit/PomValidationPlugin.java index c2e5ef786a1..c6ed039de90 100644 --- a/buildSrc/src/main/java/org/elasticsearch/gradle/precommit/PomValidationPlugin.java +++ b/buildSrc/src/main/java/org/elasticsearch/gradle/precommit/PomValidationPlugin.java @@ -23,7 +23,6 @@ import org.elasticsearch.gradle.util.Util; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.publish.PublishingExtension; -import org.gradle.api.publish.maven.plugins.MavenPublishPlugin; import org.gradle.api.publish.maven.tasks.GenerateMavenPom; import org.gradle.api.tasks.TaskProvider; @@ -34,7 +33,7 @@ public class PomValidationPlugin implements Plugin { @Override public void apply(Project project) { - project.getPlugins().withType(MavenPublishPlugin.class).whenPluginAdded(p -> { + project.getPluginManager().withPlugin("maven-publish", p -> { PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class); publishing.getPublications().all(publication -> { String publicationName = Util.capitalize(publication.getName()); diff --git a/buildSrc/src/main/java/org/elasticsearch/gradle/util/Util.java b/buildSrc/src/main/java/org/elasticsearch/gradle/util/Util.java index bd29fa08e9a..94cbc6b53db 100644 --- a/buildSrc/src/main/java/org/elasticsearch/gradle/util/Util.java +++ b/buildSrc/src/main/java/org/elasticsearch/gradle/util/Util.java @@ -37,6 +37,9 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.Locale; import java.util.Optional; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class Util { @@ -144,4 +147,31 @@ public class Util { ? Optional.empty() : Optional.ofNullable(GradleUtils.getJavaSourceSets(project).findByName(SourceSet.MAIN_SOURCE_SET_NAME)); } + + static final Pattern GIT_PATTERN = Pattern.compile("git@([^:]+):([^\\.]+)\\.git"); + + /** Find the reponame. */ + public static String urlFromOrigin(String origin) { + if (origin == null) { + return null; // best effort, the url doesnt really matter, it is just required by maven central + } + if (origin.startsWith("https")) { + return origin; + } + Matcher matcher = GIT_PATTERN.matcher(origin); + if (matcher.matches()) { + return String.format("https://%s/%s", matcher.group(1), matcher.group(2)); + } else { + return origin; // best effort, the url doesnt really matter, it is just required by maven central + } + } + + public static Object toStringable(Supplier getter) { + return new Object() { + @Override + public String toString() { + return getter.get(); + } + }; + } } diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/elasticsearch.publish.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/elasticsearch.publish.properties new file mode 100644 index 00000000000..52fa3b3da30 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/elasticsearch.publish.properties @@ -0,0 +1 @@ +implementation-class=org.elasticsearch.gradle.PublishPlugin diff --git a/buildSrc/src/testKit/elasticsearch.build/build.gradle b/buildSrc/src/testKit/elasticsearch.build/build.gradle index b505a11b519..96df6ee783a 100644 --- a/buildSrc/src/testKit/elasticsearch.build/build.gradle +++ b/buildSrc/src/testKit/elasticsearch.build/build.gradle @@ -27,6 +27,9 @@ thirdPartyAudit.enabled = false // This requires an additional Jar not part of build-tools loggerUsageCheck.enabled = false +// TODO: shouldn't be part of BuildPlugin, should be tested separately +validateNebulaPom.enabled = false + task hello { doFirst { println "build plugin can be applied" diff --git a/client/rest-high-level/build.gradle b/client/rest-high-level/build.gradle index d3798d0cb9c..80cd25de5e6 100644 --- a/client/rest-high-level/build.gradle +++ b/client/rest-high-level/build.gradle @@ -22,7 +22,7 @@ import org.elasticsearch.gradle.info.BuildParams apply plugin: 'elasticsearch.testclusters' apply plugin: 'elasticsearch.build' apply plugin: 'elasticsearch.rest-test' -apply plugin: 'nebula.maven-base-publish' +apply plugin: 'elasticsearch.publish' apply plugin: 'elasticsearch.rest-resources' group = 'org.elasticsearch.client' diff --git a/client/rest/build.gradle b/client/rest/build.gradle index b9af6f7ca58..9d1c838cf65 100644 --- a/client/rest/build.gradle +++ b/client/rest/build.gradle @@ -19,7 +19,7 @@ import de.thetaphi.forbiddenapis.gradle.CheckForbiddenApis * under the License. */ apply plugin: 'elasticsearch.build' -apply plugin: 'nebula.maven-base-publish' +apply plugin: 'elasticsearch.publish' targetCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8 diff --git a/client/sniffer/build.gradle b/client/sniffer/build.gradle index 437b3613133..aceaf6dd4e5 100644 --- a/client/sniffer/build.gradle +++ b/client/sniffer/build.gradle @@ -17,7 +17,7 @@ * under the License. */ apply plugin: 'elasticsearch.build' -apply plugin: 'nebula.maven-base-publish' +apply plugin: 'elasticsearch.publish' targetCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8 diff --git a/distribution/archives/build.gradle b/distribution/archives/build.gradle index 69a690ecfe6..50f4152b081 100644 --- a/distribution/archives/build.gradle +++ b/distribution/archives/build.gradle @@ -353,10 +353,8 @@ configure(subprojects.findAll { it.name == 'integ-test-zip' }) { MavenFilteringHack.filter(it, project(':distribution').restTestExpansions) } - // The integ-test-distribution is published to maven - BuildPlugin.configurePomGeneration(project) - apply plugin: 'nebula.maven-base-publish' + apply plugin: 'elasticsearch.publish' // make the pom file name use elasticsearch instead of the project name archivesBaseName = "elasticsearch${it.name.contains('oss') ? '-oss' : ''}" @@ -364,37 +362,9 @@ configure(subprojects.findAll { it.name == 'integ-test-zip' }) { publishing { publications { nebula { - artifactId archivesBaseName + pom.packaging = 'zip' artifact(buildDist.flatMap { it.archiveFile }) } - /* - * HUGE HACK: the underlying maven publication library refuses to - * deploy any attached artifacts when the packaging type is set to - * 'pom'. But Sonatype's OSS repositories require source files for - * artifacts that are of type 'zip'. We already publish the source - * and javadoc for Elasticsearch under the various other subprojects. - * So here we create another publication using the same name that - * has the "real" pom, and rely on the fact that gradle will execute - * the publish tasks in alphabetical order. This lets us publish the - * zip file and even though the pom says the type is 'pom' instead of - * 'zip'. We cannot setup a dependency between the tasks because the - * publishing tasks are created *extremely* late in the configuration - * phase, so that we cannot get ahold of the actual task. Furthermore, - * this entire hack only exists so we can make publishing to maven - * local work, since we publish to maven central externally. - */ - nebulaRealPom(MavenPublication) { - artifactId archivesBaseName - pom.packaging = 'pom' - pom.withXml { XmlProvider xml -> - Node root = xml.asNode() - root.appendNode('name', 'Elasticsearch') - root.appendNode('description', 'A Distributed RESTful Search Engine') - root.appendNode('url', PluginBuildPlugin.urlFromOrigin(BuildParams.gitOrigin)) - Node scmNode = root.appendNode('scm') - scmNode.appendNode('url', BuildParams.gitOrigin) - } - } } } } diff --git a/libs/cli/build.gradle b/libs/cli/build.gradle index 4363d4c1398..87005bf6f54 100644 --- a/libs/cli/build.gradle +++ b/libs/cli/build.gradle @@ -18,7 +18,7 @@ */ apply plugin: 'elasticsearch.build' apply plugin: 'nebula.optional-base' -apply plugin: 'nebula.maven-base-publish' +apply plugin: 'elasticsearch.publish' dependencies { compile 'net.sf.jopt-simple:jopt-simple:5.0.2' diff --git a/libs/core/build.gradle b/libs/core/build.gradle index 5c63c6d5309..40c81b58c4b 100644 --- a/libs/core/build.gradle +++ b/libs/core/build.gradle @@ -20,7 +20,7 @@ import org.elasticsearch.gradle.info.BuildParams */ apply plugin: 'nebula.optional-base' -apply plugin: 'nebula.maven-base-publish' +apply plugin: 'elasticsearch.publish' archivesBaseName = 'elasticsearch-core' diff --git a/libs/geo/build.gradle b/libs/geo/build.gradle index 38055d92810..5fc5c0a2364 100644 --- a/libs/geo/build.gradle +++ b/libs/geo/build.gradle @@ -18,7 +18,7 @@ */ apply plugin: 'elasticsearch.build' -apply plugin: 'nebula.maven-base-publish' +apply plugin: 'elasticsearch.publish' dependencies { testCompile(project(":test:framework")) { diff --git a/libs/nio/build.gradle b/libs/nio/build.gradle index 4661e56a2e9..54e542d081e 100644 --- a/libs/nio/build.gradle +++ b/libs/nio/build.gradle @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -apply plugin: 'nebula.maven-base-publish' +apply plugin: 'elasticsearch.publish' dependencies { compile project(':libs:elasticsearch-core') diff --git a/libs/secure-sm/build.gradle b/libs/secure-sm/build.gradle index 254b8d2f979..9182a67b726 100644 --- a/libs/secure-sm/build.gradle +++ b/libs/secure-sm/build.gradle @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -apply plugin: 'nebula.maven-base-publish' +apply plugin: 'elasticsearch.publish' dependencies { // do not add non-test compile dependencies to secure-sm without a good reason to do so diff --git a/libs/ssl-config/build.gradle b/libs/ssl-config/build.gradle index ab6ec04bc84..83cb8648c47 100644 --- a/libs/ssl-config/build.gradle +++ b/libs/ssl-config/build.gradle @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -apply plugin: "nebula.maven-base-publish" +apply plugin: "elasticsearch.publish" dependencies { compile project(':libs:elasticsearch-core') diff --git a/libs/x-content/build.gradle b/libs/x-content/build.gradle index 9819417f262..073035b63ff 100644 --- a/libs/x-content/build.gradle +++ b/libs/x-content/build.gradle @@ -18,7 +18,7 @@ */ apply plugin: 'elasticsearch.build' -apply plugin: 'nebula.maven-base-publish' +apply plugin: 'elasticsearch.publish' dependencies { compile project(':libs:elasticsearch-core') diff --git a/modules/lang-painless/spi/build.gradle b/modules/lang-painless/spi/build.gradle index 32604c0f84f..ec8a569020f 100644 --- a/modules/lang-painless/spi/build.gradle +++ b/modules/lang-painless/spi/build.gradle @@ -18,7 +18,7 @@ */ apply plugin: 'elasticsearch.build' -apply plugin: 'nebula.maven-base-publish' +apply plugin: 'elasticsearch.publish' group = 'org.elasticsearch.plugin' archivesBaseName = 'elasticsearch-scripting-painless-spi' diff --git a/plugins/transport-nio/build.gradle b/plugins/transport-nio/build.gradle index e93166091ea..eee161bac7c 100644 --- a/plugins/transport-nio/build.gradle +++ b/plugins/transport-nio/build.gradle @@ -18,7 +18,7 @@ import org.elasticsearch.gradle.info.BuildParams * specific language governing permissions and limitations * under the License. */ -apply plugin: "nebula.maven-base-publish" +apply plugin: "elasticsearch.publish" esplugin { description 'The nio transport.' diff --git a/rest-api-spec/build.gradle b/rest-api-spec/build.gradle index 2d3aed0623e..3f9224b981a 100644 --- a/rest-api-spec/build.gradle +++ b/rest-api-spec/build.gradle @@ -1,5 +1,5 @@ apply plugin: 'elasticsearch.build' -apply plugin: 'nebula.maven-base-publish' +apply plugin: 'elasticsearch.publish' apply plugin: 'elasticsearch.rest-resources' apply plugin: 'elasticsearch.validate-rest-spec' diff --git a/server/build.gradle b/server/build.gradle index 6e5aaa03013..ad540d99fae 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -21,7 +21,7 @@ import org.elasticsearch.gradle.info.BuildParams apply plugin: 'elasticsearch.build' apply plugin: 'nebula.optional-base' -apply plugin: 'nebula.maven-base-publish' +apply plugin: 'elasticsearch.publish' apply plugin: 'elasticsearch.internal-cluster-test' publishing { diff --git a/test/build.gradle b/test/build.gradle index b5bae1a951f..b136abc1d73 100644 --- a/test/build.gradle +++ b/test/build.gradle @@ -23,7 +23,7 @@ subprojects { group = 'org.elasticsearch.test' apply plugin: 'elasticsearch.build' - apply plugin: 'nebula.maven-base-publish' + apply plugin: 'elasticsearch.publish' // TODO: should we have licenses for our test deps? dependencyLicenses.enabled = false diff --git a/x-pack/plugin/core/build.gradle b/x-pack/plugin/core/build.gradle index 6a3cf327a3c..f56332dada1 100644 --- a/x-pack/plugin/core/build.gradle +++ b/x-pack/plugin/core/build.gradle @@ -5,7 +5,7 @@ import java.nio.file.Files import java.nio.file.Paths apply plugin: 'elasticsearch.esplugin' -apply plugin: 'nebula.maven-base-publish' +apply plugin: 'elasticsearch.publish' apply plugin: 'elasticsearch.internal-cluster-test' archivesBaseName = 'x-pack-core' diff --git a/x-pack/plugin/identity-provider/build.gradle b/x-pack/plugin/identity-provider/build.gradle index 5355bc188e0..2fe327e74c3 100644 --- a/x-pack/plugin/identity-provider/build.gradle +++ b/x-pack/plugin/identity-provider/build.gradle @@ -3,7 +3,7 @@ import org.elasticsearch.gradle.info.BuildParams evaluationDependsOn(xpackModule('core')) apply plugin: 'elasticsearch.esplugin' -apply plugin: 'nebula.maven-base-publish' +apply plugin: 'elasticsearch.publish' esplugin { name 'x-pack-identity-provider' description 'Elasticsearch Expanded Pack Plugin - Identity Provider' diff --git a/x-pack/plugin/security/build.gradle b/x-pack/plugin/security/build.gradle index d7c91940465..e3a9aa2e7c7 100644 --- a/x-pack/plugin/security/build.gradle +++ b/x-pack/plugin/security/build.gradle @@ -3,7 +3,7 @@ import org.elasticsearch.gradle.info.BuildParams evaluationDependsOn(xpackModule('core')) apply plugin: 'elasticsearch.esplugin' -apply plugin: 'nebula.maven-base-publish' +apply plugin: 'elasticsearch.publish' esplugin { name 'x-pack-security' description 'Elasticsearch Expanded Pack Plugin - Security' diff --git a/x-pack/plugin/sql/jdbc/build.gradle b/x-pack/plugin/sql/jdbc/build.gradle index 42ced945167..153f696c1b2 100644 --- a/x-pack/plugin/sql/jdbc/build.gradle +++ b/x-pack/plugin/sql/jdbc/build.gradle @@ -1,5 +1,5 @@ apply plugin: 'elasticsearch.build' -apply plugin: 'nebula.maven-base-publish' +apply plugin: 'elasticsearch.publish' apply plugin: 'com.github.johnrengelman.shadow' description = 'JDBC driver for Elasticsearch'