Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes to protoc inputs to GenerateProtoTask #560

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -324,19 +324,74 @@ public abstract class GenerateProtoTask extends DefaultTask {
return variant
}

@Internal("Input captured by getAlternativePaths()")
@Internal("Input captured by getExecutables()")
abstract Property<ExecutableLocator> getProtocLocator()

@Internal("Input captured by getAlternativePaths(), this is used to query alternative path by locator name.")
@Internal("Input captured by getSnapshotArtifacts(), this is used to query alternative path by locator name.")
abstract MapProperty<String, FileCollection> getLocatorToAlternativePathsMapping()

@InputFiles
@PathSensitive(PathSensitivity.NONE)
@Internal("Input captured by getReleaseDependenciesMapping()")
abstract MapProperty<String, String> getLocatorToDependencyMapping()

@Internal("This property is no longer an input, but kept and marked @Internal for backwards compatibility.")
ConfigurableFileCollection getAlternativePaths() {
return objectFactory.fileCollection().from(getLocatorToAlternativePathsMapping().get().values())
}

@Internal("Input captured by getAlternativePaths()")
/**
* For each protoc and code gen plugin defined by an artifact specification, this list will contain a String with the
* group, artifact, and version, as long as the version is a stable release version.
*
* Giving this as an input to the task allows gradle to ignore the OS classifier and use cached outputs generated from
* different operating systems since the expectation is that different operating systems will produce the same
* generated code.
*/
@Input
Provider<List<String>> getReleaseArtifacts() {
releaseDependenciesMapping.map { it.values().collect() }
}

/**
* This file collection contains the file for each protoc and code gen plugin that is defined by an artifact
* specification that specifies a SNAPSHOT version.
*
* Since snapshots are expected to differ within the same version, this input allows Gradle to consider the file
* itself rather than the version number.
*/
@InputFiles
@PathSensitive(PathSensitivity.NONE)
FileCollection getSnapshotArtifacts() {
Provider<Collection<FileCollection>> snapshotArtifacts = locatorToAlternativePathsMapping.map { map ->
Set<String> releaseArtifactKeys = releaseDependenciesMapping.get().keySet()
map.findAll { entry ->
!releaseArtifactKeys.contains(entry.key)
}.values()
}

objectFactory.fileCollection().from(snapshotArtifacts)
}

@Internal
Provider<Map<String, String>> getReleaseDependenciesMapping() {
providerFactory.provider {
locatorToDependencyMapping.get()
.findAll { entry -> ! entry.value.endsWith ("-SNAPSHOT") }
}
}

@InputFiles
@PathSensitive(PathSensitivity.NONE)
FileCollection getExecutables() {
objectFactory.fileCollection().from {
protocLocator.getOrNull()?.path
}.from {
pluginsExecutableLocators.get().values()
.collect { it.path }
.findAll { it }
}
}

@Internal("Input captured by getExecutables()")
abstract MapProperty<String, ExecutableLocator> getPluginsExecutableLocators()

@Internal("Not an actual input to the task, only used to find tasks belonging to a variant")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ class ToolsLocator {
for (GenerateProtoTask protoTask in protoTasks) {
if (protoc.is(locator) || protoTask.hasPlugin(locator.name)) {
protoTask.locatorToAlternativePathsMapping.put(locator.name, artifactFiles)
protoTask.locatorToDependencyMapping.put(locator.name, "$groupId:$artifact:$version".toString())
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,131 @@ class ProtobufJavaPluginTest extends Specification {
gradleVersion << GRADLE_VERSIONS
}

@Unroll
void "test proto generation is not up-to-date on dependency changes [gradle #gradleVersion]"() {
given: "project from testProject"
File projectDir = ProtobufPluginTestHelper.projectBuilder('testProject')
.copyDirs('testProjectBase', 'testProject')
.build()

when: "build is invoked"
BuildResult result = GradleRunner.create()
.withProjectDir(projectDir)
.withArguments('build', '--stacktrace')
.withPluginClasspath()
.withGradleVersion(gradleVersion)
.forwardStdOutput(new OutputStreamWriter(System.out))
.forwardStdError(new OutputStreamWriter(System.err))
.withDebug(true)
.build()

then: "it succeeds"
result.task(":build").outcome == TaskOutcome.SUCCESS

when: "protoc artifact is changed and build runs again"
new File(projectDir, "build.gradle")
.append("""
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.0.2'
}
}""")
result = GradleRunner.create()
.withProjectDir(projectDir)
.withArguments('build', '--stacktrace')
.withPluginClasspath()
.withGradleVersion(gradleVersion)
.forwardStdOutput(new OutputStreamWriter(System.out))
.forwardStdError(new OutputStreamWriter(System.err))
.withDebug(true)
.build()

then: "generateProto is not UP_TO_DATE"
result.task(":generateProto").outcome == TaskOutcome.SUCCESS

when: "plugin artifact is changed and build runs again"
new File(projectDir, "build.gradle")
.append("""
protobuf {
plugins {
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.0.3'
}
}
}""")
result = GradleRunner.create()
.withProjectDir(projectDir)
.withArguments('build', '--stacktrace')
.withPluginClasspath()
.withGradleVersion(gradleVersion)
.forwardStdOutput(new OutputStreamWriter(System.out))
.forwardStdError(new OutputStreamWriter(System.err))
.withDebug(true)
.build()

then: "generateProto is not UP_TO_DATE"
result.task(":generateGrpcProto").outcome == TaskOutcome.SUCCESS

where:
gradleVersion << GRADLE_VERSIONS
}

@Unroll
void "test proto generation is not up-to-date on path changes [gradle #gradleVersion]"() {
given: "project from testProject"
File projectDir = ProtobufPluginTestHelper.projectBuilder('testProject')
.copyDirs('testProjectBase', 'testProject')
.build()

when: "protoc path is set and build is invoked"
File buildGradleFile = new File(projectDir, "build.gradle")
buildGradleFile.append("""
configurations {
protoc
}

dependencies {
protoc "com.google.protobuf:protoc:3.0.0:\$project.osdetector.classifier@exe"
}

protobuf {
protoc {
path = "\$configurations.protoc.singleFile"
}
}""")
BuildResult result = GradleRunner.create()
.withProjectDir(projectDir)
.withArguments('build', '--stacktrace')
.withPluginClasspath()
.withGradleVersion(gradleVersion)
.forwardStdOutput(new OutputStreamWriter(System.out))
.forwardStdError(new OutputStreamWriter(System.err))
.withDebug(true)
.build()

then: "it succeeds"
result.task(":generateProto").outcome == TaskOutcome.SUCCESS

when: "protoc path is changed and build runs again"
buildGradleFile.text = buildGradleFile.text.replace("com.google.protobuf:protoc:3.0.0",
"com.google.protobuf:protoc:3.0.2")
result = GradleRunner.create()
.withProjectDir(projectDir)
.withArguments('build', '--stacktrace')
.withPluginClasspath()
.withGradleVersion(gradleVersion)
.forwardStdOutput(new OutputStreamWriter(System.out))
.forwardStdError(new OutputStreamWriter(System.err))
.withDebug(true)
.build()

then: "generateProto is not UP_TO_DATE"
result.task(":generateProto").outcome == TaskOutcome.SUCCESS

where:
gradleVersion << GRADLE_VERSIONS
}

@Unroll
void "test proto extraction is up-to-date for testProject when changing java sources [gradle #gradleVersion]"() {
given: "project from testProject"
Expand Down