com.google.protobuf.gradle.ProtobufPlugin.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of protobuf-gradle-plugin Show documentation
Show all versions of protobuf-gradle-plugin Show documentation
Gradle build plugin to handle Protocol Buffers automated code generation and compilation
/*
* Original work copyright (c) 2015, Alex Antonov. All rights reserved.
* Modified work copyright (c) 2015, 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.ImmutableList
import org.gradle.api.Action
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.attributes.Attribute
import org.gradle.api.file.ConfigurableFileTree
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.file.FileResolver
import org.gradle.api.plugins.AppliedPlugin
import org.gradle.api.tasks.SourceSet
import javax.inject.Inject
/**
* The main class for the protobuf plugin.
*/
class ProtobufPlugin implements Plugin {
// any one of these plugins should be sufficient to proceed with applying this plugin
private static final List PREREQ_PLUGIN_OPTIONS = [
'java',
'com.android.application',
'com.android.library',
'android',
'android-library',
]
private final FileResolver fileResolver
private Project project
private boolean wasApplied = false
@Inject
public ProtobufPlugin(FileResolver fileResolver) {
this.fileResolver = fileResolver
}
void apply(final Project project) {
this.project = project
// At least one of the prerequisite plugins must by applied before this plugin can be applied, so
// we will use the PluginManager.withPlugin() callback mechanism to delay applying this plugin until
// after that has been achieved. If project evaluation completes before one of the prerequisite plugins
// has been applied then we will assume that none of prerequisite plugins were specified and we will
// throw an Exception to alert the user of this configuration issue.
Action super AppliedPlugin> applyWithPrerequisitePlugin = { prerequisitePlugin ->
if (wasApplied) {
project.logger.warn('The com.google.protobuf plugin was already applied to the project: ' + project.path
+ ' and will not be applied again after plugin: ' + prerequisitePlugin.id)
} else {
wasApplied = true
doApply()
}
}
PREREQ_PLUGIN_OPTIONS.each { pluginName ->
project.pluginManager.withPlugin(pluginName, applyWithPrerequisitePlugin)
}
project.afterEvaluate {
if (!wasApplied) {
throw new GradleException('The com.google.protobuf plugin could not be applied during project evaluation.'
+ ' The Java plugin or one of the Android plugins must be applied to the project first.')
}
}
}
private void doApply() {
// Provides the osdetector extension
project.apply([plugin:'com.google.osdetector'])
project.convention.plugins.protobuf = new ProtobufConvention(project, fileResolver)
addSourceSetExtensions()
getSourceSets().all { sourceSet ->
createConfiguration(sourceSet.name)
}
project.afterEvaluate {
// The Android variants are only available at this point.
addProtoTasks()
project.protobuf.runTaskConfigClosures()
// Disallow user configuration outside the config closures, because
// next in linkGenerateProtoTasksToJavaCompile() we add generated,
// outputs to the inputs of javaCompile tasks, and any new codegen
// plugin output added after this point won't be added to javaCompile
// tasks.
project.protobuf.generateProtoTasks.all()*.doneConfig()
linkGenerateProtoTasksToJavaCompile()
// protoc and codegen plugin configuration may change through the protobuf{}
// block. Only at this point the configuration has been finalized.
project.protobuf.tools.registerTaskDependencies(project.protobuf.getGenerateProtoTasks().all())
}
}
/**
* Creates a configuration if necessary for a source set so that the build
* author can configure dependencies for it.
*/
private createConfiguration(String sourceSetName) {
String configName = Utils.getConfigName(sourceSetName, 'protobuf')
if (project.configurations.findByName(configName) == null) {
project.configurations.create(configName) {
visible = false
transitive = true
extendsFrom = []
}
}
}
/**
* Adds the proto extension to all SourceSets, e.g., it creates
* sourceSets.main.proto and sourceSets.test.proto.
*/
private addSourceSetExtensions() {
getSourceSets().all { sourceSet ->
sourceSet.extensions.create('proto', ProtobufSourceDirectorySet, sourceSet.name, fileResolver)
}
}
/**
* Returns the sourceSets container of a Java or an Android project.
*/
private Object getSourceSets() {
if (Utils.isAndroidProject(project)) {
return project.android.sourceSets
}
return project.sourceSets
}
private Object getNonTestVariants() {
return project.android.hasProperty('libraryVariants') ?
project.android.libraryVariants : project.android.applicationVariants
}
/**
* Adds Protobuf-related tasks to the project.
*/
private addProtoTasks() {
if (Utils.isAndroidProject(project)) {
getNonTestVariants().each { variant ->
addTasksForVariant(variant, false)
}
project.android.testVariants.each { testVariant ->
addTasksForVariant(testVariant, true)
}
} else {
getSourceSets().each { sourceSet ->
addTasksForSourceSet(sourceSet)
}
}
}
/**
* Creates Protobuf tasks for a sourceSet in a Java project.
*/
private addTasksForSourceSet(final SourceSet sourceSet) {
Task generateProtoTask = addGenerateProtoTask(sourceSet.name, [sourceSet])
generateProtoTask.sourceSet = sourceSet
generateProtoTask.doneInitializing()
generateProtoTask.builtins {
java { }
}
Task extractProtosTask = maybeAddExtractProtosTask(sourceSet.name)
generateProtoTask.dependsOn(extractProtosTask)
Task extractIncludeProtosTask = maybeAddExtractIncludeProtosTask(sourceSet.name)
generateProtoTask.dependsOn(extractIncludeProtosTask)
// Include source proto files in the compiled archive, so that proto files from
// dependent projects can import them.
Task processResourcesTask =
project.tasks.getByName(sourceSet.getTaskName('process', 'resources'))
processResourcesTask.from(generateProtoTask.inputs.sourceFiles) {
include '**/*.proto'
}
}
/**
* Creates Protobuf tasks for a variant in an Android project.
*/
private addTasksForVariant(final Object variant, final boolean isTestVariant) {
// The collection of sourceSets that will be compiled for this variant
List sourceSetNames = []
List sourceSets = []
if (isTestVariant) {
// All test variants will include the androidTest sourceSet
sourceSetNames.add 'androidTest'
} else {
// All non-test variants will include the main sourceSet
sourceSetNames.add 'main'
}
sourceSetNames.add variant.name
sourceSetNames.add variant.buildType.name
ImmutableList.Builder flavorListBuilder = ImmutableList.builder()
if (variant.hasProperty('productFlavors')) {
variant.productFlavors.each { flavor ->
sourceSetNames.add flavor.name
flavorListBuilder.add flavor.name
}
}
sourceSetNames.each { sourceSetName ->
sourceSets.add project.android.sourceSets.maybeCreate(sourceSetName)
}
Task generateProtoTask = addGenerateProtoTask(variant.name, sourceSets)
generateProtoTask.setVariant(variant, isTestVariant)
generateProtoTask.flavors = flavorListBuilder.build()
generateProtoTask.buildType = variant.buildType.name
generateProtoTask.doneInitializing()
sourceSetNames.each { sourceSetName ->
Task extractProtosTask = maybeAddExtractProtosTask(sourceSetName)
generateProtoTask.dependsOn(extractProtosTask)
}
if (variant.hasProperty("compileConfiguration")) {
// For Android Gradle plugin >= 2.5
Attribute artifactType = Attribute.of("artifactType", String)
String name = variant.name
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
Task extractIncludeProtosTask = maybeAddExtractIncludeProtosTask(
name, classPathConfig, testClassPathConfig)
generateProtoTask.dependsOn(extractIncludeProtosTask)
} else {
// For Android Gradle plugin < 2.5
sourceSetNames.each { sourceSetName ->
Task extractIncludeProtosTask =
maybeAddExtractIncludeProtosTask(sourceSetName)
generateProtoTask.dependsOn(extractIncludeProtosTask)
}
}
// TODO(zhangkun83): Include source proto files in the compiled archive,
// so that proto files from dependent projects can import them.
}
/**
* Adds a task to run protoc and compile all proto source files for a sourceSet or variant.
*
* @param sourceSetOrVariantName the name of the sourceSet (Java) or
* variant (Android) that this task will run for.
*
* @param sourceSets the sourceSets that contains the proto files to be
* compiled. For Java it's the sourceSet that sourceSetOrVariantName stands
* for; for Android it's the collection of sourceSets that the variant includes.
*/
private Task addGenerateProtoTask(String sourceSetOrVariantName, Collection