Skip to content

Commit

Permalink
Support packaging and extracting protos in Android aar (take 2) (#443)
Browse files Browse the repository at this point in the history
Fixes the issue of ExtractIncludeProto tasks failing to match dependencies, which was caused by the previous commit trying to extract included protos for each Android source set instead for each variant.

Android projects should extract included protos from each variant's compileClasspath as variants are compilation units for Android projects. So we will not create compileProtoPath configurations for Android source sets.

This change includes an integration test for covering packaging/extracting protos in AAR.
  • Loading branch information
voidzcy authored Nov 18, 2020
1 parent d96d033 commit a06c7ad
Show file tree
Hide file tree
Showing 21 changed files with 772 additions and 7 deletions.
38 changes: 33 additions & 5 deletions src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.LibraryElements
import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
Expand Down Expand Up @@ -130,8 +131,15 @@ class ProtobufPlugin implements Plugin<Project> {
project.afterEvaluate {
// All custom source sets, configurations created by other plugins,
// and Android variants are only available at this point.
getSourceSets().each { sourceSet ->
createCompileProtoPathConfiguration(sourceSet.name)

// Java projects will extract included protos from a 'compileProtoPath'
// configuration of each source set, while Android projects will
// extract included protos from {@code variant.compileConfiguration}
// of each variant.
if (!Utils.isAndroidProject(project)) {
getSourceSets().each { sourceSet ->
createCompileProtoPathConfiguration(sourceSet.name)
}
}
addProtoTasks()
project.protobuf.runTaskConfigClosures()
Expand Down Expand Up @@ -173,6 +181,7 @@ class ProtobufPlugin implements Plugin<Project> {
* The extract-include-protos task of each source set will extract protobuf files from
* dependencies in this configuration.
*
* <p> For Java projects only.
* <p> This works around 'java-library' plugin not exposing resources to consumers for compilation.
*/
private void createCompileProtoPathConfiguration(String sourceSetName) {
Expand Down Expand Up @@ -267,6 +276,7 @@ class ProtobufPlugin implements Plugin<Project> {
* Creates Protobuf tasks for a variant in an Android project.
*/
private void addTasksForVariant(final Object variant, boolean isTestVariant) {
// GenerateProto task, one per variant (compilation unit).
Task generateProtoTask = addGenerateProtoTask(variant.name, variant.sourceSets)
generateProtoTask.setVariant(variant, isTestVariant)
generateProtoTask.flavors = ImmutableList.copyOf(variant.productFlavors.collect { it.name } )
Expand All @@ -275,19 +285,37 @@ class ProtobufPlugin implements Plugin<Project> {
}
generateProtoTask.doneInitializing()

// ExtractIncludeProto task, one per variant (compilation unit).
// Proto definitions from an AAR dependencies are in its JAR resources.
Attribute artifactType = Attribute.of("artifactType", String)
FileCollection classPathConfig = variant.compileConfiguration.incoming.artifactView {
attributes {
it.attribute(artifactType, "jar")
}
}.files
FileCollection testClassPathConfig =
variant.hasProperty("testedVariant") ?
variant.testedVariant.compileConfiguration.incoming.artifactView {
attributes {
it.attribute(artifactType, "jar")
}
}.files : null
setupExtractIncludeProtosTask(generateProtoTask, variant.name, classPathConfig, testClassPathConfig)

// ExtractProto task, one per source set.
if (project.android.hasProperty('libraryVariants')) {
// Include source proto files in the compiled archive, so that proto files from
// dependent projects can import them.
Task processResourcesTask = variant.getProcessJavaResourcesProvider().get()
processResourcesTask.from(generateProtoTask.sourceFiles) {
include '**/*.proto'
}
variant.sourceSets.each {
setupExtractIncludeProtosTask(generateProtoTask, it.name)
Task extractTask = setupExtractProtosTask(generateProtoTask, it.name)
processResourcesTask.dependsOn(extractTask)
}
} else {
variant.sourceSets.each {
setupExtractIncludeProtosTask(generateProtoTask, it.name)
setupExtractProtosTask(generateProtoTask, it.name)
}
}
Expand Down Expand Up @@ -384,7 +412,7 @@ class ProtobufPlugin implements Plugin<Project> {
// dependencies.
if (Utils.isTest(sourceSetOrVariantName)) {
inputFiles.from getSourceSets()['main'].proto
inputFiles.from testedCompileClasspathConfiguration ?: project.configurations['compile']
inputFiles.from testedCompileClasspathConfiguration
}
} else {
// In Java projects, the compileClasspath of the 'test' sourceSet includes all the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,37 @@ class ProtobufAndroidPluginTest extends Specification {
gradleVersion << GRADLE_VERSION.takeRight(1)
}

@Unroll
void "testProjectAndroidDependent [android #agpVersion, gradle #gradleVersion, kotlin #kotlinVersion]"() {
given: "project from testProjectAndroidLibrary, testProjectAndroid"
File testProjectStaging = ProtobufPluginTestHelper.projectBuilder('testProject')
.copyDirs('testProjectBase', 'testProject')
.build()
File testProjectLibraryStaging = ProtobufPluginTestHelper.projectBuilder('testProjectAndroidLibrary')
.copyDirs('testProjectAndroidLibrary')
.build()
File testProjectAndroidStaging = ProtobufPluginTestHelper.projectBuilder('testProjectAndroid')
.copyDirs('testProjectAndroidDependentBase', 'testProjectAndroid')
.build()
File mainProjectDir = ProtobufPluginTestHelper.projectBuilder('testProjectAndroidDependentMain')
.copySubProjects(testProjectStaging, testProjectLibraryStaging, testProjectAndroidStaging)
.withAndroidPlugin(agpVersion)
.build()
when: "build is invoked"
BuildResult result = buildAndroidProject(
mainProjectDir,
gradleVersion,
"testProjectAndroid:build"
)

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

where:
agpVersion << ANDROID_PLUGIN_VERSION
gradleVersion << GRADLE_VERSION
}

@Unroll
void "testProjectAndroidKotlin [android #agpVersion, gradle #gradleVersion, kotlin #kotlinVersion]"() {
given: "project from testProject, testProjectLite & testProjectAndroid"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class TestLibrary {
// From src/main/proto/helloworld.proto
Helloworld.HelloRequest request;

// From testProjectLite: src/nano/proto/messages.proto
// From dependency project (testProjectLite/testProjectAndroidLibrary): src/proto/messages.proto
io.grpc.testing.Messages.SimpleRequest simpleRequest;

// From lib/protos.jar
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public final class UnitTest {
// From src/main/proto/helloworld.proto
private Helloworld.HelloRequest request;

// From testProjectLite: src/nano/proto/messages.proto
// From dependency project (testProjectLite/testProjectAndroidLibrary): src/proto/messages.proto
private io.grpc.testing.Messages.SimpleRequest simpleRequest;

// From lib/protos.jar
Expand Down
Loading

0 comments on commit a06c7ad

Please sign in to comment.