Skip to content

Commit 6bb945c

Browse files
authored
Merge pull request #560 from clayburn/457-ignore-os-input-to-generateProtoTask
Fixes to `protoc` inputs to `GenerateProtoTask`
2 parents c9576b6 + 56d5875 commit 6bb945c

File tree

3 files changed

+186
-5
lines changed

3 files changed

+186
-5
lines changed

src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy

+60-5
Original file line numberDiff line numberDiff line change
@@ -324,19 +324,74 @@ public abstract class GenerateProtoTask extends DefaultTask {
324324
return variant
325325
}
326326

327-
@Internal("Input captured by getAlternativePaths()")
327+
@Internal("Input captured by getExecutables()")
328328
abstract Property<ExecutableLocator> getProtocLocator()
329329

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

333-
@InputFiles
334-
@PathSensitive(PathSensitivity.NONE)
333+
@Internal("Input captured by getReleaseDependenciesMapping()")
334+
abstract MapProperty<String, String> getLocatorToDependencyMapping()
335+
336+
@Internal("This property is no longer an input, but kept and marked @Internal for backwards compatibility.")
335337
ConfigurableFileCollection getAlternativePaths() {
336338
return objectFactory.fileCollection().from(getLocatorToAlternativePathsMapping().get().values())
337339
}
338340

339-
@Internal("Input captured by getAlternativePaths()")
341+
/**
342+
* For each protoc and code gen plugin defined by an artifact specification, this list will contain a String with the
343+
* group, artifact, and version, as long as the version is a stable release version.
344+
*
345+
* Giving this as an input to the task allows gradle to ignore the OS classifier and use cached outputs generated from
346+
* different operating systems since the expectation is that different operating systems will produce the same
347+
* generated code.
348+
*/
349+
@Input
350+
Provider<List<String>> getReleaseArtifacts() {
351+
releaseDependenciesMapping.map { it.values().collect() }
352+
}
353+
354+
/**
355+
* This file collection contains the file for each protoc and code gen plugin that is defined by an artifact
356+
* specification that specifies a SNAPSHOT version.
357+
*
358+
* Since snapshots are expected to differ within the same version, this input allows Gradle to consider the file
359+
* itself rather than the version number.
360+
*/
361+
@InputFiles
362+
@PathSensitive(PathSensitivity.NONE)
363+
FileCollection getSnapshotArtifacts() {
364+
Provider<Collection<FileCollection>> snapshotArtifacts = locatorToAlternativePathsMapping.map { map ->
365+
Set<String> releaseArtifactKeys = releaseDependenciesMapping.get().keySet()
366+
map.findAll { entry ->
367+
!releaseArtifactKeys.contains(entry.key)
368+
}.values()
369+
}
370+
371+
objectFactory.fileCollection().from(snapshotArtifacts)
372+
}
373+
374+
@Internal
375+
Provider<Map<String, String>> getReleaseDependenciesMapping() {
376+
providerFactory.provider {
377+
locatorToDependencyMapping.get()
378+
.findAll { entry -> ! entry.value.endsWith ("-SNAPSHOT") }
379+
}
380+
}
381+
382+
@InputFiles
383+
@PathSensitive(PathSensitivity.NONE)
384+
FileCollection getExecutables() {
385+
objectFactory.fileCollection().from {
386+
protocLocator.getOrNull()?.path
387+
}.from {
388+
pluginsExecutableLocators.get().values()
389+
.collect { it.path }
390+
.findAll { it }
391+
}
392+
}
393+
394+
@Internal("Input captured by getExecutables()")
340395
abstract MapProperty<String, ExecutableLocator> getPluginsExecutableLocators()
341396

342397
@Internal("Not an actual input to the task, only used to find tasks belonging to a variant")

src/main/groovy/com/google/protobuf/gradle/ToolsLocator.groovy

+1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ class ToolsLocator {
112112
for (GenerateProtoTask protoTask in protoTasks) {
113113
if (protoc.is(locator) || protoTask.hasPlugin(locator.name)) {
114114
protoTask.locatorToAlternativePathsMapping.put(locator.name, artifactFiles)
115+
protoTask.locatorToDependencyMapping.put(locator.name, "$groupId:$artifact:$version".toString())
115116
}
116117
}
117118
}

src/test/groovy/com/google/protobuf/gradle/ProtobufJavaPluginTest.groovy

