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.file.SourceDirectorySet
import org.gradle.api.internal.file.FileResolver
import org.gradle.api.logging.LogLevel
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.feature',
'com.android.library',
'android',
'android-library',
]
private static final String USER_LANG_PROP = 'protobufGradlePluginAdditionalLanguages'
private static final List SUPPORTED_LANGUAGES = [
'java',
'kotlin',
]
private static List getLanguages(Project project) {
List additionalLanguages = []
if (project.hasProperty(USER_LANG_PROP)) {
additionalLanguages = (List) project.property(USER_LANG_PROP)
project.logger.log(
LogLevel.WARN,
"protobuf plugin is now using additional unsupported languages: " + additionalLanguages)
}
return SUPPORTED_LANGUAGES + additionalLanguages
}
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.gradle.osdetector.OsDetectorPlugin])
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 linkGenerateProtoTasksToSourceCompile() 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()
linkGenerateProtoTasksToSourceCompile()
// 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())
// Register proto and generated sources with IDE
addSourcesToIde()
}
}
/**
* Creates a configuration if necessary for a source set so that the build
* author can configure dependencies for it.
*/
private void 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 void 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 void addProtoTasks() {
if (Utils.isAndroidProject(project)) {
getNonTestVariants().each { variant ->
addTasksForVariant(variant, false)
}
(project.android.unitTestVariants + project.android.testVariants).each { variant ->
addTasksForVariant(variant, true)
}
} else {
getSourceSets().each { sourceSet ->
addTasksForSourceSet(sourceSet)
}
}
}
/**
* Creates Protobuf tasks for a sourceSet in a Java project.
*/
private void 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 void addTasksForVariant(final Object variant, boolean isTestVariant) {
Task generateProtoTask = addGenerateProtoTask(variant.name, variant.sourceSets)
generateProtoTask.setVariant(variant, isTestVariant)
generateProtoTask.flavors = ImmutableList.copyOf(variant.productFlavors.collect { it.name } )
if (variant.hasProperty('buildType')) {
generateProtoTask.buildType = variant.buildType.name
}
generateProtoTask.doneInitializing()
variant.sourceSets.each {
Task extractProtosTask = maybeAddExtractProtosTask(it.name)
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
variant.sourceSets.each {
Task extractIncludeProtosTask =
maybeAddExtractIncludeProtosTask(it.name)
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