diff --git a/build-conventions/build.gradle b/build-conventions/build.gradle index ab406056876cd..f703ef70febe7 100644 --- a/build-conventions/build.gradle +++ b/build-conventions/build.gradle @@ -6,9 +6,12 @@ * Side Public License, v 1. */ +import org.gradle.plugins.ide.eclipse.model.SourceFolder; + plugins { id 'java-gradle-plugin' id 'java-test-fixtures' + id 'eclipse' } group = "org.elasticsearch" @@ -25,6 +28,10 @@ gradlePlugin { id = 'elasticsearch.internal-licenseheaders' implementationClass = 'org.elasticsearch.gradle.internal.conventions.precommit.LicenseHeadersPrecommitPlugin' } + eclipse { + id = 'elasticsearch.eclipse' + implementationClass = 'org.elasticsearch.gradle.internal.conventions.EclipseConventionPlugin' + } publish { id = 'elasticsearch.publish' implementationClass = 'org.elasticsearch.gradle.internal.conventions.PublishPlugin' @@ -50,3 +57,19 @@ dependencies { api 'gradle.plugin.com.github.jengelman.gradle.plugins:shadow:7.0.0' api 'org.apache.rat:apache-rat:0.11' } + +project.getPlugins().withType(JavaBasePlugin.class, javaBasePlugin -> { + java.getModularity().getInferModulePath().set(false); + eclipse.getClasspath().getFile().whenMerged { classpath -> + /* + * give each source folder a unique corresponding output folder + * outside of the usual `build` folder. We can't put the build + * in the usual build folder because eclipse becomes *very* sad + * if we delete it. Which `gradlew clean` does all the time. + */ + int i = 0; + classpath.getEntries().stream().filter(e -> e instanceof SourceFolder).forEachOrdered(s -> + s.setOutput("out/eclipse"+i++) + ) + } +}) diff --git a/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/EclipseConventionPlugin.java b/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/EclipseConventionPlugin.java new file mode 100644 index 0000000000000..f1b81b3915606 --- /dev/null +++ b/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/EclipseConventionPlugin.java @@ -0,0 +1,138 @@ +/* + * 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. + */ + +package org.elasticsearch.gradle.internal.conventions; + +import org.apache.tools.ant.taskdefs.condition.Os; +import org.gradle.api.Action; +import org.gradle.api.GradleException; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.Transformer; +import org.gradle.api.plugins.JavaBasePlugin; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.tasks.Copy; +import org.gradle.api.tasks.Delete; +import org.gradle.plugins.ide.eclipse.EclipsePlugin; +import org.gradle.plugins.ide.eclipse.model.Classpath; +import org.gradle.plugins.ide.eclipse.model.EclipseModel; +import org.gradle.plugins.ide.eclipse.model.EclipseProject; +import org.gradle.plugins.ide.eclipse.model.ProjectDependency; +import org.gradle.plugins.ide.eclipse.model.SourceFolder; +import org.gradle.plugins.ide.eclipse.model.ClasspathEntry; + +import java.io.File; +import java.util.List; +import java.io.IOException; +import static java.util.stream.Collectors.toList; + +import static org.apache.commons.io.FileUtils.readFileToString; + +public class EclipseConventionPlugin implements Plugin { + @Override + public void apply(Project project) { + project.getPlugins().apply(EclipsePlugin.class); + EclipseModel eclipseModel = project.getExtensions().getByType(EclipseModel.class); + EclipseProject eclipseProject = eclipseModel.getProject(); + + // Name all the non-root projects after their path so that paths get grouped together when imported into eclipse. + if (project.getPath().equals(":") == false) { + eclipseProject.setName(project.getPath()); + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + eclipseProject.setName(eclipseProject.getName().replace(':', '_')); + } + } + + + File licenseHeaderFile; + String prefix = ":x-pack"; + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + prefix = prefix.replace(':', '_'); + } + File root = root(project); + if (eclipseProject.getName().startsWith(prefix)) { + licenseHeaderFile = new File(root, "build-tools-internal/src/main/resources/license-headers/elastic-license-2.0-header.txt"); + } else { + licenseHeaderFile = new File(root, "build-tools-internal/src/main/resources/license-headers/sspl+elastic-license-header.txt"); + } + + String lineSeparator = Os.isFamily(Os.FAMILY_WINDOWS) ? "\\\\r\\\\n" : "\\\\n"; + String licenseHeader = null; + try { + licenseHeader = readFileToString(licenseHeaderFile, "UTF-8").replace(System.lineSeparator(), lineSeparator); + } catch (IOException e) { + throw new GradleException("Cannot configure eclipse", e); + } + + String finalLicenseHeader = licenseHeader; + project.getTasks().register("copyEclipseSettings", Copy.class, copy -> { + copy.mustRunAfter("wipeEclipseSettings"); + // TODO: "package this up" for external builds + copy.from(new File(root, "build-tools-internal/src/main/resources/eclipse.settings")); + copy.into(".settings"); + copy.filter(new Transformer() { + @Override + public String transform(String s) { + return s.replaceAll("@@LICENSE_HEADER_TEXT@@", finalLicenseHeader); + } + }); + }); + // otherwise .settings is not nuked entirely + project.getTasks().register("wipeEclipseSettings", Delete.class, new Action() { + @Override + public void execute(Delete delete) { + delete.delete(".settings"); + } + }); + + project.getTasks().named("cleanEclipse").configure(t -> t.dependsOn("wipeEclipseSettings")); + + // otherwise the eclipse merging is *super confusing* + project.getTasks().named("eclipse").configure(t -> t.dependsOn("cleanEclipse", "copyEclipseSettings")); + + project.getPlugins().withType(JavaBasePlugin.class, javaBasePlugin -> { + JavaPluginExtension java = project.getExtensions().getByType(JavaPluginExtension.class); + java.getModularity().getInferModulePath().set(false); + + eclipseModel.getClasspath().getFile().whenMerged(c -> { + Classpath classpath = (Classpath) c; + + /* + * give each source folder a unique corresponding output folder + * outside of the usual `build` folder. We can't put the build + * in the usual build folder because eclipse becomes *very* sad + * if we delete it. Which `gradlew clean` does all the time. + */ + int i = 0; + List sourceFolderList = classpath.getEntries().stream().filter(e -> e instanceof SourceFolder).collect(toList()); + for (ClasspathEntry sourceFolder : sourceFolderList) { + ((SourceFolder)sourceFolder).setOutput("out/eclipse/" + i++); + } + + // Starting with Gradle 6.7 test dependencies are not exposed by eclipse + // projects by default. This breaks project dependencies using the `java-test-fixtures` plugin + // or dependencies on other projects manually declared testArtifacts configurations + // This issue is tracked in gradle issue tracker. + // See https://github.com/gradle/gradle/issues/14932 for further details + + classpath.getEntries().stream().filter(e -> e instanceof ProjectDependency).forEach(it -> + ((ProjectDependency) it).getEntryAttributes().remove("without_test_code") + ); + + }); + + project.getTasks().named("eclipseJdt").configure(t -> t.dependsOn("copyEclipseSettings")); + }); + } + + private File root(Project project) { + return project.getGradle().getParent() == null ? + project.getRootDir() : + root(project.getGradle().getParent().getRootProject()); + } +} diff --git a/build-tools-internal/build.gradle b/build-tools-internal/build.gradle index ae716a38e7891..6b1d2566749c8 100644 --- a/build-tools-internal/build.gradle +++ b/build-tools-internal/build.gradle @@ -9,6 +9,10 @@ import org.gradle.internal.jvm.Jvm import org.gradle.util.GradleVersion import org.elasticsearch.gradle.internal.conventions.VersionPropertiesLoader +import org.apache.tools.ant.taskdefs.condition.Os +import org.gradle.plugins.ide.eclipse.model.AccessRule +import org.gradle.plugins.ide.eclipse.model.SourceFolder +import org.gradle.plugins.ide.eclipse.model.ProjectDependency plugins { id 'java-gradle-plugin' @@ -16,6 +20,7 @@ plugins { id 'groovy' id 'elasticsearch.internal-licenseheaders' id 'elasticsearch.basic-build-tool-conventions' + id 'elasticsearch.eclipse' } group = 'org.elasticsearch.gradle' diff --git a/build-tools/build.gradle b/build-tools/build.gradle index a0525a773e23c..3da1d82cf3b78 100644 --- a/build-tools/build.gradle +++ b/build-tools/build.gradle @@ -22,11 +22,13 @@ description = "The elasticsearch build tools" // we write this back out below to load it in the Build.java which will be shown in rest main action // to indicate this being a snapshot build or a release build. Properties props = VersionPropertiesLoader.loadBuildSrcVersion(project.file('../build-tools-internal/version.properties')) + allprojects { group = "org.elasticsearch" version = props.getProperty("elasticsearch") apply plugin: 'java' + apply plugin: 'elasticsearch.eclipse' targetCompatibility = 11 sourceCompatibility = 11 } @@ -217,4 +219,4 @@ tasks.named("check").configure { dependsOn("integTest") } // baseClass 'spock.lang.Specification' // } // } -// } \ No newline at end of file +// } diff --git a/build.gradle b/build.gradle index 59c4209c18b0b..935fe57240409 100644 --- a/build.gradle +++ b/build.gradle @@ -314,42 +314,8 @@ gradle.projectsEvaluated { // eclipse configuration allprojects { - apply plugin: 'eclipse' - // Name all the non-root projects after their path so that paths get grouped together when imported into eclipse. - if (path != ':') { - eclipse.project.name = path - if (Os.isFamily(Os.FAMILY_WINDOWS)) { - eclipse.project.name = eclipse.project.name.replace(':', '_') - } - } - - plugins.withType(JavaBasePlugin) { - java.modularity.inferModulePath.set(false) - eclipse.classpath.file.whenMerged { classpath -> - /* - * give each source folder a unique corresponding output folder - * outside of the usual `build` folder. We can't put the build - * in the usual build folder because eclipse becomes *very* sad - * if we delete it. Which `gradlew clean` does all the time. - */ - int i = 0; - classpath.entries.findAll { it instanceof SourceFolder }.each { folder -> - i++; - folder.output = 'out/eclipse/' + i - } + apply plugin: 'elasticsearch.eclipse' - // Starting with Gradle 6.7 test dependencies are not exposed by eclipse - // projects by default. This breaks project dependencies using the `java-test-fixtures` plugin - // or dependencies on other projects manually declared testArtifacts configurations - // This issue is tracked in gradle issue tracker. - // See https://github.com/gradle/gradle/issues/14932 for further details - classpath.entries.findAll { - it instanceof ProjectDependency - }.each { - it.entryAttributes.remove('without_test_code') - } - } - } /* * Allow accessing com/sun/net/httpserver in projects that have * configured forbidden apis to allow it. @@ -365,34 +331,6 @@ allprojects { } } } - - File licenseHeaderFile - String prefix = ':x-pack' - if (Os.isFamily(Os.FAMILY_WINDOWS)) { - prefix = prefix.replace(':', '_') - } - if (eclipse.project.name.startsWith(prefix)) { - licenseHeaderFile = new File(project.rootDir, 'build-tools-internal/src/main/resources/license-headers/elastic-license-2.0-header.txt') - } else { - licenseHeaderFile = new File(project.rootDir, 'build-tools-internal/src/main/resources/license-headers/sspl+elastic-license-header.txt') - } - - String lineSeparator = Os.isFamily(Os.FAMILY_WINDOWS) ? '\\\\r\\\\n' : '\\\\n' - String licenseHeader = licenseHeaderFile.getText('UTF-8').replace(System.lineSeparator(), lineSeparator) - tasks.register('copyEclipseSettings', Copy) { - mustRunAfter 'wipeEclipseSettings' - // TODO: "package this up" for external builds - from new File(project.rootDir, 'build-tools-internal/src/main/resources/eclipse.settings') - into '.settings' - filter { it.replaceAll('@@LICENSE_HEADER_TEXT@@', licenseHeader) } - } - // otherwise .settings is not nuked entirely - tasks.register('wipeEclipseSettings', Delete) { - delete '.settings' - } - tasks.named('cleanEclipse') { dependsOn 'wipeEclipseSettings' } - // otherwise the eclipse merging is *super confusing* - tasks.named('eclipse') { dependsOn 'cleanEclipse', 'copyEclipseSettings' } } tasks.named("wrapper").configure { @@ -501,6 +439,18 @@ 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') +} + subprojects { project.ext.disableTasks = { String... tasknames -> for (String taskname : tasknames) {