+125
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,131 @@ class ProtobufJavaPluginTest extends Specification {
414414
gradleVersion << GRADLE_VERSIONS
415415
}
416416
417+
@Unroll
418+
void "test proto generation is not up-to-date on dependency changes [gradle #gradleVersion]"() {
419+
given: "project from testProject"
420+
File projectDir = ProtobufPluginTestHelper.projectBuilder('testProject')
421+
.copyDirs('testProjectBase', 'testProject')
422+
.build()
423+
424+
when: "build is invoked"
425+
BuildResult result = GradleRunner.create()
426+
.withProjectDir(projectDir)
427+
.withArguments('build', '--stacktrace')
428+
.withPluginClasspath()
429+
.withGradleVersion(gradleVersion)
430+
.forwardStdOutput(new OutputStreamWriter(System.out))
431+
.forwardStdError(new OutputStreamWriter(System.err))
432+
.withDebug(true)
433+
.build()
434+
435+
then: "it succeeds"
436+
result.task(":build").outcome == TaskOutcome.SUCCESS
437+
438+
when: "protoc artifact is changed and build runs again"
439+
new File(projectDir, "build.gradle")
440+
.append("""
441+
protobuf {
442+
protoc {
443+
artifact = 'com.google.protobuf:protoc:3.0.2'
444+
}
445+
}""")
446+
result = GradleRunner.create()
447+
.withProjectDir(projectDir)
448+
.withArguments('build', '--stacktrace')
449+
.withPluginClasspath()
450+
.withGradleVersion(gradleVersion)
451+
.forwardStdOutput(new OutputStreamWriter(System.out))
452+
.forwardStdError(new OutputStreamWriter(System.err))
453+
.withDebug(true)
454+
.build()
455+
456+
then: "generateProto is not UP_TO_DATE"
457+
result.task(":generateProto").outcome == TaskOutcome.SUCCESS
458+
459+
when: "plugin artifact is changed and build runs again"
460+
new File(projectDir, "build.gradle")
461+
.append("""
462+
protobuf {
463+
plugins {
464+
grpc {
465+
artifact = 'io.grpc:protoc-gen-grpc-java:1.0.3'
466+
}
467+
}
468+
}""")
469+
result = GradleRunner.create()
470+
.withProjectDir(projectDir)
471+
.withArguments('build', '--stacktrace')
472+
.withPluginClasspath()
473+
.withGradleVersion(gradleVersion)
474+
.forwardStdOutput(new OutputStreamWriter(System.out))
475+
.forwardStdError(new OutputStreamWriter(System.err))
476+
.withDebug(true)
477+
.build()
478+
479+
then: "generateProto is not UP_TO_DATE"
480+
result.task(":generateGrpcProto").outcome == TaskOutcome.SUCCESS
481+
482+
where:
483+
gradleVersion << GRADLE_VERSIONS
484+
}
485+
486+
@Unroll
487+
void "test proto generation is not up-to-date on path changes [gradle #gradleVersion]"() {
488+
given: "project from testProject"
489+
File projectDir = ProtobufPluginTestHelper.projectBuilder('testProject')
490+
.copyDirs('testProjectBase', 'testProject')
491+
.build()
492+
493+
when: "protoc path is set and build is invoked"
494+
File buildGradleFile = new File(projectDir, "build.gradle")
495+
buildGradleFile.append("""
496+
configurations {
497+
protoc
498+
}
499+
500+
dependencies {
501+
protoc "com.google.protobuf:protoc:3.0.0:\$project.osdetector.classifier@exe"
502+
}
503+
504+
protobuf {
505+
protoc {
506+
path = "\$configurations.protoc.singleFile"
507+
}
508+
}""")
509+
BuildResult result = GradleRunner.create()
510+
.withProjectDir(projectDir)
511+
.withArguments('build', '--stacktrace')
512+
.withPluginClasspath()
513+
.withGradleVersion(gradleVersion)
514+
.forwardStdOutput(new OutputStreamWriter(System.out))
515+
.forwardStdError(new OutputStreamWriter(System.err))
516+
.withDebug(true)
517+
.build()
518+
519+
then: "it succeeds"
520+
result.task(":generateProto").outcome == TaskOutcome.SUCCESS
521+
522+
when: "protoc path is changed and build runs again"
523+
buildGradleFile.text = buildGradleFile.text.replace("com.google.protobuf:protoc:3.0.0",
524+
"com.google.protobuf:protoc:3.0.2")
525+
result = GradleRunner.create()
526+
.withProjectDir(projectDir)
527+
.withArguments('build', '--stacktrace')
528+
.withPluginClasspath()
529+
.withGradleVersion(gradleVersion)
530+
.forwardStdOutput(new OutputStreamWriter(System.out))
531+
.forwardStdError(new OutputStreamWriter(System.err))
532+
.withDebug(true)
533+
.build()
534+
535+
then: "generateProto is not UP_TO_DATE"
536+
result.task(":generateProto").outcome == TaskOutcome.SUCCESS
537+
538+
where:
539+
gradleVersion << GRADLE_VERSIONS
540+
}
541+
417542
@Unroll
418543
void "test proto extraction is up-to-date for testProject when changing java sources [gradle #gradleVersion]"() {
419544
given: "project from testProject"

0 commit comments

Comments
 (0)