/* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0 and the Server Side Public License, v 1; you may not use this file except * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ import com.avast.gradle.dockercompose.tasks.ComposePull import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin import de.thetaphi.forbiddenapis.gradle.ForbiddenApisPlugin import org.elasticsearch.gradle.internal.BuildPlugin import org.elasticsearch.gradle.Version import org.elasticsearch.gradle.VersionProperties import org.elasticsearch.gradle.internal.BwcVersions import org.elasticsearch.gradle.internal.info.BuildParams import org.elasticsearch.gradle.plugin.PluginBuildPlugin import org.gradle.plugins.ide.eclipse.model.AccessRule import org.gradle.util.DistributionLocator import org.gradle.util.GradleVersion import org.elasticsearch.gradle.util.GradleUtils import static org.elasticsearch.gradle.util.GradleUtils.maybeConfigure import org.gradle.plugins.ide.eclipse.model.ProjectDependency import org.elasticsearch.gradle.internal.InternalPluginBuildPlugin import org.elasticsearch.gradle.internal.ResolveAllDependencies import java.nio.file.Files import static java.nio.file.StandardCopyOption.REPLACE_EXISTING plugins { id 'lifecycle-base' id 'elasticsearch.docker-support' id 'elasticsearch.global-build-info' id 'elasticsearch.build-scan' id 'elasticsearch.build-complete' id 'elasticsearch.jdk-download' id 'elasticsearch.internal-distribution-download' id 'elasticsearch.runtime-jdk-provision' id 'elasticsearch.ide' id 'elasticsearch.forbidden-dependencies' id 'elasticsearch.local-distribution' id 'elasticsearch.fips' id 'elasticsearch.internal-testclusters' id 'elasticsearch.run' id 'elasticsearch.release-tools' id 'elasticsearch.versions' } /** * This is a convenient method for declaring test artifact dependencies provided by the internal * test artifact plugin. It replaces basically the longer dependency notation with explicit capability * declaration like this: * * testImplementation(project(xpackModule('repositories-metering-api'))) { * capabilities { * requireCapability("org.elasticsearch.gradle:repositories-metering-api-test-artifacts") * } * } * * */ ext.testArtifact = { p, String name = "test" -> def projectDependency = p.dependencies.create(p) projectDependency.capabilities { requireCapabilities("org.elasticsearch.gradle:${projectDependency.name}-${name}-artifacts") }; } tasks.register("updateCIBwcVersions") { doLast { File yml = file(".ci/bwcVersions") yml.text = "" yml << "BWC_VERSION:\n" BuildParams.bwcVersions.indexCompatible.each { yml << " - \"$it\"\n" } } } tasks.register("verifyVersions") { doLast { if (gradle.startParameter.isOffline()) { throw new GradleException("Must run in online mode to verify versions") } // Read the list from maven central. // Fetch the metadata and parse the xml into Version instances because it's more straight forward here // rather than bwcVersion ( VersionCollection ). new URL('https://repo1.maven.org/maven2/org/elasticsearch/elasticsearch/maven-metadata.xml').openStream().withStream { s -> BuildParams.bwcVersions.compareToAuthoritative( new XmlParser().parse(s) .versioning.versions.version .collect { it.text() }.findAll { it ==~ /\d+\.\d+\.\d+/ } .collect { Version.fromString(it) } ) } String ciYml = file(".ci/bwcVersions").text BuildParams.bwcVersions.indexCompatible.each { if (ciYml.contains("\"$it\"\n") == false) { throw new Exception(".ci/bwcVersions is outdated, run `./gradlew updateCIBwcVersions` and check in the results"); } } // Make sure backport bot config file is up to date JsonNode backportConfig = new ObjectMapper().readTree(file(".backportrc.json")) List<BwcVersions.UnreleasedVersionInfo> unreleased = BuildParams.bwcVersions.unreleased.collect { BuildParams.bwcVersions.unreleasedInfo(it) } unreleased.each { unreleasedVersion -> boolean valid = backportConfig.get("targetBranchChoices").elements().any { branchChoice -> if (branchChoice.isObject()) { return branchChoice.get("name").textValue() == unreleasedVersion.branch } else { return branchChoice.textValue() == unreleasedVersion.branch } } if (valid == false) { throw new GradleException("No branch choice exists for development branch ${unreleasedVersion.branch} in .backportrc.json.") } } BwcVersions.UnreleasedVersionInfo nextMinor = unreleased.find { it.branch.endsWith("x") } String versionMapping = backportConfig.get("branchLabelMapping").fields().find { it.value.textValue() == nextMinor.branch }.key if (versionMapping != "^v${nextMinor.version}\$") { throw new GradleException("Backport label mapping for branch ${nextMinor.branch} is '${versionMapping}' but should be " + "'^v${nextMinor.version}\$'. Update .backportrc.json.") } } } /* * When adding backcompat behavior that spans major versions, temporarily * disabling the backcompat tests is necessary. This flag controls * the enabled state of every bwc task. It should be set back to true * after the backport of the backcompat code is complete. */ boolean bwc_tests_enabled = true // place a PR link here when committing bwc changes: String bwc_tests_disabled_issue = "" /* * FIPS 140-2 behavior was fixed in 7.11.0. Before that there is no way to run elasticsearch in a * JVM that is properly configured to be in fips mode with BCFIPS. For now we need to disable * all bwc testing in fips mode. */ if ( BuildParams.inFipsJvm ) { bwc_tests_enabled = false bwc_tests_disabled_issue = "https://github.com/elastic/elasticsearch/issues/66772" } if (bwc_tests_enabled == false) { if (bwc_tests_disabled_issue.isEmpty()) { throw new GradleException("bwc_tests_disabled_issue must be set when bwc_tests_enabled == false") } println "========================= WARNING =========================" println " Backwards compatibility tests are disabled!" println "See ${bwc_tests_disabled_issue}" println "===========================================================" } if (project.gradle.startParameter.taskNames.find { it.startsWith("checkPart") } != null) { // Disable BWC tests for checkPart* tasks as it's expected that this will run un it's own check bwc_tests_enabled = false } subprojects { apply plugin: 'elasticsearch.base' } allprojects { // We disable this plugin for now till we shaked out the issues we see // e.g. see https://github.com/elastic/elasticsearch/issues/72169 // apply plugin:'elasticsearch.internal-test-rerun' plugins.withType(InternalPluginBuildPlugin).whenPluginAdded { project.dependencies { compileOnly project(":server") testImplementation project(":test:framework") } } // injecting groovy property variables into all projects project.ext { // for ide hacks... isEclipse = providers.systemProperty("eclipse.launcher").forUseAtConfigurationTime().isPresent() || // Detects gradle launched from Eclipse's IDE providers.systemProperty("eclipse.application").forUseAtConfigurationTime().isPresent() || // Detects gradle launched from the Eclipse compiler server gradle.startParameter.taskNames.contains('eclipse') || // Detects gradle launched from the command line to do eclipse stuff gradle.startParameter.taskNames.contains('cleanEclipse') } ext.bwc_tests_enabled = bwc_tests_enabled // ignore missing javadocs tasks.withType(Javadoc).configureEach { Javadoc javadoc -> // the -quiet here is because of a bug in gradle, in that adding a string option // by itself is not added to the options. By adding quiet, both this option and // the "value" -quiet is added, separated by a space. This is ok since the javadoc // command already adds -quiet, so we are just duplicating it // see https://discuss.gradle.org/t/add-custom-javadoc-option-that-does-not-take-an-argument/5959 javadoc.options.encoding = 'UTF8' javadoc.options.addStringOption('Xdoclint:all,-missing', '-quiet') } // eclipse configuration apply plugin: 'elasticsearch.eclipse' /* * Allow accessing com/sun/net/httpserver in projects that have * configured forbidden apis to allow it. */ plugins.withType(ForbiddenApisPlugin) { eclipse.classpath.file.whenMerged { classpath -> if (false == forbiddenApisTest.bundledSignatures.contains('jdk-non-portable')) { classpath.entries .findAll { it.kind == "con" && it.toString().contains("org.eclipse.jdt.launching.JRE_CONTAINER") } .each { it.accessRules.add(new AccessRule("accessible", "com/sun/net/httpserver/*")) } } } } tasks.register('resolveAllDependencies', ResolveAllDependencies) { configs = project.configurations if (project.path.contains("fixture")) { dependsOn tasks.withType(ComposePull) } } def checkPart1 = tasks.register('checkPart1') def checkPart2 = tasks.register('checkPart2') plugins.withId('lifecycle-base') { if (project.path.startsWith(":x-pack:")) { checkPart2.configure { dependsOn 'check' } } else { checkPart1.configure { dependsOn 'check' } } } project.ext.disableTasks = { String... tasknames -> for (String taskname : tasknames) { project.tasks.named(taskname).configure { enabled = false } } } /* * Remove assemble/dependenciesInfo on all qa projects because we don't * need to publish artifacts for them. */ if (project.name.equals('qa') || project.path.contains(':qa:')) { maybeConfigure(project.tasks, 'assemble') { it.enabled = false } maybeConfigure(project.tasks, 'dependenciesInfo') { it.enabled = false } maybeConfigure(project.tasks, 'dependenciesGraph') { it.enabled = false } } project.afterEvaluate { // Ensure similar tasks in dependent projects run first. The projectsEvaluated here is // important because, while dependencies.all will pickup future dependencies, // it is not necessarily true that the task exists in both projects at the time // the dependency is added. if (project.path == ':test:framework') { // :test:framework:test cannot run before and after :server:test return } tasks.matching { it.name.equals('integTest')}.configureEach {integTestTask -> integTestTask.mustRunAfter tasks.matching { it.name.equals("test") } } configurations.matching { it.canBeResolved }.all { Configuration configuration -> dependencies.matching { it instanceof ProjectDependency }.all { ProjectDependency dep -> Project upstreamProject = dep.dependencyProject if (project.path != upstreamProject?.path) { for (String taskName : ['test', 'integTest']) { project.tasks.matching { it.name == taskName }.configureEach {task -> task.shouldRunAfter(upstreamProject.tasks.matching { upStreamTask -> upStreamTask.name == taskName }) } } } } } // Handle javadoc dependencies across projects. Order matters: the linksOffline for // 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) || project.plugins.hasPlugin(PluginBuildPlugin)) { String artifactsHost = VersionProperties.elasticsearch.endsWith("-SNAPSHOT") ? "https://snapshots.elastic.co" : "https://artifacts.elastic.co" Closure sortClosure = { a, b -> b.group <=> a.group } Closure depJavadocClosure = { shadowed, dep -> if ((dep instanceof ProjectDependency) == false) { return } Project upstreamProject = dep.dependencyProject 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 /* * 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 externalLinkName = upstreamProject.archivesBaseName String artifactPath = dep.group.replaceAll('\\.', '/') + '/' + externalLinkName.replaceAll('\\.', '/') + '/' + dep.version project.javadoc.options.linksOffline artifactsHost + "/javadoc/" + artifactPath, "${upstreamProject.buildDir}/docs/javadoc/" } } boolean hasShadow = project.plugins.hasPlugin(ShadowPlugin) project.configurations.compileClasspath.dependencies .findAll() .toSorted(sortClosure) .each({ c -> depJavadocClosure(hasShadow, c) }) project.configurations.compileOnly.dependencies .findAll() .toSorted(sortClosure) .each({ c -> depJavadocClosure(false, c) }) if (hasShadow) { // include any dependencies for shadow JAR projects that are *not* bundled in the shadow JAR project.configurations.shadow.dependencies .findAll() .toSorted(sortClosure) .each({ c -> depJavadocClosure(false, c) }) } } } apply plugin: 'elasticsearch.formatting' } tasks.register("verifyBwcTestsEnabled") { doLast { if (bwc_tests_enabled == false) { throw new GradleException('Bwc tests are disabled. They must be re-enabled after completing backcompat behavior backporting.') } } } tasks.register("branchConsistency") { description 'Ensures this branch is internally consistent. For example, that versions constants match released versions.' group 'Verification' dependsOn ":verifyVersions", ":verifyBwcTestsEnabled" } tasks.named("wrapper").configure { distributionType = 'ALL' doLast { final DistributionLocator locator = new DistributionLocator() final GradleVersion version = GradleVersion.version(wrapper.gradleVersion) final URI distributionUri = locator.getDistributionFor(version, wrapper.distributionType.name().toLowerCase(Locale.ENGLISH)) final URI sha256Uri = new URI(distributionUri.toString() + ".sha256") final String sha256Sum = new String(sha256Uri.toURL().bytes) wrapper.getPropertiesFile() << "distributionSha256Sum=${sha256Sum}\n" println "Added checksum to wrapper properties" // copy wrapper properties file to build-tools-internal to allow seamless idea integration def file = new File("build-tools-internal/gradle/wrapper/gradle-wrapper.properties") Files.copy(wrapper.getPropertiesFile().toPath(), file.toPath(), REPLACE_EXISTING) // Update build-tools to reflect the Gradle upgrade // TODO: we can remove this once we have tests to make sure older versions work. project.file('build-tools-internal/src/main/resources/minimumGradleVersion').text = gradleVersion println "Updated minimum Gradle Version" } } gradle.projectsEvaluated { // Having the same group and name for distinct projects causes Gradle to consider them equal when resolving // dependencies leading to hard to debug failures. Run a check across all project to prevent this from happening. // see: https://github.com/gradle/gradle/issues/847 Map coordsToProject = [:] project.allprojects.forEach { p -> String coords = "${p.group}:${p.name}" if (false == coordsToProject.putIfAbsent(coords, p)) { throw new GradleException( "Detected that two projects: ${p.path} and ${coordsToProject[coords].path} " + "have the same name and group: ${coords}. " + "This doesn't currently work correctly in Gradle, see: " + "https://github.com/gradle/gradle/issues/847" ) } } } tasks.named("precommit") { dependsOn gradle.includedBuild('build-tools').task(':precommit') dependsOn gradle.includedBuild('build-tools-internal').task(':precommit') } tasks.named("checkPart1").configure { dependsOn gradle.includedBuild('build-tools').task(':check') dependsOn gradle.includedBuild('build-tools-internal').task(':check') } tasks.named("assemble").configure { dependsOn gradle.includedBuild('build-tools').task(':assemble') } tasks.named("cleanEclipse").configure { dependsOn gradle.includedBuild('build-conventions').task(':cleanEclipse') dependsOn gradle.includedBuild('build-tools').task(':cleanEclipse') dependsOn gradle.includedBuild('build-tools-internal').task(':cleanEclipse') } tasks.named("eclipse").configure { dependsOn gradle.includedBuild('build-conventions').task(':eclipse') dependsOn gradle.includedBuild('build-tools').task(':eclipse') dependsOn gradle.includedBuild('build-tools-internal').task(':eclipse') } tasks.register("buildReleaseArtifacts").configure { group = 'build' description = 'Builds all artifacts required for release manager' dependsOn allprojects.findAll { it.path.startsWith(':distribution:docker') == false && it.path.startsWith(':ml-cpp') == false } .collect { GradleUtils.findByName(it.tasks, 'assemble') } .findAll { it != null } } tasks.named("spotlessApply").configure { dependsOn gradle.includedBuild('build-tools').task(':spotlessApply') dependsOn gradle.includedBuild('build-tools').task(':reaper:spotlessApply') dependsOn gradle.includedBuild('build-tools-internal').task(':spotlessApply') }