From 1136217596d87373593d339b91d739b9b690b25c Mon Sep 17 00:00:00 2001 From: sheche Date: Thu, 11 Aug 2022 14:10:07 +0800 Subject: [PATCH 1/8] Add the generated source folders to Eclipse classpath entries - add the generated source folders to Eclipse classpath entries. Due to a limition of Buildship, we have to create those folders beforehand, see: https://github.com/eclipse/buildship/issues/1196 Signed-off-by: sheche --- .../protobuf/gradle/ProtobufPlugin.groovy | 2 + .../com/google/protobuf/gradle/Utils.groovy | 72 +++++++++++++++++++ .../gradle/ProtobufJavaPluginTest.groovy | 40 +++++++++++ testProject/build.gradle | 1 + 4 files changed, 115 insertions(+) diff --git a/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy index b04d12e6..7713bdea 100644 --- a/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy @@ -482,6 +482,8 @@ class ProtobufPlugin implements Plugin { project.tasks.withType(GenerateProtoTask).each { GenerateProtoTask generateProtoTask -> generateProtoTask.getOutputSourceDirectories().each { File outputDir -> Utils.addToIdeSources(project, generateProtoTask.isTest, outputDir, true) + Utils.addToEclipseSources(project, generateProtoTask.isTest, + generateProtoTask.sourceSet.name, outputDir) } } } diff --git a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy index 89f9950f..86a53c94 100644 --- a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy @@ -33,6 +33,10 @@ import groovy.transform.CompileStatic import org.apache.commons.lang.StringUtils import org.gradle.api.Project import org.gradle.api.tasks.SourceSet +import org.gradle.plugins.ide.eclipse.model.Classpath +import org.gradle.plugins.ide.eclipse.model.ClasspathEntry +import org.gradle.plugins.ide.eclipse.model.EclipseModel +import org.gradle.plugins.ide.eclipse.model.SourceFolder import org.gradle.plugins.ide.idea.GenerateIdeaModule import org.gradle.plugins.ide.idea.model.IdeaModel import org.gradle.util.GUtil @@ -129,4 +133,72 @@ class Utils { } } } + + /** + * Add the the folder of generated source to Eclipse .classpath file. + */ + static void addToEclipseSources(Project project, boolean isTest, String sourceSetName, File f) { + project.plugins.withId("eclipse") { + File projectDir = project.getProjectDir() + + EclipseModel model = project.getExtensions().findByType(EclipseModel) + model.classpath.file.whenMerged { Classpath cp -> + // buildship requires the folder exists on disk, otherwise + // it will be ignored when updating classpath file, see: + // https://github.com/eclipse/buildship/issues/1196 + f.mkdirs() + + String relativePath = projectDir.toURI().relativize(f.toURI()).getPath() + // remove trailing slash + if (relativePath.endsWith("/")) { + relativePath = relativePath.substring(0, relativePath.length() - 1); + } + SourceFolder entry = new SourceFolder(relativePath, getOutputPath(cp, sourceSetName)) + entry.entryAttributes.put("optional", "true") + entry.entryAttributes.put("ignore_optional_problems", "true") + // check if output is not null here because test source folder + // must have a separate output folder in Eclipse + if (entry.output != null && isTest) { + entry.entryAttributes.put("test", "true") + } + cp.entries.add(entry) + } + } + } + + /** + * Get the output path according to the source set name, if no valid path can be + * found, null will return. + */ + private static String getOutputPath(Classpath classpath, String sourceSetName) { + String path = "bin/" + sourceSetName + if (isValidOutput(classpath, path)) { + return path + } + // fallback to default output + return null + } + + /** + * Check if the output path is valid or not. + * See: org.eclipse.jdt.internal.core.ClasspathEntry#validateClasspath() + */ + private static boolean isValidOutput(Classpath classpath, String path) { + Set outputs = new HashSet<>() + for (ClasspathEntry cpe : classpath.getEntries()) { + if (cpe instanceof SourceFolder) { + outputs.add(((SourceFolder) cpe).getOutput()) + } + } + for (String output : outputs) { + if (Objects.equals(output, path)) { + continue + } + // Eclipse does not allow nested output path + if (output.startsWith(path) || path.startsWith(output)) { + return false + } + } + return true + } } diff --git a/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy b/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy index 7f6e60d4..6075ac18 100644 --- a/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy +++ b/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy @@ -414,6 +414,46 @@ class ProtobufJavaPluginTest extends Specification { gradleVersion << GRADLE_VERSIONS } + @Unroll + void "testProject proto and generated output directories should be added to Eclipse [gradle #gradleVersion]"() { + given: "project from testProject" + File projectDir = ProtobufPluginTestHelper.projectBuilder('testEclipse') + .copyDirs('testProjectBase', 'testProject') + .build() + + when: "eclipse is invoked" + BuildResult result = GradleRunner.create() + .withProjectDir(projectDir) + .withArguments('eclipse') + .withPluginClasspath() + .withGradleVersion(gradleVersion) + .build() + + then: "it succeed" + result.task(":eclipse").outcome == TaskOutcome.SUCCESS + Node classpathFile = new XmlParser().parse(projectDir.toPath().resolve(".classpath").toFile()) + Collection srcEntries = classpathFile.classpathentry.findAll {it.'@kind' == 'src'} + assert srcEntries.size() == 6 + + Set sourceDir = [] as Set + srcEntries.each { + sourceDir.add(it.@path) + } + + Set expectedSourceDir = ImmutableSet.builder() + .add('src/main/java') + .add('src/test/java') + .add('build/generated/source/proto/grpc/java') + .add('build/generated/source/proto/grpc/grpc_output') + .add('build/generated/source/proto/main/java') + .add('build/generated/source/proto/test/java') + .build() + assert Objects.equals(expectedSourceDir, sourceDir) + + where: + gradleVersion << GRADLE_VERSIONS + } + @Unroll void "test proto generation is not up-to-date on dependency changes [gradle #gradleVersion]"() { given: "project from testProject" diff --git a/testProject/build.gradle b/testProject/build.gradle index 62ec63be..a44e7fee 100644 --- a/testProject/build.gradle +++ b/testProject/build.gradle @@ -3,6 +3,7 @@ plugins { id 'java' id 'idea' + id 'eclipse' id 'com.google.protobuf' } apply from: 'build_base.gradle' From 44cfe40f9dbd1c1b68d75708ab4692dcb60a1f9a Mon Sep 17 00:00:00 2001 From: sheche Date: Thu, 18 Aug 2022 10:27:40 +0800 Subject: [PATCH 2/8] Add more attributes to the classpath entries Signed-off-by: sheche --- .../groovy/com/google/protobuf/gradle/Utils.groovy | 9 +++++++++ .../protobuf/gradle/ProtobufJavaPluginTest.groovy | 11 ++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy index 86a53c94..05e7ea9b 100644 --- a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy @@ -156,6 +156,15 @@ class Utils { SourceFolder entry = new SourceFolder(relativePath, getOutputPath(cp, sourceSetName)) entry.entryAttributes.put("optional", "true") entry.entryAttributes.put("ignore_optional_problems", "true") + + entry.entryAttributes.put("gradle_scope", isTest ? "test" : "main") + entry.entryAttributes.put("gradle_used_by_scope", isTest ? "test" : "main,test") + + // this attribute is optional, but it could be useful for IDEs to recognize that it is + // generated source folder from protobuf, thus providing some support for that. + // e.g. Hint user to run generate tasks if the folder is empty or does not exist. + entry.entryAttributes.put("protobuf_generated_source", "true") + // check if output is not null here because test source folder // must have a separate output folder in Eclipse if (entry.output != null && isTest) { diff --git a/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy b/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy index 6075ac18..1acc1f13 100644 --- a/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy +++ b/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy @@ -437,7 +437,16 @@ class ProtobufJavaPluginTest extends Specification { Set sourceDir = [] as Set srcEntries.each { - sourceDir.add(it.@path) + String path = it.@path + sourceDir.add(path) + if (path.startsWith("build/generated/source/proto")) { + if (path.contains("test")) { + // test source path has one more attribute: ["test"="true"] + assert it.attributes.attribute.size() == 6 + } else { + assert it.attributes.attribute.size() == 5 + } + } } Set expectedSourceDir = ImmutableSet.builder() From 13b8c1bc724cbdbd529135289176f19eee06d9f7 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Mon, 29 Aug 2022 13:32:31 +0800 Subject: [PATCH 3/8] Remove useless classpath attributes Signed-off-by: Sheng Chen --- src/main/groovy/com/google/protobuf/gradle/Utils.groovy | 3 --- .../com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy index 05e7ea9b..ac440d22 100644 --- a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy @@ -157,9 +157,6 @@ class Utils { entry.entryAttributes.put("optional", "true") entry.entryAttributes.put("ignore_optional_problems", "true") - entry.entryAttributes.put("gradle_scope", isTest ? "test" : "main") - entry.entryAttributes.put("gradle_used_by_scope", isTest ? "test" : "main,test") - // this attribute is optional, but it could be useful for IDEs to recognize that it is // generated source folder from protobuf, thus providing some support for that. // e.g. Hint user to run generate tasks if the folder is empty or does not exist. diff --git a/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy b/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy index 1acc1f13..6f23f18b 100644 --- a/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy +++ b/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy @@ -442,9 +442,9 @@ class ProtobufJavaPluginTest extends Specification { if (path.startsWith("build/generated/source/proto")) { if (path.contains("test")) { // test source path has one more attribute: ["test"="true"] - assert it.attributes.attribute.size() == 6 + assert it.attributes.attribute.size() == 4 } else { - assert it.attributes.attribute.size() == 5 + assert it.attributes.attribute.size() == 3 } } } From d5ecc9412386bcc13ee052dab83c98882f6c4933 Mon Sep 17 00:00:00 2001 From: rougsig Date: Mon, 29 Aug 2022 13:33:38 +0300 Subject: [PATCH 4/8] Move ide test to IDESupportTest --- .github/workflows/testing.yml | 2 +- .../com/google/protobuf/gradle/Utils.groovy | 4 +- .../protobuf/gradle/IDESupportTest.groovy | 142 ++++++++++++++++++ .../gradle/ProtobufJavaPluginTest.groovy | 125 --------------- 4 files changed, 145 insertions(+), 128 deletions(-) create mode 100644 src/test/groovy/com/google/protobuf/gradle/IDESupportTest.groovy diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index a0852a8f..c5044786 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -27,7 +27,7 @@ jobs: needs: build strategy: matrix: - tests: [ProtobufJavaPluginTest, ProtobufKotlinDslCopySpecTest, ProtobufKotlinDslPluginTest, ProtobufAndroidPluginTest, ProtobufAndroidPluginKotlinTest, AndroidProjectDetectionTest] + tests: [ProtobufJavaPluginTest, ProtobufKotlinDslCopySpecTest, ProtobufKotlinDslPluginTest, ProtobufAndroidPluginTest, ProtobufAndroidPluginKotlinTest, AndroidProjectDetectionTest, IDESupportTest] runs-on: ubuntu-latest timeout-minutes: 30 steps: diff --git a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy index ac440d22..d7a2d6d2 100644 --- a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy @@ -151,7 +151,7 @@ class Utils { String relativePath = projectDir.toURI().relativize(f.toURI()).getPath() // remove trailing slash if (relativePath.endsWith("/")) { - relativePath = relativePath.substring(0, relativePath.length() - 1); + relativePath = relativePath[0..(relativePath.length() - 1)] } SourceFolder entry = new SourceFolder(relativePath, getOutputPath(cp, sourceSetName)) entry.entryAttributes.put("optional", "true") @@ -190,7 +190,7 @@ class Utils { * See: org.eclipse.jdt.internal.core.ClasspathEntry#validateClasspath() */ private static boolean isValidOutput(Classpath classpath, String path) { - Set outputs = new HashSet<>() + Set outputs = [] for (ClasspathEntry cpe : classpath.getEntries()) { if (cpe instanceof SourceFolder) { outputs.add(((SourceFolder) cpe).getOutput()) diff --git a/src/test/groovy/com/google/protobuf/gradle/IDESupportTest.groovy b/src/test/groovy/com/google/protobuf/gradle/IDESupportTest.groovy new file mode 100644 index 00000000..4c36229e --- /dev/null +++ b/src/test/groovy/com/google/protobuf/gradle/IDESupportTest.groovy @@ -0,0 +1,142 @@ +package com.google.protobuf.gradle + +import com.google.common.collect.ImmutableSet +import groovy.transform.CompileDynamic +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import spock.lang.Specification +import spock.lang.Unroll + +/** + * Unit tests to check ide metadata generation + */ +@CompileDynamic +class IDESupportTest extends Specification { + // Current supported version is Gradle 5+. + private static final List GRADLE_VERSIONS = ["5.6", "6.0", "6.7.1", "7.4.2"] + + @Unroll + void "testProject proto and generated output directories should be added to intellij [gradle #gradleVersion]"() { + given: "project from testProject" + File projectDir = ProtobufPluginTestHelper.projectBuilder('testIdea') + .copyDirs('testProjectBase', 'testProject') + .build() + + when: "idea is invoked" + BuildResult result = GradleRunner.create() + .withProjectDir(projectDir) + .withArguments('idea') + .withPluginClasspath() + .withGradleVersion(gradleVersion) + .build() + + then: "it succeed" + result.task(":idea").outcome == TaskOutcome.SUCCESS + Node imlRoot = new XmlParser().parse(projectDir.toPath().resolve("testProject.iml").toFile()) + Collection rootMgr = imlRoot.component.findAll { it.'@name' == 'NewModuleRootManager' } + assert rootMgr.size() == 1 + assert rootMgr.content.sourceFolder.size() == 1 + + Set sourceDir = [] as Set + Set testSourceDir = [] as Set + Set generatedDirs = [] as Set + rootMgr.content.sourceFolder[0].each { + if (Boolean.parseBoolean(it.@isTestSource)) { + testSourceDir.add(it.@url) + } else { + sourceDir.add(it.@url) + } + if (Boolean.parseBoolean(it.@generated)) { + generatedDirs.add(it.@url) + } + } + + Set expectedSourceDir = ImmutableSet.builder() + .add('file://$MODULE_DIR$/src/main/java') + .add('file://$MODULE_DIR$/src/grpc/proto') + .add('file://$MODULE_DIR$/src/main/proto') + .add('file://$MODULE_DIR$/build/extracted-include-protos/grpc') + .add('file://$MODULE_DIR$/build/extracted-protos/main') + .add('file://$MODULE_DIR$/build/extracted-include-protos/main') + .add('file://$MODULE_DIR$/build/extracted-protos/grpc') + .add('file://$MODULE_DIR$/build/generated/source/proto/grpc/java') + .add('file://$MODULE_DIR$/build/generated/source/proto/grpc/grpc_output') + .add('file://$MODULE_DIR$/build/generated/source/proto/main/java') + .build() + Set expectedTestSourceDir = ImmutableSet.builder() + .add('file://$MODULE_DIR$/src/test/java') + .add('file://$MODULE_DIR$/src/test/proto') + .add('file://$MODULE_DIR$/build/extracted-protos/test') + .add('file://$MODULE_DIR$/build/extracted-include-protos/test') + .add('file://$MODULE_DIR$/build/generated/source/proto/test/java') + .build() + Set expectedGeneratedDirs = [ + 'file://$MODULE_DIR$/build/extracted-include-protos/grpc', + 'file://$MODULE_DIR$/build/extracted-protos/main', + 'file://$MODULE_DIR$/build/extracted-include-protos/main', + 'file://$MODULE_DIR$/build/extracted-protos/grpc', + 'file://$MODULE_DIR$/build/generated/source/proto/grpc/java', + 'file://$MODULE_DIR$/build/generated/source/proto/grpc/grpc_output', + 'file://$MODULE_DIR$/build/generated/source/proto/main/java', + 'file://$MODULE_DIR$/build/extracted-protos/test', + 'file://$MODULE_DIR$/build/extracted-include-protos/test', + 'file://$MODULE_DIR$/build/generated/source/proto/test/java', + ] + assert Objects.equals(expectedSourceDir, sourceDir) + assert Objects.equals(expectedTestSourceDir, testSourceDir) + Objects.equals(expectedGeneratedDirs, generatedDirs) + + where: + gradleVersion << GRADLE_VERSIONS + } + + @Unroll + void "testProject proto and generated output directories should be added to Eclipse [gradle #gradleVersion]"() { + given: "project from testProject" + File projectDir = ProtobufPluginTestHelper.projectBuilder('testEclipse') + .copyDirs('testProjectBase', 'testProject') + .build() + + when: "eclipse is invoked" + BuildResult result = GradleRunner.create() + .withProjectDir(projectDir) + .withArguments('eclipse') + .withPluginClasspath() + .withGradleVersion(gradleVersion) + .build() + + then: "it succeed" + result.task(":eclipse").outcome == TaskOutcome.SUCCESS + Node classpathFile = new XmlParser().parse(projectDir.toPath().resolve(".classpath").toFile()) + Collection srcEntries = classpathFile.classpathentry.findAll { it.'@kind' == 'src' } + assert srcEntries.size() == 6 + + Set sourceDir = [] as Set + srcEntries.each { + String path = it.@path + sourceDir.add(path) + if (path.startsWith("build/generated/source/proto")) { + if (path.contains("test")) { + // test source path has one more attribute: ["test"="true"] + assert it.attributes.attribute.size() == 4 + } else { + assert it.attributes.attribute.size() == 3 + } + } + } + + Set expectedSourceDir = ImmutableSet.builder() + .add('src/main/java') + .add('src/test/java') + .add('build/generated/source/proto/grpc/java') + .add('build/generated/source/proto/grpc/grpc_output') + .add('build/generated/source/proto/main/java') + .add('build/generated/source/proto/test/java') + .build() + assert Objects.equals(expectedSourceDir, sourceDir) + + where: + gradleVersion << GRADLE_VERSIONS + } +} diff --git a/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy b/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy index 6f23f18b..58c72b59 100644 --- a/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy +++ b/src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy @@ -1,6 +1,5 @@ package com.google.protobuf.gradle -import com.google.common.collect.ImmutableSet import groovy.transform.CompileDynamic import org.gradle.api.Project import org.gradle.testfixtures.ProjectBuilder @@ -339,130 +338,6 @@ class ProtobufJavaPluginTest extends Specification { gradleVersion << GRADLE_VERSIONS } - @Unroll - void "testProject proto and generated output directories should be added to intellij [gradle #gradleVersion]"() { - given: "project from testProject" - File projectDir = ProtobufPluginTestHelper.projectBuilder('testIdea') - .copyDirs('testProjectBase', 'testProject') - .build() - - when: "idea is invoked" - BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) - .withArguments('idea') - .withPluginClasspath() - .withGradleVersion(gradleVersion) - .build() - - then: "it succeed" - result.task(":idea").outcome == TaskOutcome.SUCCESS - Node imlRoot = new XmlParser().parse(projectDir.toPath().resolve("testProject.iml").toFile()) - Collection rootMgr = imlRoot.component.findAll { it.'@name' == 'NewModuleRootManager' } - assert rootMgr.size() == 1 - assert rootMgr.content.sourceFolder.size() == 1 - - Set sourceDir = [] as Set - Set testSourceDir = [] as Set - Set generatedDirs = [] as Set - rootMgr.content.sourceFolder[0].each { - if (Boolean.parseBoolean(it.@isTestSource)) { - testSourceDir.add(it.@url) - } else { - sourceDir.add(it.@url) - } - if (Boolean.parseBoolean(it.@generated)) { - generatedDirs.add(it.@url) - } - } - - Set expectedSourceDir = ImmutableSet.builder() - .add('file://$MODULE_DIR$/src/main/java') - .add('file://$MODULE_DIR$/src/grpc/proto') - .add('file://$MODULE_DIR$/src/main/proto') - .add('file://$MODULE_DIR$/build/extracted-include-protos/grpc') - .add('file://$MODULE_DIR$/build/extracted-protos/main') - .add('file://$MODULE_DIR$/build/extracted-include-protos/main') - .add('file://$MODULE_DIR$/build/extracted-protos/grpc') - .add('file://$MODULE_DIR$/build/generated/source/proto/grpc/java') - .add('file://$MODULE_DIR$/build/generated/source/proto/grpc/grpc_output') - .add('file://$MODULE_DIR$/build/generated/source/proto/main/java') - .build() - Set expectedTestSourceDir = ImmutableSet.builder() - .add('file://$MODULE_DIR$/src/test/java') - .add('file://$MODULE_DIR$/src/test/proto') - .add('file://$MODULE_DIR$/build/extracted-protos/test') - .add('file://$MODULE_DIR$/build/extracted-include-protos/test') - .add('file://$MODULE_DIR$/build/generated/source/proto/test/java') - .build() - Set expectedGeneratedDirs = [ - 'file://$MODULE_DIR$/build/extracted-include-protos/grpc', - 'file://$MODULE_DIR$/build/extracted-protos/main', - 'file://$MODULE_DIR$/build/extracted-include-protos/main', - 'file://$MODULE_DIR$/build/extracted-protos/grpc', - 'file://$MODULE_DIR$/build/generated/source/proto/grpc/java', - 'file://$MODULE_DIR$/build/generated/source/proto/grpc/grpc_output', - 'file://$MODULE_DIR$/build/generated/source/proto/main/java', - 'file://$MODULE_DIR$/build/extracted-protos/test', - 'file://$MODULE_DIR$/build/extracted-include-protos/test', - 'file://$MODULE_DIR$/build/generated/source/proto/test/java', - ] - assert Objects.equals(expectedSourceDir, sourceDir) - assert Objects.equals(expectedTestSourceDir, testSourceDir) - Objects.equals(expectedGeneratedDirs, generatedDirs) - - where: - gradleVersion << GRADLE_VERSIONS - } - - @Unroll - void "testProject proto and generated output directories should be added to Eclipse [gradle #gradleVersion]"() { - given: "project from testProject" - File projectDir = ProtobufPluginTestHelper.projectBuilder('testEclipse') - .copyDirs('testProjectBase', 'testProject') - .build() - - when: "eclipse is invoked" - BuildResult result = GradleRunner.create() - .withProjectDir(projectDir) - .withArguments('eclipse') - .withPluginClasspath() - .withGradleVersion(gradleVersion) - .build() - - then: "it succeed" - result.task(":eclipse").outcome == TaskOutcome.SUCCESS - Node classpathFile = new XmlParser().parse(projectDir.toPath().resolve(".classpath").toFile()) - Collection srcEntries = classpathFile.classpathentry.findAll {it.'@kind' == 'src'} - assert srcEntries.size() == 6 - - Set sourceDir = [] as Set - srcEntries.each { - String path = it.@path - sourceDir.add(path) - if (path.startsWith("build/generated/source/proto")) { - if (path.contains("test")) { - // test source path has one more attribute: ["test"="true"] - assert it.attributes.attribute.size() == 4 - } else { - assert it.attributes.attribute.size() == 3 - } - } - } - - Set expectedSourceDir = ImmutableSet.builder() - .add('src/main/java') - .add('src/test/java') - .add('build/generated/source/proto/grpc/java') - .add('build/generated/source/proto/grpc/grpc_output') - .add('build/generated/source/proto/main/java') - .add('build/generated/source/proto/test/java') - .build() - assert Objects.equals(expectedSourceDir, sourceDir) - - where: - gradleVersion << GRADLE_VERSIONS - } - @Unroll void "test proto generation is not up-to-date on dependency changes [gradle #gradleVersion]"() { given: "project from testProject" From 0bc669bbb4922a23c63a38f1d87dc46f2020ab7d Mon Sep 17 00:00:00 2001 From: rougsig Date: Sun, 21 Aug 2022 16:28:11 +0300 Subject: [PATCH 5/8] Disable fast-fail --- .github/workflows/testing.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index c5044786..8d14fdb1 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -28,6 +28,7 @@ jobs: strategy: matrix: tests: [ProtobufJavaPluginTest, ProtobufKotlinDslCopySpecTest, ProtobufKotlinDslPluginTest, ProtobufAndroidPluginTest, ProtobufAndroidPluginKotlinTest, AndroidProjectDetectionTest, IDESupportTest] + fail-fast: false runs-on: ubuntu-latest timeout-minutes: 30 steps: From f47592a94d6a5fba35131ef914a127006726cd5b Mon Sep 17 00:00:00 2001 From: rougsig Date: Mon, 29 Aug 2022 13:43:44 +0300 Subject: [PATCH 6/8] Fix tests after lint fixes --- .../groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy | 3 +-- src/main/groovy/com/google/protobuf/gradle/Utils.groovy | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy index 7713bdea..7e21c0dd 100644 --- a/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy @@ -482,8 +482,7 @@ class ProtobufPlugin implements Plugin { project.tasks.withType(GenerateProtoTask).each { GenerateProtoTask generateProtoTask -> generateProtoTask.getOutputSourceDirectories().each { File outputDir -> Utils.addToIdeSources(project, generateProtoTask.isTest, outputDir, true) - Utils.addToEclipseSources(project, generateProtoTask.isTest, - generateProtoTask.sourceSet.name, outputDir) + Utils.addToEclipseSources(project, generateProtoTask.isTest, generateProtoTask.sourceSet.name, outputDir) } } } diff --git a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy index d7a2d6d2..742fce9b 100644 --- a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy @@ -151,7 +151,7 @@ class Utils { String relativePath = projectDir.toURI().relativize(f.toURI()).getPath() // remove trailing slash if (relativePath.endsWith("/")) { - relativePath = relativePath[0..(relativePath.length() - 1)] + relativePath = relativePath[0..(relativePath.length() - 2)] } SourceFolder entry = new SourceFolder(relativePath, getOutputPath(cp, sourceSetName)) entry.entryAttributes.put("optional", "true") From 38a667a598f567c91ae17928389bd3e4092e1539 Mon Sep 17 00:00:00 2001 From: rougsig Date: Thu, 11 Aug 2022 02:10:07 +0300 Subject: [PATCH 7/8] Remove eclipse plugin configuration --- .../protobuf/gradle/ProtobufPlugin.groovy | 15 ++-- .../com/google/protobuf/gradle/Utils.groovy | 78 ------------------- .../protobuf/gradle/IDESupportTest.groovy | 4 +- 3 files changed, 11 insertions(+), 86 deletions(-) diff --git a/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy index 7f6fb873..e87e780e 100644 --- a/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy @@ -263,8 +263,16 @@ class ProtobufPlugin implements Plugin { } postConfigure.add { - boolean isTest = Utils.isTest(sourceSet.name) + project.plugins.withId("eclipse") { + // This is required because the intellij/eclipse plugin does not allow adding source directories + // that do not exist. The intellij/eclipse config files should be valid from the start. + generateProtoTask.get().getOutputSourceDirectories().each { File outputDir -> + outputDir.mkdirs() + } + } + project.plugins.withId("idea") { + boolean isTest = Utils.isTest(sourceSet.name) protoSrcDirSet.srcDirs.each { File protoDir -> Utils.addToIdeSources(project, isTest, protoDir, false) } @@ -274,11 +282,6 @@ class ProtobufPlugin implements Plugin { Utils.addToIdeSources(project, isTest, outputDir, true) } } - project.plugins.withId("eclipse") { - generateProtoTask.get().getOutputSourceDirectories().each { File outputDir -> - Utils.addToEclipseSources(project, isTest, sourceSet.name, outputDir) - } - } } } diff --git a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy index 742fce9b..89f9950f 100644 --- a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy @@ -33,10 +33,6 @@ import groovy.transform.CompileStatic import org.apache.commons.lang.StringUtils import org.gradle.api.Project import org.gradle.api.tasks.SourceSet -import org.gradle.plugins.ide.eclipse.model.Classpath -import org.gradle.plugins.ide.eclipse.model.ClasspathEntry -import org.gradle.plugins.ide.eclipse.model.EclipseModel -import org.gradle.plugins.ide.eclipse.model.SourceFolder import org.gradle.plugins.ide.idea.GenerateIdeaModule import org.gradle.plugins.ide.idea.model.IdeaModel import org.gradle.util.GUtil @@ -133,78 +129,4 @@ class Utils { } } } - - /** - * Add the the folder of generated source to Eclipse .classpath file. - */ - static void addToEclipseSources(Project project, boolean isTest, String sourceSetName, File f) { - project.plugins.withId("eclipse") { - File projectDir = project.getProjectDir() - - EclipseModel model = project.getExtensions().findByType(EclipseModel) - model.classpath.file.whenMerged { Classpath cp -> - // buildship requires the folder exists on disk, otherwise - // it will be ignored when updating classpath file, see: - // https://github.com/eclipse/buildship/issues/1196 - f.mkdirs() - - String relativePath = projectDir.toURI().relativize(f.toURI()).getPath() - // remove trailing slash - if (relativePath.endsWith("/")) { - relativePath = relativePath[0..(relativePath.length() - 2)] - } - SourceFolder entry = new SourceFolder(relativePath, getOutputPath(cp, sourceSetName)) - entry.entryAttributes.put("optional", "true") - entry.entryAttributes.put("ignore_optional_problems", "true") - - // this attribute is optional, but it could be useful for IDEs to recognize that it is - // generated source folder from protobuf, thus providing some support for that. - // e.g. Hint user to run generate tasks if the folder is empty or does not exist. - entry.entryAttributes.put("protobuf_generated_source", "true") - - // check if output is not null here because test source folder - // must have a separate output folder in Eclipse - if (entry.output != null && isTest) { - entry.entryAttributes.put("test", "true") - } - cp.entries.add(entry) - } - } - } - - /** - * Get the output path according to the source set name, if no valid path can be - * found, null will return. - */ - private static String getOutputPath(Classpath classpath, String sourceSetName) { - String path = "bin/" + sourceSetName - if (isValidOutput(classpath, path)) { - return path - } - // fallback to default output - return null - } - - /** - * Check if the output path is valid or not. - * See: org.eclipse.jdt.internal.core.ClasspathEntry#validateClasspath() - */ - private static boolean isValidOutput(Classpath classpath, String path) { - Set outputs = [] - for (ClasspathEntry cpe : classpath.getEntries()) { - if (cpe instanceof SourceFolder) { - outputs.add(((SourceFolder) cpe).getOutput()) - } - } - for (String output : outputs) { - if (Objects.equals(output, path)) { - continue - } - // Eclipse does not allow nested output path - if (output.startsWith(path) || path.startsWith(output)) { - return false - } - } - return true - } } diff --git a/src/test/groovy/com/google/protobuf/gradle/IDESupportTest.groovy b/src/test/groovy/com/google/protobuf/gradle/IDESupportTest.groovy index 4c36229e..40af80b7 100644 --- a/src/test/groovy/com/google/protobuf/gradle/IDESupportTest.groovy +++ b/src/test/groovy/com/google/protobuf/gradle/IDESupportTest.groovy @@ -119,9 +119,9 @@ class IDESupportTest extends Specification { if (path.startsWith("build/generated/source/proto")) { if (path.contains("test")) { // test source path has one more attribute: ["test"="true"] - assert it.attributes.attribute.size() == 4 - } else { assert it.attributes.attribute.size() == 3 + } else { + assert it.attributes.attribute.size() == 2 } } } From 3649cd85b957416283f556366cb2bd2fcb1ee119 Mon Sep 17 00:00:00 2001 From: rougsig Date: Mon, 26 Sep 2022 19:00:39 +0300 Subject: [PATCH 8/8] Add copyright header to IDESupportTest --- .../protobuf/gradle/IDESupportTest.groovy | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/test/groovy/com/google/protobuf/gradle/IDESupportTest.groovy b/src/test/groovy/com/google/protobuf/gradle/IDESupportTest.groovy index 40af80b7..489d3faa 100644 --- a/src/test/groovy/com/google/protobuf/gradle/IDESupportTest.groovy +++ b/src/test/groovy/com/google/protobuf/gradle/IDESupportTest.groovy @@ -1,3 +1,31 @@ +/* + * Copyright (c) 2017, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ package com.google.protobuf.gradle import com.google.common.collect.ImmutableSet