From 6e4c9e83441263bfd1a44253fc7c194e9787af38 Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Wed, 2 Jun 2021 16:19:35 +0200 Subject: [PATCH 1/5] Fix eclipse configuration for build tools projects --- build-conventions/build.gradle | 25 ++++ .../conventions/EclipseConventionPlugin.java | 135 ++++++++++++++++++ build-tools-internal/build.gradle | 5 + build-tools/build.gradle | 1 + build.gradle | 76 ++-------- 5 files changed, 179 insertions(+), 63 deletions(-) create mode 100644 build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/EclipseConventionPlugin.java diff --git a/build-conventions/build.gradle b/build-conventions/build.gradle index 3e1bf5a641a34..6983d709a4551 100644 --- a/build-conventions/build.gradle +++ b/build-conventions/build.gradle @@ -6,9 +6,14 @@ * Side Public License, v 1. */ +import org.gradle.plugins.ide.eclipse.model.Classpath; +import org.gradle.plugins.ide.eclipse.model.ProjectDependency; +import org.gradle.plugins.ide.eclipse.model.SourceFolder; + plugins { id 'java-gradle-plugin' id 'java-test-fixtures' + id 'eclipse' } group = "org.elasticsearch" @@ -26,6 +31,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' @@ -51,3 +60,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..10c35436a8cb6 --- /dev/null +++ b/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/EclipseConventionPlugin.java @@ -0,0 +1,135 @@ +/* + * 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 java.io.File; +import java.io.IOException; + +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; + classpath.getEntries().stream().filter(e -> e instanceof SourceFolder).forEachOrdered(it -> + ((SourceFolder) it).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 9f7fc0c3ff837..03c01a347e452 100644 --- a/build-tools-internal/build.gradle +++ b/build-tools-internal/build.gradle @@ -8,6 +8,10 @@ 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' @@ -15,6 +19,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 233685d038673..80e5cc938eb82 100644 --- a/build-tools/build.gradle +++ b/build-tools/build.gradle @@ -29,6 +29,7 @@ allprojects { version = props.getProperty("elasticsearch") apply plugin: 'java' + apply plugin: 'elasticsearch.eclipse' targetCompatibility = minRuntimeJava sourceCompatibility = minRuntimeJava } diff --git a/build.gradle b/build.gradle index 540ba8c7022f1..feaf6ca0450c9 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 { @@ -504,6 +442,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) { From 07488607e561ef69d388dc03c3a93c4231accc63 Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Wed, 2 Jun 2021 22:23:35 +0200 Subject: [PATCH 2/5] Fix eclipse convention plugin --- .../conventions/EclipseConventionPlugin.java | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) 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 index 10c35436a8cb6..e9784a4b735a2 100644 --- 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 @@ -68,31 +68,31 @@ public void apply(Project project) { 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 -> { + 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); @@ -107,7 +107,7 @@ public void execute(Delete delete) { */ int i = 0; classpath.getEntries().stream().filter(e -> e instanceof SourceFolder).forEachOrdered(it -> - ((SourceFolder) it).setOutput("out/eclipse"+i) + ((SourceFolder) it).setOutput("out/eclipse/"+i) ); // Starting with Gradle 6.7 test dependencies are not exposed by eclipse @@ -123,7 +123,6 @@ public void execute(Delete delete) { }); project.getTasks().named("eclipseJdt").configure(t -> t.dependsOn("copyEclipseSettings")); - }); }); } From 8dc6f1d951841785bd5282114c554f18339d7729 Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Fri, 4 Jun 2021 10:38:56 +0200 Subject: [PATCH 3/5] keep distinct output folders per sourceSet --- .../internal/conventions/EclipseConventionPlugin.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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 index e9784a4b735a2..f1b81b3915606 100644 --- 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 @@ -24,9 +24,12 @@ 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; @@ -106,9 +109,10 @@ public void execute(Delete delete) { * if we delete it. Which `gradlew clean` does all the time. */ int i = 0; - classpath.getEntries().stream().filter(e -> e instanceof SourceFolder).forEachOrdered(it -> - ((SourceFolder) it).setOutput("out/eclipse/"+i) - ); + 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 From ee0b72c7f2bde2291a9754be341a2670127b6e89 Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Tue, 8 Jun 2021 11:54:33 +0200 Subject: [PATCH 4/5] Fix eclipse sourcefolder outputs for build convention --- build-conventions/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-conventions/build.gradle b/build-conventions/build.gradle index 6983d709a4551..94dae08aebe12 100644 --- a/build-conventions/build.gradle +++ b/build-conventions/build.gradle @@ -72,7 +72,7 @@ project.getPlugins().withType(JavaBasePlugin.class, javaBasePlugin -> { */ int i = 0; classpath.getEntries().stream().filter(e -> e instanceof SourceFolder).forEachOrdered(s -> - s.setOutput("out/eclipse"+i) + s.setOutput("out/eclipse"+i++) ) } }) From 9f7d20e6b349b76216a4015c267a92bfc514cad9 Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Tue, 8 Jun 2021 11:57:08 +0200 Subject: [PATCH 5/5] Fix imports in build conventions --- build-conventions/build.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/build-conventions/build.gradle b/build-conventions/build.gradle index 94dae08aebe12..d5b1a71fa673c 100644 --- a/build-conventions/build.gradle +++ b/build-conventions/build.gradle @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -import org.gradle.plugins.ide.eclipse.model.Classpath; -import org.gradle.plugins.ide.eclipse.model.ProjectDependency; import org.gradle.plugins.ide.eclipse.model.SourceFolder; plugins {