From 6a35a0284202807c3ac4613d3db578d789d0bf88 Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Fri, 27 May 2016 13:58:35 +0800 Subject: [PATCH 01/27] Refactor plugin for more language support Change-Id: I8758b8d3b094c08a4ac7d15b97de8c59053198e5 --- .../protobuf/gradle/GenerateProtoTask.groovy | 3 +- .../protobuf/gradle/ProtobufPlugin.groovy | 403 ------------------ .../protobuf/gradle/TaskGenerator.groovy | 122 ++++++ .../com/google/protobuf/gradle/Utils.groovy | 33 ++ .../plugins/ProtobufAndroidPlugin.groovy | 124 ++++++ .../gradle/plugins/ProtobufBasePlugin.groovy | 27 ++ .../gradle/plugins/ProtobufJavaPlugin.groovy | 93 ++++ .../gradle/plugins/ProtobufPlugin.groovy | 82 ++++ .../com.google.protobuf.android.properties | 1 + .../com.google.protobuf.base.properties | 1 + .../com.google.protobuf.java.properties | 1 + .../com.google.protobuf.properties | 2 +- 12 files changed, 486 insertions(+), 406 deletions(-) delete mode 100644 src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy create mode 100644 src/main/groovy/com/google/protobuf/gradle/TaskGenerator.groovy create mode 100644 src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy create mode 100644 src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy create mode 100644 src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy create mode 100644 src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy create mode 100644 src/main/resources/META-INF/gradle-plugins/com.google.protobuf.android.properties create mode 100644 src/main/resources/META-INF/gradle-plugins/com.google.protobuf.base.properties create mode 100644 src/main/resources/META-INF/gradle-plugins/com.google.protobuf.java.properties diff --git a/src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy b/src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy index 45951dd0..56e03e58 100644 --- a/src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy @@ -202,8 +202,7 @@ public class GenerateProtoTask extends DefaultTask { throw new IllegalStateException( "requested descriptor path but descriptor generation is off") } - return descriptorSetOptions.path != null - ? descriptorSetOptions.path : "${outputBaseDir}/descriptor_set.desc" + return descriptorSetOptions.path != null ? descriptorSetOptions.path : "${outputBaseDir}/descriptor_set.desc" } public GenerateProtoTask() { diff --git a/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy deleted file mode 100644 index c6e02e3a..00000000 --- a/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy +++ /dev/null @@ -1,403 +0,0 @@ -/* - * 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.file.ConfigurableFileTree -import org.gradle.api.internal.file.FileResolver -import org.gradle.api.plugins.AppliedPlugin -import org.gradle.api.tasks.SourceSet - -import javax.inject.Inject - -class ProtobufPlugin implements Plugin { - // any one of these plugins should be sufficient to proceed with applying this plugin - private static final List prerequisitePluginOptions = [ - '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 - def gv = project.gradle.gradleVersion =~ "(\\d*)\\.(\\d*).*" - if (!gv || !gv.matches() || gv.group(1).toInteger() != 2 || gv.group(2).toInteger() < 12) { - println("You are using Gradle ${project.gradle.gradleVersion}: " - + " This version of the protobuf plugin works with Gradle version 2.12+") - } - - // 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 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() - } - } - - prerequisitePluginOptions.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: '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 = false - 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 - } else { - 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) { - def generateProtoTask = addGenerateProtoTask(sourceSet.name, [sourceSet]) - generateProtoTask.sourceSet = sourceSet - generateProtoTask.doneInitializing() - generateProtoTask.builtins { - java {} - } - - def extractProtosTask = maybeAddExtractProtosTask(sourceSet.name) - generateProtoTask.dependsOn(extractProtosTask) - - def 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. - def 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 - def sourceSetNames = new ArrayList() - def sourceSets = new ArrayList() - 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) - } - - def generateProtoTask = addGenerateProtoTask(variant.name, sourceSets) - generateProtoTask.setVariant(variant, isTestVariant) - generateProtoTask.flavors = flavorListBuilder.build() - generateProtoTask.buildType = variant.buildType.name - generateProtoTask.doneInitializing() - generateProtoTask.builtins { - javanano {} - } - - sourceSetNames.each { sourceSetName -> - def extractProtosTask = maybeAddExtractProtosTask(sourceSetName) - generateProtoTask.dependsOn(extractProtosTask) - - def 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 sourceSets) { - def generateProtoTaskName = 'generate' + - Utils.getSourceSetSubstringForTaskNames(sourceSetOrVariantName) + 'Proto' - return project.tasks.create(generateProtoTaskName, GenerateProtoTask) { - description = "Compiles Proto source for '${sourceSetOrVariantName}'" - outputBaseDir = "${project.protobuf.generatedFilesBaseDir}/${sourceSetOrVariantName}" - sourceSets.each { sourceSet -> - // Include sources - inputs.source sourceSet.proto - ProtobufSourceDirectorySet protoSrcDirSet = sourceSet.proto - protoSrcDirSet.srcDirs.each { srcDir -> - include srcDir - } - - // Include extracted sources - ConfigurableFileTree extractedProtoSources = - project.fileTree(getExtractedProtosDir(sourceSet.name)) { - include "**/*.proto" - } - inputs.source extractedProtoSources - include extractedProtoSources.dir - - // Register extracted include protos - ConfigurableFileTree extractedIncludeProtoSources = - project.fileTree(getExtractedIncludeProtosDir(sourceSet.name)) { - include "**/*.proto" - } - // Register them as input, but not as "source". - // Inputs are checked in incremental builds, but only "source" files are compiled. - inputs.dir extractedIncludeProtoSources - // Add the extracted include dir to the --proto_path include paths. - include extractedIncludeProtoSources.dir - } - } - } - - /** - * Adds a task to extract protos from protobuf dependencies. They are - * treated as sources and will be compiled. - * - *

This task is per-sourceSet, for both Java and Android. In Android a - * variant may have multiple sourceSets, each of these sourceSets will have - * its own extraction task. - */ - private Task maybeAddExtractProtosTask(String sourceSetName) { - def extractProtosTaskName = 'extract' + - Utils.getSourceSetSubstringForTaskNames(sourceSetName) + 'Proto' - Task existingTask = project.tasks.findByName(extractProtosTaskName) - if (existingTask != null) { - return existingTask - } - return project.tasks.create(extractProtosTaskName, ProtobufExtract) { - description = "Extracts proto files/dependencies specified by 'protobuf' configuration" - destDir = getExtractedProtosDir(sourceSetName) as File - inputs.files project.configurations[Utils.getConfigName(sourceSetName, 'protobuf')] - } - } - - /** - * Adds a task to extract protos from compile dependencies of a sourceSet, - * if there isn't one. Those are needed for imports in proto files, but - * they won't be compiled since they have already been compiled in their - * own projects or artifacts. - * - *

This task is per-sourceSet, for both Java and Android. In Android a - * variant may have multiple sourceSets, each of these sourceSets will have - * its own extraction task. - */ - private Task maybeAddExtractIncludeProtosTask(String sourceSetName) { - def extractIncludeProtosTaskName = 'extractInclude' + - Utils.getSourceSetSubstringForTaskNames(sourceSetName) + 'Proto' - Task existingTask = project.tasks.findByName(extractIncludeProtosTaskName) - if (existingTask != null) { - return existingTask - } - return project.tasks.create(extractIncludeProtosTaskName, ProtobufExtract) { - description = "Extracts proto files from compile dependencies for includes" - destDir = getExtractedIncludeProtosDir(sourceSetName) as File - inputs.files project.configurations[Utils.getConfigName(sourceSetName, 'compile')] - - // TL; DR: Make protos in 'test' sourceSet able to import protos from the 'main' sourceSet. - // Sub-configurations, e.g., 'testCompile' that extends 'compile', don't depend on the - // their super configurations. As a result, 'testCompile' doesn't depend on 'compile' and - // it cannot get the proto files from 'main' sourceSet through the configuration. However, - if (Utils.isAndroidProject(project)) { - // TODO(zhangkun83): Android sourceSet doesn't have compileClasspath. If it did, we - // haven't figured out a way to put source protos in 'resources'. For now we use an ad-hoc - // solution that manually includes the source protos of 'main' and its dependencies. - if (sourceSetName == 'androidTest') { - inputs.files getSourceSets()['main'].proto - inputs.files project.configurations['compile'] - } - } else { - // In Java projects, the compileClasspath of the 'test' sourceSet includes all the - // 'resources' of the output of 'main', in which the source protos are placed. - // This is nicer than the ad-hoc solution that Android has, because it works for any - // extended configuration, not just 'testCompile'. - inputs.files getSourceSets()[sourceSetName].compileClasspath - } - } - } - - private linkGenerateProtoTasksToJavaCompile() { - if (Utils.isAndroidProject(project)) { - (getNonTestVariants() + project.android.testVariants).each { variant -> - project.protobuf.generateProtoTasks.ofVariant(variant.name).each { generateProtoTask -> - // This cannot be called once task execution has started - variant.registerJavaGeneratingTask(generateProtoTask, generateProtoTask.getAllOutputDirs()) - } - } - } else { - project.sourceSets.each { sourceSet -> - def javaCompileTask = project.tasks.getByName(sourceSet.getCompileTaskName("java")) - project.protobuf.generateProtoTasks.ofSourceSet(sourceSet.name).each { generateProtoTask -> - javaCompileTask.dependsOn(generateProtoTask) - generateProtoTask.getAllOutputDirs().each { dir -> - javaCompileTask.source project.fileTree(dir: dir) - } - } - } - } - } - - private String getExtractedIncludeProtosDir(String sourceSetName) { - return "${project.buildDir}/extracted-include-protos/${sourceSetName}" - } - - private String getExtractedProtosDir(String sourceSetName) { - return "${project.buildDir}/extracted-protos/${sourceSetName}" - } - -} diff --git a/src/main/groovy/com/google/protobuf/gradle/TaskGenerator.groovy b/src/main/groovy/com/google/protobuf/gradle/TaskGenerator.groovy new file mode 100644 index 00000000..1e95bab4 --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/TaskGenerator.groovy @@ -0,0 +1,122 @@ +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.file.ConfigurableFileTree +import org.gradle.api.internal.file.FileResolver +import org.gradle.api.plugins.AppliedPlugin +import org.gradle.api.tasks.SourceSet + +class TaskGenerator { + + /** + * 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. + */ + static Task addGenerateProtoTask(Project project, String sourceSetOrVariantName, Collection sourceSets) { + def generateProtoTaskName = 'generate' + + Utils.getSourceSetSubstringForTaskNames(sourceSetOrVariantName) + 'Proto' + return project.tasks.create(generateProtoTaskName, GenerateProtoTask) { + description = "Compiles Proto source for '${sourceSetOrVariantName}'" + outputBaseDir = "${project.protobuf.generatedFilesBaseDir}/${sourceSetOrVariantName}" + sourceSets.each { sourceSet -> + // Include sources + inputs.source sourceSet.proto + ProtobufSourceDirectorySet protoSrcDirSet = sourceSet.proto + protoSrcDirSet.srcDirs.each { srcDir -> + include srcDir + } + + // Include extracted sources + ConfigurableFileTree extractedProtoSources = + project.fileTree(getExtractedProtosDir(sourceSet.name)) { + include "**/*.proto" + } + inputs.source extractedProtoSources + include extractedProtoSources.dir + + // Register extracted include protos + ConfigurableFileTree extractedIncludeProtoSources = + project.fileTree(getExtractedIncludeProtosDir(sourceSet.name)) { + include "**/*.proto" + } + // Register them as input, but not as "source". + // Inputs are checked in incremental builds, but only "source" files are compiled. + inputs.dir extractedIncludeProtoSources + // Add the extracted include dir to the --proto_path include paths. + include extractedIncludeProtoSources.dir + } + } + } + + /** + * Adds a task to extract protos from protobuf dependencies. They are + * treated as sources and will be compiled. + * + *

This task is per-sourceSet, for both Java and Android. In Android a + * variant may have multiple sourceSets, each of these sourceSets will have + * its own extraction task. + */ + static Task maybeAddExtractProtosTask(Project project, String sourceSetName) { + def extractProtosTaskName = 'extract' + + Utils.getSourceSetSubstringForTaskNames(sourceSetName) + 'Proto' + Task existingTask = project.tasks.findByName(extractProtosTaskName) + if (existingTask != null) { + return existingTask + } + return project.tasks.create(extractProtosTaskName, ProtobufExtract) { + description = "Extracts proto files/dependencies specified by 'protobuf' configuration" + destDir = getExtractedProtosDir(sourceSetName) as File + inputs.files project.configurations[Utils.getConfigName(sourceSetName, 'protobuf')] + } + } + + /** + * Adds a task to extract protos from compile dependencies of a sourceSet, + * if there isn't one. Those are needed for imports in proto files, but + * they won't be compiled since they have already been compiled in their + * own projects or artifacts. + * + *

This task is per-sourceSet, for both Java and Android. In Android a + * variant may have multiple sourceSets, each of these sourceSets will have + * its own extraction task. + */ + static Task maybeAddExtractIncludeProtosTask(Project project, String sourceSetName, def... inputFilesList) { + def extractIncludeProtosTaskName = 'extractInclude' + + Utils.getSourceSetSubstringForTaskNames(sourceSetName) + 'Proto' + Task existingTask = project.tasks.findByName(extractIncludeProtosTaskName) + if (existingTask != null) { + return existingTask + } + return project.tasks.create(extractIncludeProtosTaskName, ProtobufExtract) { + description = "Extracts proto files from compile dependencies for includes" + destDir = getExtractedIncludeProtosDir(sourceSetName) as File + inputs.files project.configurations[Utils.getConfigName(sourceSetName, 'compile')] + + // TL; DR: Make protos in 'test' sourceSet able to import protos from the 'main' sourceSet. + // Sub-configurations, e.g., 'testCompile' that extends 'compile', don't depend on the + // their super configurations. As a result, 'testCompile' doesn't depend on 'compile' and + // it cannot get the proto files from 'main' sourceSet through the configuration. However, + inputFilesList.each({ inputFiles -> inputs.files inputFiles }) + } + } + + private String getExtractedIncludeProtosDir(Project project, String sourceSetName) { + return "${project.buildDir}/extracted-include-protos/${sourceSetName}" + } + + private String getExtractedProtosDir(Project project, String sourceSetName) { + return "${project.buildDir}/extracted-protos/${sourceSetName}" + } +} \ No newline at end of file diff --git a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy index e2c430e7..2e6b9f19 100644 --- a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy @@ -32,6 +32,7 @@ package com.google.protobuf.gradle import org.apache.commons.lang.StringUtils import org.gradle.api.Project import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.SourceSetContainer class Utils { /** @@ -54,4 +55,36 @@ class Utils { static boolean isAndroidProject(Project project) { return project.hasProperty('android') && project.android.sourceSets } + + /** + * Creates a configuration if necessary for a source set so that the build + * author can configure dependencies for it. + */ + private static void createConfiguration(Project project, String sourceSetName) { + String configName = getConfigName(sourceSetName, 'protobuf') + if (project.configurations.findByName(configName) == null) { + project.configurations.create(configName) { + visible = false + transitive = false + extendsFrom = [] + } + } + } + + /** + * Adds the proto extension to all SourceSets, e.g., it creates + * sourceSets.main.proto and sourceSets.test.proto. + */ + static void addSourceSetExtensions(SourceSetContainer sourceSets, fileResolver) { + sourceSets.each { sourceSet -> + sourceSet.extensions.create('proto', ProtobufSourceDirectorySet, sourceSet.name, fileResolver) + } + } + + static void setupSourceSets(Project project, SourceSetContainer sourceSets, fileResolver) { + addSourceSetExtensions(sourceSets, fileResolver) + sourceSets.all { sourceSet -> + createConfiguration(project, sourceSet.name) + } + } } diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy new file mode 100644 index 00000000..2c69c125 --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy @@ -0,0 +1,124 @@ +package com.google.protobuf.gradle.plugins + +import com.google.common.collect.ImmutableList +import com.google.protobuf.gradle.TaskGenerator +import com.google.protobuf.gradle.Utils +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.internal.file.FileResolver +import org.gradle.api.tasks.SourceSet + +import javax.inject.Inject + +class ProtobufAndroidPlugin implements Plugin { + + private Project project + private final FileResolver fileResolver + + @Inject + public ProtobufAndroidPlugin(FileResolver fileResolver) { + this.fileResolver = fileResolver + } + + void apply(final Project project) { + this.project = project + + project.apply plugin: 'com.google.protobuf.base' + + Utils.setupSourceSets(project, project.android.sourceSets, fileResolver) + + 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() + } + } + + private void addProtoTasks() { + getNonTestVariants().each { variant -> + addTasksForVariant(variant, false) + } + project.android.testVariants.each { testVariant -> + addTasksForVariant(testVariant, true) + } + } + + private Object getNonTestVariants() { + return project.android.hasProperty('libraryVariants') ? + project.android.libraryVariants : project.android.applicationVariants + } + + /** + * 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 + def sourceSetNames = new ArrayList() + def sourceSets = new ArrayList() + 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) + } + + def generateProtoTask = TaskGenerator.addGenerateProtoTask(project, variant.name, sourceSets) + generateProtoTask.setVariant(variant, isTestVariant) + generateProtoTask.flavors = flavorListBuilder.build() + generateProtoTask.buildType = variant.buildType.name + generateProtoTask.doneInitializing() + generateProtoTask.builtins { + javanano {} + } + + sourceSetNames.each { sourceSetName -> + def extractProtosTask = TaskGenerator.maybeAddExtractProtosTask(project, sourceSetName) + generateProtoTask.dependsOn(extractProtosTask) + + def extractIncludeProtosTask + + // TODO(zhangkun83): Android sourceSet doesn't have compileClasspath. If it did, we + // haven't figured out a way to put source protos in 'resources'. For now we use an ad-hoc + // solution that manually includes the source protos of 'main' and its dependencies. + if (sourceSetName == 'androidTest') { + extractIncludeProtosTask = TaskGenerator.maybeAddExtractIncludeProtosTask(project, sourceSetName, project.android.sourceSets['main'].proto, project.configurations['compile']) + } else { + extractIncludeProtosTask = TaskGenerator.maybeAddExtractIncludeProtosTask(project, sourceSetName) + } + + generateProtoTask.dependsOn(extractIncludeProtosTask) + } + + // TODO(zhangkun83): Include source proto files in the compiled archive, + // so that proto files from dependent projects can import them. + } + + private void linkGenerateProtoTasksToJavaCompile() { + (getNonTestVariants() + project.android.testVariants).each { variant -> + project.protobuf.generateProtoTasks.ofVariant(variant.name).each { generateProtoTask -> + // This cannot be called once task execution has started + variant.registerJavaGeneratingTask(generateProtoTask, generateProtoTask.getAllOutputDirs()) + } + } + } +} \ No newline at end of file diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy new file mode 100644 index 00000000..b0d1ec25 --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy @@ -0,0 +1,27 @@ +package com.google.protobuf.gradle.plugins + +import com.google.protobuf.gradle.ProtobufConvention +import org.gradle.api.Plugin +import org.gradle.api.Project + +class ProtobufBasePlugin implements Plugin { + + void apply(final Project project) { + def gv = project.gradle.gradleVersion =~ "(\\d*)\\.(\\d*).*" + if (!gv || !gv.matches() || gv.group(1).toInteger() != 2 || gv.group(2).toInteger() < 12) { + project.logger.error("You are using Gradle ${project.gradle.gradleVersion}: " + + " This version of the protobuf plugin works with Gradle version 2.12+") + } + + // Provides the osdetector extension + project.apply plugin: 'osdetector' + + project.convention.plugins.protobuf = new ProtobufConvention(project, fileResolver); + + project.afterEvaluate { + // 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.generateProtoTasks.all()) + } + } +} \ No newline at end of file diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy new file mode 100644 index 00000000..f92e4973 --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy @@ -0,0 +1,93 @@ +package com.google.protobuf.gradle.plugins + +import com.google.protobuf.gradle.TaskGenerator +import com.google.protobuf.gradle.Utils +import javafx.concurrent.Task +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.internal.file.FileResolver +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.SourceSetContainer + +import javax.inject.Inject + +class ProtobufJavaPlugin implements Plugin { + + private Project project + private final FileResolver fileResolver + + @Inject + public ProtobufJavaPlugin(FileResolver fileResolver) { + this.fileResolver = fileResolver + } + + void apply(final Project project) { + this.project = project + + project.apply plugin: 'com.google.protobuf.base' + + Utils.setupSourceSets(project, project.sourceSets, fileResolver) + project.afterEvaluate { + 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() + } + } + + /** + * Adds Protobuf-related tasks to the project. + */ + private addProtoTasks(SourceSetContainer sourceSets) { + sourceSets.each { sourceSet -> + addTasksForSourceSet(sourceSet) + } + } + + /** + * Creates Protobuf tasks for a sourceSet in a Java project. + */ + private addTasksForSourceSet(final SourceSet sourceSet) { + def generateProtoTask = TaskGenerator.addGenerateProtoTask(project, sourceSet.name, [sourceSet]) + generateProtoTask.sourceSet = sourceSet + generateProtoTask.doneInitializing() + generateProtoTask.builtins { + java {} + } + + def extractProtosTask = TaskGenerator.maybeAddExtractProtosTask(project, sourceSet.name) + generateProtoTask.dependsOn(extractProtosTask) + + // In Java projects, the compileClasspath of the 'test' sourceSet includes all the + // 'resources' of the output of 'main', in which the source protos are placed. + // This is nicer than the ad-hoc solution that Android has, because it works for any + // extended configuration, not just 'testCompile'. + def extractIncludeProtosTask = TaskGenerator.maybeAddExtractIncludeProtosTask(project, sourceSet.name, sourceSet.compileClasspath) + generateProtoTask.dependsOn(extractIncludeProtosTask) + + // Include source proto files in the compiled archive, so that proto files from + // dependent projects can import them. + def processResourcesTask = + project.tasks.getByName(sourceSet.getTaskName('process', 'resources')) + processResourcesTask.from(generateProtoTask.inputs.sourceFiles) { + include '**/*.proto' + } + } + + private linkGenerateProtoTasksToJavaCompile() { + project.sourceSets.each { sourceSet -> + def javaCompileTask = project.tasks.getByName(sourceSet.getCompileTaskName("java")) + project.protobuf.generateProtoTasks.ofSourceSet(sourceSet.name).each { generateProtoTask -> + javaCompileTask.dependsOn(generateProtoTask) + generateProtoTask.getAllOutputDirs().each { dir -> + javaCompileTask.source project.fileTree(dir: dir) + } + } + } + } +} \ No newline at end of file diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy new file mode 100644 index 00000000..d13bf179 --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy @@ -0,0 +1,82 @@ +/* + * 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.plugins + +import org.gradle.api.Action +import org.gradle.api.GradleException +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.AppliedPlugin + +class ProtobufPlugin implements Plugin { + // any one of these plugins should be sufficient to proceed with applying this plugin + private static final List javaPluginOptions = ['java'] + private static final List androidPluginOptions = ['com.android.application', + 'com.android.library', + 'android', + 'android-library'] + + private boolean wasApplied = false; + + void apply(final 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 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 + + if (prerequisitePlugin.id in javaPluginOptions) { + project.apply plugin: 'com.google.protobuf.java' + } else { + project.apply plugin: 'com.google.protobuf.android' + } + } + } + + (javaPluginOptions + androidPluginOptions).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.') + } + } + } +} diff --git a/src/main/resources/META-INF/gradle-plugins/com.google.protobuf.android.properties b/src/main/resources/META-INF/gradle-plugins/com.google.protobuf.android.properties new file mode 100644 index 00000000..82aa56cd --- /dev/null +++ b/src/main/resources/META-INF/gradle-plugins/com.google.protobuf.android.properties @@ -0,0 +1 @@ +implementation-class=com.google.protobuf.gradle.plugins.ProtobufAndroidPlugin \ No newline at end of file diff --git a/src/main/resources/META-INF/gradle-plugins/com.google.protobuf.base.properties b/src/main/resources/META-INF/gradle-plugins/com.google.protobuf.base.properties new file mode 100644 index 00000000..3d1db40f --- /dev/null +++ b/src/main/resources/META-INF/gradle-plugins/com.google.protobuf.base.properties @@ -0,0 +1 @@ +implementation-class=com.google.protobuf.gradle.plugins.ProtobufBasePlugin diff --git a/src/main/resources/META-INF/gradle-plugins/com.google.protobuf.java.properties b/src/main/resources/META-INF/gradle-plugins/com.google.protobuf.java.properties new file mode 100644 index 00000000..8bb6513c --- /dev/null +++ b/src/main/resources/META-INF/gradle-plugins/com.google.protobuf.java.properties @@ -0,0 +1 @@ +implementation-class=com.google.protobuf.gradle.plugins.ProtobufJavaPlugin \ No newline at end of file diff --git a/src/main/resources/META-INF/gradle-plugins/com.google.protobuf.properties b/src/main/resources/META-INF/gradle-plugins/com.google.protobuf.properties index 50501673..b7b6690c 100644 --- a/src/main/resources/META-INF/gradle-plugins/com.google.protobuf.properties +++ b/src/main/resources/META-INF/gradle-plugins/com.google.protobuf.properties @@ -1 +1 @@ -implementation-class=com.google.protobuf.gradle.ProtobufPlugin +implementation-class=com.google.protobuf.gradle.plugins.ProtobufPlugin From 7c42ca50191b36a1187d8b6df2360412b82645c0 Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Sat, 28 May 2016 07:58:19 +0800 Subject: [PATCH 02/27] Fix broken signature part --- .../protobuf/gradle/ProtobufConfigurator.groovy | 3 +-- .../protobuf/gradle/ProtobufConvention.groovy | 5 ++--- .../google/protobuf/gradle/TaskGenerator.groovy | 15 +++++++-------- .../com/google/protobuf/gradle/Utils.groovy | 5 +++-- .../gradle/plugins/ProtobufAndroidPlugin.groovy | 9 +-------- .../gradle/plugins/ProtobufBasePlugin.groovy | 12 +++++++++++- .../gradle/plugins/ProtobufJavaPlugin.groovy | 13 ++----------- .../protobuf/gradle/plugins/ProtobufPlugin.groovy | 1 + 8 files changed, 28 insertions(+), 35 deletions(-) diff --git a/src/main/groovy/com/google/protobuf/gradle/ProtobufConfigurator.groovy b/src/main/groovy/com/google/protobuf/gradle/ProtobufConfigurator.groovy index bbc5049d..1cd51d38 100644 --- a/src/main/groovy/com/google/protobuf/gradle/ProtobufConfigurator.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/ProtobufConfigurator.groovy @@ -31,7 +31,6 @@ package com.google.protobuf.gradle import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project -import org.gradle.api.internal.file.FileResolver import org.gradle.api.tasks.SourceSet import org.gradle.util.ConfigureUtil @@ -50,7 +49,7 @@ public class ProtobufConfigurator { */ public String generatedFilesBaseDir - public ProtobufConfigurator(Project project, FileResolver fileResolver) { + public ProtobufConfigurator(Project project) { this.project = project if (Utils.isAndroidProject(project)) { tasks = new AndroidGenerateProtoTaskCollection() diff --git a/src/main/groovy/com/google/protobuf/gradle/ProtobufConvention.groovy b/src/main/groovy/com/google/protobuf/gradle/ProtobufConvention.groovy index 91b75bba..97194c43 100644 --- a/src/main/groovy/com/google/protobuf/gradle/ProtobufConvention.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/ProtobufConvention.groovy @@ -33,15 +33,14 @@ package com.google.protobuf.gradle import com.google.common.collect.ArrayListMultimap import com.google.common.collect.Multimap import org.gradle.api.Project -import org.gradle.api.internal.file.FileResolver import org.gradle.util.ConfigureUtil /** * Adds the protobuf {} block as a property of the project. */ class ProtobufConvention { - def ProtobufConvention(Project project, FileResolver fileResolver) { - protobuf = new ProtobufConfigurator(project, fileResolver) + def ProtobufConvention(Project project) { + protobuf = new ProtobufConfigurator(project) } def final ProtobufConfigurator protobuf diff --git a/src/main/groovy/com/google/protobuf/gradle/TaskGenerator.groovy b/src/main/groovy/com/google/protobuf/gradle/TaskGenerator.groovy index 1e95bab4..31f524ab 100644 --- a/src/main/groovy/com/google/protobuf/gradle/TaskGenerator.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/TaskGenerator.groovy @@ -8,7 +8,6 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.file.ConfigurableFileTree -import org.gradle.api.internal.file.FileResolver import org.gradle.api.plugins.AppliedPlugin import org.gradle.api.tasks.SourceSet @@ -40,7 +39,7 @@ class TaskGenerator { // Include extracted sources ConfigurableFileTree extractedProtoSources = - project.fileTree(getExtractedProtosDir(sourceSet.name)) { + project.fileTree(getExtractedProtosDir(project, sourceSet.name)) { include "**/*.proto" } inputs.source extractedProtoSources @@ -48,7 +47,7 @@ class TaskGenerator { // Register extracted include protos ConfigurableFileTree extractedIncludeProtoSources = - project.fileTree(getExtractedIncludeProtosDir(sourceSet.name)) { + project.fileTree(getExtractedIncludeProtosDir(project, sourceSet.name)) { include "**/*.proto" } // Register them as input, but not as "source". @@ -77,7 +76,7 @@ class TaskGenerator { } return project.tasks.create(extractProtosTaskName, ProtobufExtract) { description = "Extracts proto files/dependencies specified by 'protobuf' configuration" - destDir = getExtractedProtosDir(sourceSetName) as File + destDir = getExtractedProtosDir(project, sourceSetName) as File inputs.files project.configurations[Utils.getConfigName(sourceSetName, 'protobuf')] } } @@ -92,7 +91,7 @@ class TaskGenerator { * variant may have multiple sourceSets, each of these sourceSets will have * its own extraction task. */ - static Task maybeAddExtractIncludeProtosTask(Project project, String sourceSetName, def... inputFilesList) { + static Task maybeAddExtractIncludeProtosTask(Project project, String sourceSetName, Object... inputFilesList) { def extractIncludeProtosTaskName = 'extractInclude' + Utils.getSourceSetSubstringForTaskNames(sourceSetName) + 'Proto' Task existingTask = project.tasks.findByName(extractIncludeProtosTaskName) @@ -101,7 +100,7 @@ class TaskGenerator { } return project.tasks.create(extractIncludeProtosTaskName, ProtobufExtract) { description = "Extracts proto files from compile dependencies for includes" - destDir = getExtractedIncludeProtosDir(sourceSetName) as File + destDir = getExtractedIncludeProtosDir(project, sourceSetName) as File inputs.files project.configurations[Utils.getConfigName(sourceSetName, 'compile')] // TL; DR: Make protos in 'test' sourceSet able to import protos from the 'main' sourceSet. @@ -112,11 +111,11 @@ class TaskGenerator { } } - private String getExtractedIncludeProtosDir(Project project, String sourceSetName) { + private static String getExtractedIncludeProtosDir(Project project, String sourceSetName) { return "${project.buildDir}/extracted-include-protos/${sourceSetName}" } - private String getExtractedProtosDir(Project project, String sourceSetName) { + private static String getExtractedProtosDir(Project project, String sourceSetName) { return "${project.buildDir}/extracted-protos/${sourceSetName}" } } \ No newline at end of file diff --git a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy index 2e6b9f19..629aa32b 100644 --- a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy @@ -33,6 +33,7 @@ import org.apache.commons.lang.StringUtils import org.gradle.api.Project import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.internal.file.FileResolver class Utils { /** @@ -75,13 +76,13 @@ class Utils { * Adds the proto extension to all SourceSets, e.g., it creates * sourceSets.main.proto and sourceSets.test.proto. */ - static void addSourceSetExtensions(SourceSetContainer sourceSets, fileResolver) { + static void addSourceSetExtensions(Object sourceSets, FileResolver fileResolver) { sourceSets.each { sourceSet -> sourceSet.extensions.create('proto', ProtobufSourceDirectorySet, sourceSet.name, fileResolver) } } - static void setupSourceSets(Project project, SourceSetContainer sourceSets, fileResolver) { + static void setupSourceSets(Project project, Object sourceSets, FileResolver fileResolver) { addSourceSetExtensions(sourceSets, fileResolver) sourceSets.all { sourceSet -> createConfiguration(project, sourceSet.name) diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy index 2c69c125..bd2e7f96 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy @@ -5,7 +5,6 @@ import com.google.protobuf.gradle.TaskGenerator import com.google.protobuf.gradle.Utils import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.internal.file.FileResolver import org.gradle.api.tasks.SourceSet import javax.inject.Inject @@ -13,19 +12,13 @@ import javax.inject.Inject class ProtobufAndroidPlugin implements Plugin { private Project project - private final FileResolver fileResolver - - @Inject - public ProtobufAndroidPlugin(FileResolver fileResolver) { - this.fileResolver = fileResolver - } void apply(final Project project) { this.project = project project.apply plugin: 'com.google.protobuf.base' - Utils.setupSourceSets(project, project.android.sourceSets, fileResolver) + Utils.setupSourceSets(project, project.android.sourceSets, project.plugins['com.google.protobuf.base'].fileResolver) project.afterEvaluate { // The Android variants are only available at this point. diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy index b0d1ec25..e66373ba 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy @@ -3,9 +3,19 @@ package com.google.protobuf.gradle.plugins import com.google.protobuf.gradle.ProtobufConvention import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.internal.file.FileResolver + +import javax.inject.Inject class ProtobufBasePlugin implements Plugin { + final FileResolver fileResolver + + @Inject + public ProtobufBasePlugin(FileResolver fileResolver) { + this.fileResolver = fileResolver + } + void apply(final Project project) { def gv = project.gradle.gradleVersion =~ "(\\d*)\\.(\\d*).*" if (!gv || !gv.matches() || gv.group(1).toInteger() != 2 || gv.group(2).toInteger() < 12) { @@ -16,7 +26,7 @@ class ProtobufBasePlugin implements Plugin { // Provides the osdetector extension project.apply plugin: 'osdetector' - project.convention.plugins.protobuf = new ProtobufConvention(project, fileResolver); + project.convention.plugins.protobuf = new ProtobufConvention(project); project.afterEvaluate { // protoc and codegen plugin configuration may change through the protobuf{} diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy index f92e4973..d330089a 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy @@ -5,30 +5,21 @@ import com.google.protobuf.gradle.Utils import javafx.concurrent.Task import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.internal.file.FileResolver import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.SourceSetContainer -import javax.inject.Inject - class ProtobufJavaPlugin implements Plugin { private Project project - private final FileResolver fileResolver - - @Inject - public ProtobufJavaPlugin(FileResolver fileResolver) { - this.fileResolver = fileResolver - } void apply(final Project project) { this.project = project project.apply plugin: 'com.google.protobuf.base' - Utils.setupSourceSets(project, project.sourceSets, fileResolver) + Utils.setupSourceSets(project, project.sourceSets, project.plugins['com.google.protobuf.base'].fileResolver) project.afterEvaluate { - addProtoTasks() + addProtoTasks(project.sourceSets) project.protobuf.runTaskConfigClosures() // Disallow user configuration outside the config closures, because // next in linkGenerateProtoTasksToJavaCompile() we add generated, diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy index d13bf179..9f86f2d7 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy @@ -53,6 +53,7 @@ class ProtobufPlugin implements Plugin { // 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 applyWithPrerequisitePlugin = { prerequisitePlugin -> + println(prerequisitePlugin.id) 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) From 84ab62a08e12c75c79e4c5c3eba6dd4d37a7222c Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Sat, 28 May 2016 09:31:38 +0800 Subject: [PATCH 03/27] Fix Java build --- .../plugins/ProtobufAndroidPlugin.groovy | 25 +++++++--------- .../gradle/plugins/ProtobufBasePlugin.groovy | 29 +++++++++++++++++-- .../gradle/plugins/ProtobufJavaPlugin.groovy | 22 ++++++-------- 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy index bd2e7f96..6cf6b5cd 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy @@ -19,22 +19,12 @@ class ProtobufAndroidPlugin implements Plugin { project.apply plugin: 'com.google.protobuf.base' Utils.setupSourceSets(project, project.android.sourceSets, project.plugins['com.google.protobuf.base'].fileResolver) - - 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() - } } - private void addProtoTasks() { + /** + * Adds Protobuf-related tasks to the project. + */ + void addProtoTasks() { getNonTestVariants().each { variant -> addTasksForVariant(variant, false) } @@ -43,6 +33,13 @@ class ProtobufAndroidPlugin implements Plugin { } } + /** + * Performs after task are added and configured + */ + void afterTaskAdded() { + linkGenerateProtoTasksToJavaCompile() + } + private Object getNonTestVariants() { return project.android.hasProperty('libraryVariants') ? project.android.libraryVariants : project.android.applicationVariants diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy index e66373ba..caf5f221 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy @@ -11,6 +11,10 @@ class ProtobufBasePlugin implements Plugin { final FileResolver fileResolver + private static final List protobufPlugins = [ + 'com.google.protobuf.java', + 'com.google.protobuf.android'] + @Inject public ProtobufBasePlugin(FileResolver fileResolver) { this.fileResolver = fileResolver @@ -29,9 +33,28 @@ class ProtobufBasePlugin implements Plugin { project.convention.plugins.protobuf = new ProtobufConvention(project); project.afterEvaluate { - // 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.generateProtoTasks.all()) + protobufPlugins.each { pluginName -> + project.pluginManager.withPlugin(pluginName, { plugin -> + def appliedPlugin = project.plugins[plugin.id] + + // The Android variants are only available at this point. + appliedPlugin.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() + + appliedPlugin.afterTaskAdded() + + // 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.generateProtoTasks.all()) + }) + } } } } \ No newline at end of file diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy index d330089a..604f74f2 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy @@ -18,28 +18,24 @@ class ProtobufJavaPlugin implements Plugin { project.apply plugin: 'com.google.protobuf.base' Utils.setupSourceSets(project, project.sourceSets, project.plugins['com.google.protobuf.base'].fileResolver) - project.afterEvaluate { - addProtoTasks(project.sourceSets) - 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() - } } /** * Adds Protobuf-related tasks to the project. */ - private addProtoTasks(SourceSetContainer sourceSets) { - sourceSets.each { sourceSet -> + void addProtoTasks() { + project.sourceSets.each { sourceSet -> addTasksForSourceSet(sourceSet) } } + /** + * Performs after task are added and configured + */ + void afterTaskAdded() { + linkGenerateProtoTasksToJavaCompile() + } + /** * Creates Protobuf tasks for a sourceSet in a Java project. */ From f67d16628a6179a2ddd4a827c10e1a797eacaa2a Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Sat, 28 May 2016 17:12:39 +0800 Subject: [PATCH 04/27] Fix sourceSets issues --- .../protobuf/gradle/ProtobufConfigurator.groovy | 6 +++++- .../google/protobuf/gradle/ProtobufConvention.groovy | 5 +++-- .../groovy/com/google/protobuf/gradle/Utils.groovy | 10 +++++----- .../gradle/plugins/ProtobufAndroidPlugin.groovy | 2 +- .../gradle/plugins/ProtobufBasePlugin.groovy | 12 ++++++------ .../gradle/plugins/ProtobufJavaPlugin.groovy | 3 ++- .../protobuf/gradle/plugins/ProtobufPlugin.groovy | 1 - testProjectCustomProtoDir/build.gradle | 1 - 8 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/main/groovy/com/google/protobuf/gradle/ProtobufConfigurator.groovy b/src/main/groovy/com/google/protobuf/gradle/ProtobufConfigurator.groovy index 1cd51d38..adbc5651 100644 --- a/src/main/groovy/com/google/protobuf/gradle/ProtobufConfigurator.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/ProtobufConfigurator.groovy @@ -32,6 +32,7 @@ package com.google.protobuf.gradle import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project import org.gradle.api.tasks.SourceSet +import org.gradle.api.internal.file.FileResolver import org.gradle.util.ConfigureUtil /** @@ -43,14 +44,17 @@ public class ProtobufConfigurator { private final ToolsLocator tools private final ArrayList taskConfigClosures + final FileResolver fileResolver + /** * The base directory of generated files. The default is * "${project.buildDir}/generated/source/proto". */ public String generatedFilesBaseDir - public ProtobufConfigurator(Project project) { + public ProtobufConfigurator(Project project, FileResolver fileResolver) { this.project = project + this.fileResolver = fileResolver if (Utils.isAndroidProject(project)) { tasks = new AndroidGenerateProtoTaskCollection() } else { diff --git a/src/main/groovy/com/google/protobuf/gradle/ProtobufConvention.groovy b/src/main/groovy/com/google/protobuf/gradle/ProtobufConvention.groovy index 97194c43..91b75bba 100644 --- a/src/main/groovy/com/google/protobuf/gradle/ProtobufConvention.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/ProtobufConvention.groovy @@ -33,14 +33,15 @@ package com.google.protobuf.gradle import com.google.common.collect.ArrayListMultimap import com.google.common.collect.Multimap import org.gradle.api.Project +import org.gradle.api.internal.file.FileResolver import org.gradle.util.ConfigureUtil /** * Adds the protobuf {} block as a property of the project. */ class ProtobufConvention { - def ProtobufConvention(Project project) { - protobuf = new ProtobufConfigurator(project) + def ProtobufConvention(Project project, FileResolver fileResolver) { + protobuf = new ProtobufConfigurator(project, fileResolver) } def final ProtobufConfigurator protobuf diff --git a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy index 629aa32b..4a779714 100644 --- a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy @@ -77,15 +77,15 @@ class Utils { * sourceSets.main.proto and sourceSets.test.proto. */ static void addSourceSetExtensions(Object sourceSets, FileResolver fileResolver) { - sourceSets.each { sourceSet -> + sourceSets.all { sourceSet -> sourceSet.extensions.create('proto', ProtobufSourceDirectorySet, sourceSet.name, fileResolver) } } static void setupSourceSets(Project project, Object sourceSets, FileResolver fileResolver) { - addSourceSetExtensions(sourceSets, fileResolver) - sourceSets.all { sourceSet -> - createConfiguration(project, sourceSet.name) - } + addSourceSetExtensions(sourceSets, fileResolver) + sourceSets.all { sourceSet -> + createConfiguration(project, sourceSet.name) + } } } diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy index 6cf6b5cd..58be351a 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy @@ -18,7 +18,7 @@ class ProtobufAndroidPlugin implements Plugin { project.apply plugin: 'com.google.protobuf.base' - Utils.setupSourceSets(project, project.android.sourceSets, project.plugins['com.google.protobuf.base'].fileResolver) + Utils.setupSourceSets(project, project.android.sourceSets, project.protobuf.fileResolver) } /** diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy index caf5f221..96c48191 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy @@ -30,11 +30,11 @@ class ProtobufBasePlugin implements Plugin { // Provides the osdetector extension project.apply plugin: 'osdetector' - project.convention.plugins.protobuf = new ProtobufConvention(project); + project.convention.plugins.protobuf = new ProtobufConvention(project, fileResolver); - project.afterEvaluate { - protobufPlugins.each { pluginName -> - project.pluginManager.withPlugin(pluginName, { plugin -> + protobufPlugins.each { pluginName -> + project.pluginManager.withPlugin(pluginName, { plugin -> + project.afterEvaluate { def appliedPlugin = project.plugins[plugin.id] // The Android variants are only available at this point. @@ -53,8 +53,8 @@ class ProtobufBasePlugin implements Plugin { // 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.generateProtoTasks.all()) - }) - } + } + }) } } } \ No newline at end of file diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy index 604f74f2..6221586d 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy @@ -1,5 +1,6 @@ package com.google.protobuf.gradle.plugins +import com.google.protobuf.gradle.ProtobufConvention import com.google.protobuf.gradle.TaskGenerator import com.google.protobuf.gradle.Utils import javafx.concurrent.Task @@ -17,7 +18,7 @@ class ProtobufJavaPlugin implements Plugin { project.apply plugin: 'com.google.protobuf.base' - Utils.setupSourceSets(project, project.sourceSets, project.plugins['com.google.protobuf.base'].fileResolver) + Utils.setupSourceSets(project, project.sourceSets, project.protobuf.fileResolver) } /** diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy index 9f86f2d7..d13bf179 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy @@ -53,7 +53,6 @@ class ProtobufPlugin implements Plugin { // 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 applyWithPrerequisitePlugin = { prerequisitePlugin -> - println(prerequisitePlugin.id) 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) diff --git a/testProjectCustomProtoDir/build.gradle b/testProjectCustomProtoDir/build.gradle index 2db14061..606831a8 100644 --- a/testProjectCustomProtoDir/build.gradle +++ b/testProjectCustomProtoDir/build.gradle @@ -39,7 +39,6 @@ task printDeps(dependsOn: build) << { sourceSets.each { println it.getCompileTaskName("proto") } sourceSets.each { println it.getCompileTaskName("java") } sourceSets.each { println it } - println tasks['generateProto'].source println tasks['compileJava'].source println project.buildDir println project.buildDir.path From f5740c63d4f9a4a7f862be2a1a2de11a1ac3d5a7 Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Sat, 28 May 2016 17:39:08 +0800 Subject: [PATCH 05/27] Allow applying multiple protobuf plugins --- .../gradle/ProtobufConfigurator.groovy | 2 +- .../gradle/plugins/ProtobufPlugin.groovy | 44 ++++++++++--------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/main/groovy/com/google/protobuf/gradle/ProtobufConfigurator.groovy b/src/main/groovy/com/google/protobuf/gradle/ProtobufConfigurator.groovy index adbc5651..0145bcbe 100644 --- a/src/main/groovy/com/google/protobuf/gradle/ProtobufConfigurator.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/ProtobufConfigurator.groovy @@ -31,8 +31,8 @@ package com.google.protobuf.gradle import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project -import org.gradle.api.tasks.SourceSet import org.gradle.api.internal.file.FileResolver +import org.gradle.api.tasks.SourceSet import org.gradle.util.ConfigureUtil /** diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy index d13bf179..72c3c1a3 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy @@ -44,39 +44,43 @@ class ProtobufPlugin implements Plugin { 'android', 'android-library'] - private boolean wasApplied = false; + private List pluginsApplied = []; + private Project project; 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 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 + applyWithPrerequisitePlugin(javaPluginOptions, 'com.google.protobuf.java') + applyWithPrerequisitePlugin(androidPluginOptions, 'com.google.protobuf.android') - if (prerequisitePlugin.id in javaPluginOptions) { - project.apply plugin: 'com.google.protobuf.java' - } else { - project.apply plugin: 'com.google.protobuf.android' - } + project.afterEvaluate { + if (pluginsApplied.empty) { + 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.') } } + } - (javaPluginOptions + androidPluginOptions).each { pluginName -> - project.pluginManager.withPlugin(pluginName, applyWithPrerequisitePlugin) + void applyWithPrerequisitePlugin(List possiblePluginNames, String pluginToBeApplied) { + possiblePluginNames.each { pluginName -> + project.pluginManager.withPlugin(pluginName, { prerequisitePlugin -> + applyWithPrerequisitePlugin (prerequisitePlugin, pluginToBeApplied) + }) } + } - 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.') - } + void applyWithPrerequisitePlugin(AppliedPlugin prerequisitePlugin, String pluginToBeApplied) { + if (pluginToBeApplied in pluginsApplied) { + 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 { + pluginsApplied.add pluginToBeApplied + project.apply plugin: pluginToBeApplied } } } From 5638ef00b25614892b70b24d8aee74873927f154 Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Sat, 28 May 2016 17:43:15 +0800 Subject: [PATCH 06/27] Remove unused import --- src/main/groovy/com/google/protobuf/gradle/Utils.groovy | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy index 4a779714..33bf66c1 100644 --- a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy @@ -32,7 +32,6 @@ package com.google.protobuf.gradle import org.apache.commons.lang.StringUtils import org.gradle.api.Project import org.gradle.api.tasks.SourceSet -import org.gradle.api.tasks.SourceSetContainer import org.gradle.api.internal.file.FileResolver class Utils { From ac787cec1d4b3fa3c304817c2589e78622df9686 Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Sun, 29 May 2016 09:59:27 +0800 Subject: [PATCH 07/27] Apply project.afterEvaluate on base instead of per plugin --- .../gradle/plugins/ProtobufBasePlugin.groovy | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy index 96c48191..b3f62dba 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy @@ -13,7 +13,8 @@ class ProtobufBasePlugin implements Plugin { private static final List protobufPlugins = [ 'com.google.protobuf.java', - 'com.google.protobuf.android'] + 'com.google.protobuf.android', + 'com.google.protobuf.csharp'] @Inject public ProtobufBasePlugin(FileResolver fileResolver) { @@ -32,29 +33,27 @@ class ProtobufBasePlugin implements Plugin { project.convention.plugins.protobuf = new ProtobufConvention(project, fileResolver); - protobufPlugins.each { pluginName -> - project.pluginManager.withPlugin(pluginName, { plugin -> - project.afterEvaluate { - def appliedPlugin = project.plugins[plugin.id] - - // The Android variants are only available at this point. - appliedPlugin.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() - - appliedPlugin.afterTaskAdded() - - // 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.generateProtoTasks.all()) - } - }) + project.afterEvaluate { + def appliedPlugins = protobufPlugins + .findAll { project.plugins.hasPlugin(it) } + .collect { project.plugins.getPlugin(it) } + + // The Android variants are only available at this point. + appliedPlugins.each { it.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() + + appliedPlugins.each { it.afterTaskAdded() } + + // 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.generateProtoTasks.all()) } } } \ No newline at end of file From 9b5bba043a1dafa810d5b8d9e8b9d5656a6e5932 Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Sun, 29 May 2016 11:24:11 +0800 Subject: [PATCH 08/27] Add basic unit test for ProtobufJavaPlugin --- .../plugins/ProtobufJavaPluginTest.groovy | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy diff --git a/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy b/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy new file mode 100644 index 00000000..c9b74e29 --- /dev/null +++ b/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy @@ -0,0 +1,45 @@ +package com.google.protobuf.gradle.plugins + +import com.google.protobuf.gradle.GenerateProtoTask +import com.google.protobuf.gradle.ProtobufExtract +import org.gradle.api.Project +import org.gradle.testfixtures.ProjectBuilder +import org.junit.Before +import org.junit.Test + +class ProtobufJavaPluginTest { + + private Project project + + @Before + public void setup() { + project = ProjectBuilder.builder().build() + project.apply plugin: 'java' + project.apply plugin: 'com.google.protobuf' + } + + @Test + public void generateProtoAddedToProject() { + project.evaluate() + + assert project.tasks.generateProto instanceof GenerateProtoTask + assert project.tasks.generateTestProto instanceof GenerateProtoTask + + assert project.tasks.extractIncludeProto instanceof ProtobufExtract + assert project.tasks.extractIncludeTestProto instanceof ProtobufExtract + assert project.tasks.extractProto instanceof ProtobufExtract + assert project.tasks.extractTestProto instanceof ProtobufExtract + } + + @Test + public void generateProtoForSourceSetAddedToProject() { + project.sourceSets.create('nano') + + project.evaluate() + + assert project.tasks.generateNanoProto instanceof GenerateProtoTask + + assert project.tasks.extractIncludeNanoProto instanceof ProtobufExtract + assert project.tasks.extractNanoProto instanceof ProtobufExtract + } +} \ No newline at end of file From 2ef8b3b34db1e2273d4f0d2d7f9885456251260d Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Sun, 29 May 2016 11:28:06 +0800 Subject: [PATCH 09/27] Remove csharp change in the commit --- .../google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy index b3f62dba..f77fee3b 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy @@ -13,8 +13,7 @@ class ProtobufBasePlugin implements Plugin { private static final List protobufPlugins = [ 'com.google.protobuf.java', - 'com.google.protobuf.android', - 'com.google.protobuf.csharp'] + 'com.google.protobuf.android'] @Inject public ProtobufBasePlugin(FileResolver fileResolver) { From 9d7b9be309a6902eb5763890fabff7734d65016d Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Sun, 29 May 2016 15:57:52 +0800 Subject: [PATCH 10/27] Rewrite java unit test on spock --- build.gradle | 22 +++- settings.gradle | 6 - .../plugins/ProtobufJavaPluginTest.groovy | 106 ++++++++++++++++-- .../plugins/ProtobufPluginTestHelper.groovy | 39 +++++++ testProject/build.gradle | 39 +------ 5 files changed, 155 insertions(+), 57 deletions(-) create mode 100644 src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy diff --git a/build.gradle b/build.gradle index 23918476..d2aa0ec5 100644 --- a/build.gradle +++ b/build.gradle @@ -62,12 +62,30 @@ repositories { mavenCentral() } +// Write the plugin's classpath to a file to share with the tests +task createClasspathManifest { + def outputDir = file("$buildDir/$name") + + inputs.files sourceSets.main.runtimeClasspath + outputs.dir outputDir + + doLast { + outputDir.mkdirs() + file("$outputDir/plugin-classpath.txt").text = sourceSets.main.runtimeClasspath.join("\n") + } +} + dependencies { compile gradleApi() - testCompile group: 'junit', name: 'junit', version: '4.8.1' - compile localGroovy() + compile 'org.codehaus.groovy:groovy-all:2.4.4' compile 'com.google.gradle:osdetector-gradle-plugin:1.2.1' compile 'commons-lang:commons-lang:2.6' + testCompile gradleTestKit() + testCompile 'org.spockframework:spock-core:1.0-groovy-2.4' + testCompile 'commons-io:commons-io:2.5' + + // Add the classpath file to the test runtime classpath + testRuntime files(createClasspathManifest) } task wrapper(type: Wrapper) { diff --git a/settings.gradle b/settings.gradle index 021b1bab..1ce4846c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,12 +14,6 @@ if (gradle.startParameter.taskNames.intersect(['install', 'uploadArchives', 'pub logger.warn('To test the current code, make sure you have run "./gradlew install" first.') logger.warn('***************************************************************************') - include ':testProject' - project(':testProject').projectDir = "$rootDir/testProject" as File - include ':testProjectCustomProtoDir' - project(':testProjectCustomProtoDir').projectDir = "$rootDir/testProjectCustomProtoDir" as File - include ':testProjectDependent' - project(':testProjectDependent').projectDir = "$rootDir/testProjectDependent" as File include ':testProjectAndroid' project(':testProjectAndroid').projectDir = "$rootDir/testProjectAndroid" as File } diff --git a/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy b/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy index c9b74e29..b1d2efb1 100644 --- a/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy +++ b/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy @@ -4,24 +4,36 @@ import com.google.protobuf.gradle.GenerateProtoTask import com.google.protobuf.gradle.ProtobufExtract import org.gradle.api.Project import org.gradle.testfixtures.ProjectBuilder -import org.junit.Before -import org.junit.Test +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import org.apache.commons.io.FileUtils -class ProtobufJavaPluginTest { +import spock.lang.Specification - private Project project +class ProtobufJavaPluginTest extends Specification { - @Before - public void setup() { - project = ProjectBuilder.builder().build() + @Rule + final TemporaryFolder tempDir = new TemporaryFolder() + + final ProtobufPluginTestHelper helper = new ProtobufPluginTestHelper() + + private Project setupBasicProject() { + Project project = ProjectBuilder.builder().build() project.apply plugin: 'java' project.apply plugin: 'com.google.protobuf' + return project } - @Test - public void generateProtoAddedToProject() { + def "Applying java and com.google.protobuf adds corresponding task to project"() { + given: "a basic project with java and com.google.protobuf" + def project = setupBasicProject() + + when: "project evaluated" project.evaluate() + then: "generate tasks added" assert project.tasks.generateProto instanceof GenerateProtoTask assert project.tasks.generateTestProto instanceof GenerateProtoTask @@ -31,15 +43,87 @@ class ProtobufJavaPluginTest { assert project.tasks.extractTestProto instanceof ProtobufExtract } - @Test - public void generateProtoForSourceSetAddedToProject() { + def "Custom sourceSet should get its own GenerateProtoTask"() { + given: "a basic project with java and com.google.protobuf" + def project = setupBasicProject() + + when: "adding custom sourceSet nano" project.sourceSets.create('nano') + and: "project evaluated" project.evaluate() + then: "tasks for nano added" assert project.tasks.generateNanoProto instanceof GenerateProtoTask assert project.tasks.extractIncludeNanoProto instanceof ProtobufExtract assert project.tasks.extractNanoProto instanceof ProtobufExtract } + + def "testProject should be successfully executed"() { + given: "project from testProject" + def projectDir = tempDir.newFolder() + helper.copyTestProject('testProject', projectDir) + + when: "build is invoked" + def result = GradleRunner.create() + .withProjectDir(projectDir) + .withArguments('build') + .build() + + then: "it succeed" + result.task(":build").outcome == TaskOutcome.SUCCESS + ['grpc', 'grpc_nano', 'main', 'nano', 'test'].each { + def generatedSrcDir = new File(projectDir.path, "build/generated/source/proto/$it") + def fileList = [] + generatedSrcDir.eachFileRecurse { file -> + if (file.path.endsWith('.java')) { + fileList.add (file) + } + } + assert fileList.size > 0 + } + } + + def "testProjectDependent should be successfully executed"() { + given: "project from testProject & testProjectDependent" + def mainProjectDir = tempDir.newFolder() + def settingsFile = new File(mainProjectDir, 'settings.gradle') + settingsFile.createNewFile() + + ['testProject', 'testProjectDependent'].each { + helper.copyTestProject(it, new File(mainProjectDir.path, it)) + settingsFile << """ + include ':$it' + project(':$it').projectDir = "\$rootDir/testProject" as File + """ + } + + def buildFile = new File(mainProjectDir, 'build.gradle') + buildFile.createNewFile() + + when: "build is invoked" + def result = GradleRunner.create() + .withProjectDir(mainProjectDir) + .withArguments('testProjectDependent:build') + .build() + + then: "it succeed" + result.task(":testProjectDependent:build").outcome == TaskOutcome.SUCCESS + } + + def "testProjectCustomProtoDir should be successfully executed"() { + given: "project from testProjectCustomProtoDir" + def projectDir = tempDir.newFolder() + helper.copyTestProject('testProjectCustomProtoDir', projectDir) + + when: "build is invoked" + def result = GradleRunner.create() + .withProjectDir(projectDir) + .withArguments('build') + .build() + + then: "it succeed" + result.task(":build").outcome == TaskOutcome.SUCCESS + } } \ No newline at end of file diff --git a/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy b/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy new file mode 100644 index 00000000..47a65913 --- /dev/null +++ b/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy @@ -0,0 +1,39 @@ +import org.apache.commons.io.FileUtils + +class ProtobufPluginTestHelper { + + void appendPluginClasspath(File buildFile) { + def pluginClasspathResource = getClass().classLoader.findResource("plugin-classpath.txt") + if (pluginClasspathResource == null) { + throw new IllegalStateException("Did not find plugin classpath resource, run `testClasses` build task.") + } + + def pluginClasspath = pluginClasspathResource.readLines() + .collect { it.replace('\\', '\\\\') } // escape backslashes in Windows paths + .collect { "'$it'" } + .join(", ") + + // Add the logic under test to the test build + buildFile << """ + buildscript { + dependencies { + classpath files($pluginClasspath) + } + } + + repositories { + mavenCentral() + jcenter() + } + """ + } + + void copyTestProject(String testProjectName, File projectDir) { + def baseDir = new File(System.getProperty("user.dir"), "testProject") + + FileUtils.copyDirectory(baseDir, projectDir) + + def buildFile = new File(projectDir.path, "build.gradle") + appendPluginClasspath(buildFile) + } +} \ No newline at end of file diff --git a/testProject/build.gradle b/testProject/build.gradle index f4d3f5ab..e608ca46 100644 --- a/testProject/build.gradle +++ b/testProject/build.gradle @@ -93,41 +93,4 @@ jar { from sourceSet.output dependsOn sourceSet.getCompileTaskName('java') } -} - -def assertJavaCompileHasProtoGeneratedDir(String sourceSet, Collection codegenPlugins) { - def compileJavaTask = tasks.getByName(sourceSets.getByName(sourceSet).getCompileTaskName("java")) - assertJavaCompileHasProtoGeneratedDir(project, sourceSet, compileJavaTask, codegenPlugins) -} - -def assertFileExists(boolean exists, String path) { - if (exists) { - org.junit.Assert.assertTrue("\"${path}\" exists", (path as File).exists()); - } else { - org.junit.Assert.assertFalse("\"${path}\" doesn't exist", (path as File).exists()); - } -} - -test.doLast { - org.junit.Assert.assertEquals( - ['generateProto', 'generateNanoProto', 'generateGrpcProto', - 'generateGrpc_nanoProto', 'generateTestProto'] as Set, - protobuf.generateProtoTasks.all().collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateProto'] as Set, - protobuf.generateProtoTasks.ofSourceSet('main').collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateNanoProto'] as Set, - protobuf.generateProtoTasks.ofSourceSet('nano').collect({ it.name }) as Set) - assertJavaCompileHasProtoGeneratedDir('main', ['java']) - assertJavaCompileHasProtoGeneratedDir('test', ['java']) - assertJavaCompileHasProtoGeneratedDir('nano', ['javanano']) - assertJavaCompileHasProtoGeneratedDir('grpc', ['java', 'grpc']) - assertJavaCompileHasProtoGeneratedDir('grpc_nano', ['javanano', 'grpcjavanano']) - - // Check generateDescriptorSet option has been honored - ['main', 'test', 'nano', 'grpc'].each { sourceSet -> - assertFileExists(false, "$buildDir/generated/source/proto/$sourceSet/descriptor_set.desc") - } - assertFileExists(true, "$buildDir/generated/source/proto/grpc_nano/descriptor_set.desc") -} +} \ No newline at end of file From c296131999bde6d9fed507cda631d91c122581e6 Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Sun, 29 May 2016 16:49:19 +0800 Subject: [PATCH 11/27] make ProtobufPluginTestHelper function to static --- .../com/google/plugins/ProtobufJavaPluginTest.groovy | 8 +++----- .../com/google/plugins/ProtobufPluginTestHelper.groovy | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy b/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy index b1d2efb1..d42a13e3 100644 --- a/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy +++ b/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy @@ -17,8 +17,6 @@ class ProtobufJavaPluginTest extends Specification { @Rule final TemporaryFolder tempDir = new TemporaryFolder() - final ProtobufPluginTestHelper helper = new ProtobufPluginTestHelper() - private Project setupBasicProject() { Project project = ProjectBuilder.builder().build() project.apply plugin: 'java' @@ -63,7 +61,7 @@ class ProtobufJavaPluginTest extends Specification { def "testProject should be successfully executed"() { given: "project from testProject" def projectDir = tempDir.newFolder() - helper.copyTestProject('testProject', projectDir) + ProtobufPluginTestHelper.copyTestProject('testProject', projectDir) when: "build is invoked" def result = GradleRunner.create() @@ -92,7 +90,7 @@ class ProtobufJavaPluginTest extends Specification { settingsFile.createNewFile() ['testProject', 'testProjectDependent'].each { - helper.copyTestProject(it, new File(mainProjectDir.path, it)) + ProtobufPluginTestHelper.copyTestProject(it, new File(mainProjectDir.path, it)) settingsFile << """ include ':$it' project(':$it').projectDir = "\$rootDir/testProject" as File @@ -115,7 +113,7 @@ class ProtobufJavaPluginTest extends Specification { def "testProjectCustomProtoDir should be successfully executed"() { given: "project from testProjectCustomProtoDir" def projectDir = tempDir.newFolder() - helper.copyTestProject('testProjectCustomProtoDir', projectDir) + ProtobufPluginTestHelper.copyTestProject('testProjectCustomProtoDir', projectDir) when: "build is invoked" def result = GradleRunner.create() diff --git a/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy b/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy index 47a65913..d15b065d 100644 --- a/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy +++ b/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy @@ -2,8 +2,8 @@ import org.apache.commons.io.FileUtils class ProtobufPluginTestHelper { - void appendPluginClasspath(File buildFile) { - def pluginClasspathResource = getClass().classLoader.findResource("plugin-classpath.txt") + static void appendPluginClasspath(File buildFile) { + def pluginClasspathResource = ProtobufPluginTestHelper.class.classLoader.findResource("plugin-classpath.txt") if (pluginClasspathResource == null) { throw new IllegalStateException("Did not find plugin classpath resource, run `testClasses` build task.") } @@ -28,7 +28,7 @@ class ProtobufPluginTestHelper { """ } - void copyTestProject(String testProjectName, File projectDir) { + static void copyTestProject(String testProjectName, File projectDir) { def baseDir = new File(System.getProperty("user.dir"), "testProject") FileUtils.copyDirectory(baseDir, projectDir) From 7479d13e937a7d952fb23b35c052622eda151ec3 Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Sun, 29 May 2016 22:15:32 +0800 Subject: [PATCH 12/27] Refactor on fileResolver and support suffix in configuration --- .../gradle/ProtobufConfigurator.groovy | 10 +++---- .../protobuf/gradle/ProtobufConvention.groovy | 5 ++-- .../protobuf/gradle/TaskGenerator.groovy | 18 ++++--------- .../com/google/protobuf/gradle/Utils.groovy | 26 +++++++++---------- .../plugins/ProtobufAndroidPlugin.groovy | 2 +- .../gradle/plugins/ProtobufBasePlugin.groovy | 15 +++-------- .../gradle/plugins/ProtobufJavaPlugin.groovy | 2 +- .../plugins/ProtobufJavaPluginTest.groovy | 2 -- .../plugins/ProtobufPluginTestHelper.groovy | 2 +- 9 files changed, 28 insertions(+), 54 deletions(-) diff --git a/src/main/groovy/com/google/protobuf/gradle/ProtobufConfigurator.groovy b/src/main/groovy/com/google/protobuf/gradle/ProtobufConfigurator.groovy index 0145bcbe..e69605c2 100644 --- a/src/main/groovy/com/google/protobuf/gradle/ProtobufConfigurator.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/ProtobufConfigurator.groovy @@ -31,7 +31,6 @@ package com.google.protobuf.gradle import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project -import org.gradle.api.internal.file.FileResolver import org.gradle.api.tasks.SourceSet import org.gradle.util.ConfigureUtil @@ -44,21 +43,18 @@ public class ProtobufConfigurator { private final ToolsLocator tools private final ArrayList taskConfigClosures - final FileResolver fileResolver - /** * The base directory of generated files. The default is * "${project.buildDir}/generated/source/proto". */ public String generatedFilesBaseDir - public ProtobufConfigurator(Project project, FileResolver fileResolver) { + public ProtobufConfigurator(Project project) { this.project = project - this.fileResolver = fileResolver if (Utils.isAndroidProject(project)) { tasks = new AndroidGenerateProtoTaskCollection() } else { - tasks = new JavaGenerateProtoTaskCollection() + tasks = new DefaultGenerateProtoTaskCollection() } tools = new ToolsLocator(project) taskConfigClosures = new ArrayList() @@ -153,7 +149,7 @@ public class ProtobufConfigurator { } } - public class JavaGenerateProtoTaskCollection + public class DefaultGenerateProtoTaskCollection extends GenerateProtoTaskCollection { public Collection ofSourceSet(String sourceSet) { return all().findAll { task -> diff --git a/src/main/groovy/com/google/protobuf/gradle/ProtobufConvention.groovy b/src/main/groovy/com/google/protobuf/gradle/ProtobufConvention.groovy index 91b75bba..97194c43 100644 --- a/src/main/groovy/com/google/protobuf/gradle/ProtobufConvention.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/ProtobufConvention.groovy @@ -33,15 +33,14 @@ package com.google.protobuf.gradle import com.google.common.collect.ArrayListMultimap import com.google.common.collect.Multimap import org.gradle.api.Project -import org.gradle.api.internal.file.FileResolver import org.gradle.util.ConfigureUtil /** * Adds the protobuf {} block as a property of the project. */ class ProtobufConvention { - def ProtobufConvention(Project project, FileResolver fileResolver) { - protobuf = new ProtobufConfigurator(project, fileResolver) + def ProtobufConvention(Project project) { + protobuf = new ProtobufConfigurator(project) } def final ProtobufConfigurator protobuf diff --git a/src/main/groovy/com/google/protobuf/gradle/TaskGenerator.groovy b/src/main/groovy/com/google/protobuf/gradle/TaskGenerator.groovy index 31f524ab..d6b18eef 100644 --- a/src/main/groovy/com/google/protobuf/gradle/TaskGenerator.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/TaskGenerator.groovy @@ -23,9 +23,9 @@ class TaskGenerator { * compiled. For Java it's the sourceSet that sourceSetOrVariantName stands * for; for Android it's the collection of sourceSets that the variant includes. */ - static Task addGenerateProtoTask(Project project, String sourceSetOrVariantName, Collection sourceSets) { + static Task addGenerateProtoTask(Project project, String sourceSetOrVariantName, Collection sourceSets, String suffix = "") { def generateProtoTaskName = 'generate' + - Utils.getSourceSetSubstringForTaskNames(sourceSetOrVariantName) + 'Proto' + Utils.getSourceSetSubstringForTaskNames(sourceSetOrVariantName) + 'Proto' + suffix return project.tasks.create(generateProtoTaskName, GenerateProtoTask) { description = "Compiles Proto source for '${sourceSetOrVariantName}'" outputBaseDir = "${project.protobuf.generatedFilesBaseDir}/${sourceSetOrVariantName}" @@ -67,17 +67,13 @@ class TaskGenerator { * variant may have multiple sourceSets, each of these sourceSets will have * its own extraction task. */ - static Task maybeAddExtractProtosTask(Project project, String sourceSetName) { + static Task maybeAddExtractProtosTask(Project project, String sourceSetName, String suffix = "") { def extractProtosTaskName = 'extract' + - Utils.getSourceSetSubstringForTaskNames(sourceSetName) + 'Proto' - Task existingTask = project.tasks.findByName(extractProtosTaskName) - if (existingTask != null) { - return existingTask - } + Utils.getSourceSetSubstringForTaskNames(sourceSetName) + 'Proto' + suffix return project.tasks.create(extractProtosTaskName, ProtobufExtract) { description = "Extracts proto files/dependencies specified by 'protobuf' configuration" destDir = getExtractedProtosDir(project, sourceSetName) as File - inputs.files project.configurations[Utils.getConfigName(sourceSetName, 'protobuf')] + inputs.files project.configurations[Utils.getConfigName(sourceSetName, 'protobuf', suffix)] } } @@ -94,10 +90,6 @@ class TaskGenerator { static Task maybeAddExtractIncludeProtosTask(Project project, String sourceSetName, Object... inputFilesList) { def extractIncludeProtosTaskName = 'extractInclude' + Utils.getSourceSetSubstringForTaskNames(sourceSetName) + 'Proto' - Task existingTask = project.tasks.findByName(extractIncludeProtosTaskName) - if (existingTask != null) { - return existingTask - } return project.tasks.create(extractIncludeProtosTaskName, ProtobufExtract) { description = "Extracts proto files from compile dependencies for includes" destDir = getExtractedIncludeProtosDir(project, sourceSetName) as File diff --git a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy index 33bf66c1..0aca81cf 100644 --- a/src/main/groovy/com/google/protobuf/gradle/Utils.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/Utils.groovy @@ -38,9 +38,9 @@ class Utils { /** * Returns the conventional name of a configuration for a sourceSet */ - static String getConfigName(String sourceSetName, String type) { - return sourceSetName == SourceSet.MAIN_SOURCE_SET_NAME ? - type : (sourceSetName + StringUtils.capitalize(type)) + static String getConfigName(String sourceSetName, String type, String suffix = "") { + return (sourceSetName == SourceSet.MAIN_SOURCE_SET_NAME ? + type : (sourceSetName + StringUtils.capitalize(type))) + suffix } /** @@ -60,14 +60,12 @@ class Utils { * Creates a configuration if necessary for a source set so that the build * author can configure dependencies for it. */ - private static void createConfiguration(Project project, String sourceSetName) { - String configName = getConfigName(sourceSetName, 'protobuf') - if (project.configurations.findByName(configName) == null) { - project.configurations.create(configName) { - visible = false - transitive = false - extendsFrom = [] - } + private static void createConfiguration(Project project, String sourceSetName, String suffix) { + String configName = getConfigName(sourceSetName, 'protobuf', suffix) + project.configurations.create(configName) { + visible = false + transitive = false + extendsFrom = [] } } @@ -81,10 +79,10 @@ class Utils { } } - static void setupSourceSets(Project project, Object sourceSets, FileResolver fileResolver) { - addSourceSetExtensions(sourceSets, fileResolver) + static void setupSourceSets(Project project, Object sourceSets, String suffix = "") { + addSourceSetExtensions(sourceSets, project.fileResolver) sourceSets.all { sourceSet -> - createConfiguration(project, sourceSet.name) + createConfiguration(project, sourceSet.name, suffix) } } } diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy index 58be351a..354edbf2 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy @@ -18,7 +18,7 @@ class ProtobufAndroidPlugin implements Plugin { project.apply plugin: 'com.google.protobuf.base' - Utils.setupSourceSets(project, project.android.sourceSets, project.protobuf.fileResolver) + Utils.setupSourceSets(project, project.android.sourceSets) } /** diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy index f77fee3b..b846164e 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy @@ -3,22 +3,13 @@ package com.google.protobuf.gradle.plugins import com.google.protobuf.gradle.ProtobufConvention import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.internal.file.FileResolver - -import javax.inject.Inject class ProtobufBasePlugin implements Plugin { - final FileResolver fileResolver - private static final List protobufPlugins = [ 'com.google.protobuf.java', - 'com.google.protobuf.android'] - - @Inject - public ProtobufBasePlugin(FileResolver fileResolver) { - this.fileResolver = fileResolver - } + 'com.google.protobuf.android', + 'com.google.protobuf.csharp'] void apply(final Project project) { def gv = project.gradle.gradleVersion =~ "(\\d*)\\.(\\d*).*" @@ -30,7 +21,7 @@ class ProtobufBasePlugin implements Plugin { // Provides the osdetector extension project.apply plugin: 'osdetector' - project.convention.plugins.protobuf = new ProtobufConvention(project, fileResolver); + project.convention.plugins.protobuf = new ProtobufConvention(project) project.afterEvaluate { def appliedPlugins = protobufPlugins diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy index 6221586d..02d126b7 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy @@ -18,7 +18,7 @@ class ProtobufJavaPlugin implements Plugin { project.apply plugin: 'com.google.protobuf.base' - Utils.setupSourceSets(project, project.sourceSets, project.protobuf.fileResolver) + Utils.setupSourceSets(project, project.sourceSets) } /** diff --git a/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy b/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy index d42a13e3..8b9ff46c 100644 --- a/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy +++ b/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy @@ -8,8 +8,6 @@ import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome import org.junit.Rule import org.junit.rules.TemporaryFolder -import org.apache.commons.io.FileUtils - import spock.lang.Specification class ProtobufJavaPluginTest extends Specification { diff --git a/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy b/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy index d15b065d..14b93cd8 100644 --- a/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy +++ b/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy @@ -29,7 +29,7 @@ class ProtobufPluginTestHelper { } static void copyTestProject(String testProjectName, File projectDir) { - def baseDir = new File(System.getProperty("user.dir"), "testProject") + def baseDir = new File(System.getProperty("user.dir"), testProjectName) FileUtils.copyDirectory(baseDir, projectDir) From a9327cdfc05372842b7267bdf71a2a5014141ab3 Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Tue, 7 Jun 2016 20:31:26 +0800 Subject: [PATCH 13/27] Remove unused import --- .../google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy index 02d126b7..a534a7eb 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy @@ -1,13 +1,10 @@ package com.google.protobuf.gradle.plugins -import com.google.protobuf.gradle.ProtobufConvention import com.google.protobuf.gradle.TaskGenerator import com.google.protobuf.gradle.Utils -import javafx.concurrent.Task import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.tasks.SourceSet -import org.gradle.api.tasks.SourceSetContainer class ProtobufJavaPlugin implements Plugin { From 8def5715bdbe84d0e8ac8a8fb907b313551a94e8 Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Thu, 9 Jun 2016 09:50:43 +0800 Subject: [PATCH 14/27] Move testProjectAndroid to new testing framework --- build.gradle | 16 ------- settings.gradle | 14 +----- .../plugins/ProtobufAndroidPluginTest.groovy | 44 +++++++++++++++++++ 3 files changed, 45 insertions(+), 29 deletions(-) create mode 100644 src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy diff --git a/build.gradle b/build.gradle index d2aa0ec5..765cd733 100644 --- a/build.gradle +++ b/build.gradle @@ -41,22 +41,6 @@ buildscript { } } -subprojects { - buildscript { - repositories { - jcenter() - mavenLocal() - } - dependencies { - classpath "com.google.protobuf:protobuf-gradle-plugin:${rootProject.version}" - } - } - repositories { - mavenCentral() - jcenter() - } -} - repositories { mavenLocal() mavenCentral() diff --git a/settings.gradle b/settings.gradle index 1ce4846c..08e71515 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,19 +1,7 @@ rootProject.name = 'protobuf-gradle-plugin' -// Include test projects only when we intend to run the tests. -// We have to exclude them when we intend to build the plugin, because the test -// projects apply the plugin at evaluation time. At evaluation time the plugin -// has not been compiled yet. if (gradle.startParameter.taskNames.intersect(['install', 'uploadArchives', 'publishPlugins'])) { if (gradle.startParameter.taskNames.size > 1) { throw new GradleException("'install', 'uploadArchives' or 'publishPlugins' can only be used alone") } -} else { - logger.warn('***************************************************************************') - logger.warn('The tests will be run against the plugin that is installed locally.') - logger.warn('To test the current code, make sure you have run "./gradlew install" first.') - logger.warn('***************************************************************************') - - include ':testProjectAndroid' - project(':testProjectAndroid').projectDir = "$rootDir/testProjectAndroid" as File -} +} \ No newline at end of file diff --git a/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy b/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy new file mode 100644 index 00000000..765c3a5e --- /dev/null +++ b/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy @@ -0,0 +1,44 @@ +package com.google.protobuf.gradle.plugins + +import com.google.protobuf.gradle.GenerateProtoTask +import com.google.protobuf.gradle.ProtobufExtract +import org.gradle.api.Project +import org.gradle.testfixtures.ProjectBuilder +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import spock.lang.Specification + +class ProtobufAndroidPluginTest extends Specification { + + @Rule + final TemporaryFolder tempDir = new TemporaryFolder() + + def "testProjectAndroid should be successfully executed"() { + given: "project from testProject & testProjectAndroid" + def mainProjectDir = tempDir.newFolder() + def settingsFile = new File(mainProjectDir, 'settings.gradle') + settingsFile.createNewFile() + + ['testProject', 'testProjectAndroid'].each { + ProtobufPluginTestHelper.copyTestProject(it, new File(mainProjectDir.path, it)) + settingsFile << """ + include ':$it' + project(':$it').projectDir = "\$rootDir/testProject" as File + """ + } + + def buildFile = new File(mainProjectDir, 'build.gradle') + buildFile.createNewFile() + + when: "build is invoked" + def result = GradleRunner.create() + .withProjectDir(mainProjectDir) + .withArguments('testProjectAndroid:build') + .build() + + then: "it succeed" + result.task(":testProjectAndroid:build").outcome == TaskOutcome.SUCCESS + } +} \ No newline at end of file From 45d3980a53fecd649e48ac7618070a2f0432bba7 Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Fri, 10 Jun 2016 08:20:58 +0800 Subject: [PATCH 15/27] Fix test project didn't run correctly --- build.gradle | 31 ------------------- .../protobuf/gradle/TaskGenerator.groovy | 8 +++++ .../plugins/ProtobufAndroidPluginTest.groovy | 2 +- .../plugins/ProtobufJavaPluginTest.groovy | 2 +- testProjectAndroid/build.gradle | 9 +++++- 5 files changed, 18 insertions(+), 34 deletions(-) diff --git a/build.gradle b/build.gradle index 765cd733..42e60950 100644 --- a/build.gradle +++ b/build.gradle @@ -174,34 +174,3 @@ task checkJavaVersion << { } } [uploadArchives, publishPlugins]*.dependsOn checkJavaVersion - -ext { - // Shared test utility. Checks a JavaCompile task for the given sourceSet - // includes the generated source dirs for the given codegenPlugins, and does - // not include any other dirs under the generated code base dir. - assertJavaCompileHasProtoGeneratedDir = { - Project project, String sourceSet, JavaCompile compileJavaTask, Collection codegenPlugins -> - def baseDir = "${project.buildDir}/generated/source/proto/$sourceSet" as File - // The expected direct subdirectories under baseDir - def expectedDirs = codegenPlugins.collect { codegenPlugin -> - "${project.buildDir}/generated/source/proto/$sourceSet/$codegenPlugin" as File - } as Set - - def actualDirs = new HashSet() - compileJavaTask.source.visit { fileVisitDetails -> - // If the visited file is or is under a direct subdirectory of baseDir, add - // that subdirectory to actualDirs. - def file = fileVisitDetails.file - while (true) { - if (file.parentFile == baseDir) { - actualDirs.add file - } - if (file.parentFile == null) { - break - } - file = file.parentFile - } - } - org.junit.Assert.assertEquals("sourceSet=${sourceSet}", expectedDirs, actualDirs) - } -} diff --git a/src/main/groovy/com/google/protobuf/gradle/TaskGenerator.groovy b/src/main/groovy/com/google/protobuf/gradle/TaskGenerator.groovy index d6b18eef..9b1de9d4 100644 --- a/src/main/groovy/com/google/protobuf/gradle/TaskGenerator.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/TaskGenerator.groovy @@ -70,6 +70,10 @@ class TaskGenerator { static Task maybeAddExtractProtosTask(Project project, String sourceSetName, String suffix = "") { def extractProtosTaskName = 'extract' + Utils.getSourceSetSubstringForTaskNames(sourceSetName) + 'Proto' + suffix + Task existingTask = project.tasks.findByName(extractProtosTaskName) + if (existingTask != null) { + return existingTask + } return project.tasks.create(extractProtosTaskName, ProtobufExtract) { description = "Extracts proto files/dependencies specified by 'protobuf' configuration" destDir = getExtractedProtosDir(project, sourceSetName) as File @@ -90,6 +94,10 @@ class TaskGenerator { static Task maybeAddExtractIncludeProtosTask(Project project, String sourceSetName, Object... inputFilesList) { def extractIncludeProtosTaskName = 'extractInclude' + Utils.getSourceSetSubstringForTaskNames(sourceSetName) + 'Proto' + Task existingTask = project.tasks.findByName(extractIncludeProtosTaskName) + if (existingTask != null) { + return existingTask + } return project.tasks.create(extractIncludeProtosTaskName, ProtobufExtract) { description = "Extracts proto files from compile dependencies for includes" destDir = getExtractedIncludeProtosDir(project, sourceSetName) as File diff --git a/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy b/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy index 765c3a5e..c57087cc 100644 --- a/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy +++ b/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy @@ -25,7 +25,7 @@ class ProtobufAndroidPluginTest extends Specification { ProtobufPluginTestHelper.copyTestProject(it, new File(mainProjectDir.path, it)) settingsFile << """ include ':$it' - project(':$it').projectDir = "\$rootDir/testProject" as File + project(':$it').projectDir = "\$rootDir/$it" as File """ } diff --git a/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy b/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy index 8b9ff46c..144e96a0 100644 --- a/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy +++ b/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy @@ -91,7 +91,7 @@ class ProtobufJavaPluginTest extends Specification { ProtobufPluginTestHelper.copyTestProject(it, new File(mainProjectDir.path, it)) settingsFile << """ include ':$it' - project(':$it').projectDir = "\$rootDir/testProject" as File + project(':$it').projectDir = "\$rootDir/$it" as File """ } diff --git a/testProjectAndroid/build.gradle b/testProjectAndroid/build.gradle index e79b62ee..08798397 100644 --- a/testProjectAndroid/build.gradle +++ b/testProjectAndroid/build.gradle @@ -9,6 +9,13 @@ buildscript { classpath 'junit:junit:4.7' classpath 'org.jacoco:org.jacoco.core:0.7.4.201502262128' } + repositories { + mavenCentral() + jcenter() + maven { + url "https://plugins.gradle.org/m2/" + } + } } android { @@ -90,7 +97,7 @@ dependencies { } def assertJavaCompileHasProtoGeneratedDir(Object variant, Collection codegenPlugins) { - assertJavaCompileHasProtoGeneratedDir(project, variant.name, variant.javaCompile, codegenPlugins) +// assertJavaCompileHasProtoGeneratedDir(project, variant.name, variant.javaCompile, codegenPlugins) } afterEvaluate { From c18424ec95d95edd3a24dcc48c5f8407de73764b Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Fri, 10 Jun 2016 20:48:44 +0800 Subject: [PATCH 16/27] Add back assertion on testProject --- testProject/build.gradle | 70 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/testProject/build.gradle b/testProject/build.gradle index e608ca46..409d45bd 100644 --- a/testProject/build.gradle +++ b/testProject/build.gradle @@ -93,4 +93,72 @@ jar { from sourceSet.output dependsOn sourceSet.getCompileTaskName('java') } -} \ No newline at end of file +} + +def assertJavaCompileHasProtoGeneratedDir(String sourceSet, Collection codegenPlugins) { + def compileJavaTask = tasks.getByName(sourceSets.getByName(sourceSet).getCompileTaskName("java")) + assertJavaCompileHasProtoGeneratedDir(project, sourceSet, compileJavaTask, codegenPlugins) +} + +def assertFileExists(boolean exists, String path) { + if (exists) { + org.junit.Assert.assertTrue("\"${path}\" exists", (path as File).exists()); + } else { + org.junit.Assert.assertFalse("\"${path}\" doesn't exist", (path as File).exists()); + } +} + +test.doLast { + org.junit.Assert.assertEquals( + ['generateProto', 'generateNanoProto', 'generateGrpcProto', + 'generateGrpc_nanoProto', 'generateTestProto'] as Set, + protobuf.generateProtoTasks.all().collect({ it.name }) as Set) + org.junit.Assert.assertEquals( + ['generateProto'] as Set, + protobuf.generateProtoTasks.ofSourceSet('main').collect({ it.name }) as Set) + org.junit.Assert.assertEquals( + ['generateNanoProto'] as Set, + protobuf.generateProtoTasks.ofSourceSet('nano').collect({ it.name }) as Set) + assertJavaCompileHasProtoGeneratedDir('main', ['java']) + assertJavaCompileHasProtoGeneratedDir('test', ['java']) + assertJavaCompileHasProtoGeneratedDir('nano', ['javanano']) + assertJavaCompileHasProtoGeneratedDir('grpc', ['java', 'grpc']) + assertJavaCompileHasProtoGeneratedDir('grpc_nano', ['javanano', 'grpcjavanano']) + + // Check generateDescriptorSet option has been honored + ['main', 'test', 'nano', 'grpc'].each { sourceSet -> + assertFileExists(false, "$buildDir/generated/source/proto/$sourceSet/descriptor_set.desc") + } + assertFileExists(true, "$buildDir/generated/source/proto/grpc_nano/descriptor_set.desc") +} + +ext { + // Shared test utility. Checks a JavaCompile task for the given sourceSet + // includes the generated source dirs for the given codegenPlugins, and does + // not include any other dirs under the generated code base dir. + assertJavaCompileHasProtoGeneratedDir = { + Project project, String sourceSet, JavaCompile compileJavaTask, Collection codegenPlugins -> + def baseDir = "${project.buildDir}/generated/source/proto/$sourceSet" as File + // The expected direct subdirectories under baseDir + def expectedDirs = codegenPlugins.collect { codegenPlugin -> + "${project.buildDir}/generated/source/proto/$sourceSet/$codegenPlugin" as File + } as Set + + def actualDirs = new HashSet() + compileJavaTask.source.visit { fileVisitDetails -> + // If the visited file is or is under a direct subdirectory of baseDir, add + // that subdirectory to actualDirs. + def file = fileVisitDetails.file + while (true) { + if (file.parentFile == baseDir) { + actualDirs.add file + } + if (file.parentFile == null) { + break + } + file = file.parentFile + } + } + org.junit.Assert.assertEquals("sourceSet=${sourceSet}", expectedDirs, actualDirs) + } +} From 5b52f1e28494a625f27297f49de06117d60bf6af Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Fri, 10 Jun 2016 21:47:06 +0800 Subject: [PATCH 17/27] Remove gradlew install in ci --- .travis.yml | 3 --- testProject/build.gradle | 2 +- testProjectAndroid/build.gradle | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index c01643b5..23503936 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,9 +14,6 @@ android: licenses: - '.+' -install: - - ./gradlew install - script: - ./gradlew clean assemble test --stacktrace diff --git a/testProject/build.gradle b/testProject/build.gradle index f3b27eab..fca16699 100644 --- a/testProject/build.gradle +++ b/testProject/build.gradle @@ -142,7 +142,7 @@ test.doLast { assertFileExists(true, "$buildDir/generated/source/proto/grpc_nano/descriptor_set.desc") } -ext { +rootProject.ext { // Shared test utility. Checks a JavaCompile task for the given sourceSet // includes the generated source dirs for the given codegenPlugins, and does // not include any other dirs under the generated code base dir. diff --git a/testProjectAndroid/build.gradle b/testProjectAndroid/build.gradle index 4b84365f..15f554b1 100644 --- a/testProjectAndroid/build.gradle +++ b/testProjectAndroid/build.gradle @@ -108,7 +108,7 @@ dependencies { } def assertJavaCompileHasProtoGeneratedDir(Object variant, Collection codegenPlugins) { -// assertJavaCompileHasProtoGeneratedDir(project, variant.name, variant.javaCompile, codegenPlugins) + rootProject.assertJavaCompileHasProtoGeneratedDir(project, variant.name, variant.javaCompile, codegenPlugins) } afterEvaluate { From f432202d3a48c59e472b53ed33638ad41b1b6a0b Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Fri, 10 Jun 2016 23:54:53 +0800 Subject: [PATCH 18/27] Fix android test - in favor of assert (copy of #80) --- build.gradle | 12 +++- testProject/build.gradle | 23 ++++---- testProjectAndroid/build.gradle | 96 +++++++++++++++---------------- testProjectDependent/build.gradle | 4 +- 4 files changed, 67 insertions(+), 68 deletions(-) diff --git a/build.gradle b/build.gradle index 4351f472..2ec0d848 100644 --- a/build.gradle +++ b/build.gradle @@ -57,13 +57,19 @@ task createClasspathManifest { } dependencies { - compile gradleApi() - compile 'org.codehaus.groovy:groovy-all:2.4.4' + compileOnly gradleApi() + compileOnly localGroovy() + compile 'com.google.guava:guava:18.0' compile 'com.google.gradle:osdetector-gradle-plugin:1.2.1' compile 'commons-lang:commons-lang:2.6' + testCompile gradleTestKit() + testCompile gradleApi() + testCompile localGroovy() testCompile 'junit:junit:4.12' - testCompile 'org.spockframework:spock-core:1.0-groovy-2.4' + testCompile ('org.spockframework:spock-core:1.0-groovy-2.4') { + exclude module : 'groovy-all' + } testCompile 'commons-io:commons-io:2.5' // Add the classpath file to the test runtime classpath diff --git a/testProject/build.gradle b/testProject/build.gradle index fca16699..5b425651 100644 --- a/testProject/build.gradle +++ b/testProject/build.gradle @@ -112,23 +112,20 @@ def assertJavaCompileHasProtoGeneratedDir(String sourceSet, Collection c def assertFileExists(boolean exists, String path) { if (exists) { - org.junit.Assert.assertTrue("\"${path}\" exists", (path as File).exists()); + assert (path as File).exists() } else { - org.junit.Assert.assertFalse("\"${path}\" doesn't exist", (path as File).exists()); + assert !(path as File).exists() } } test.doLast { - org.junit.Assert.assertEquals( - ['generateProto', 'generateNanoProto', 'generateGrpcProto', - 'generateGrpc_nanoProto', 'generateTestProto'] as Set, - protobuf.generateProtoTasks.all().collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateProto'] as Set, - protobuf.generateProtoTasks.ofSourceSet('main').collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateNanoProto'] as Set, - protobuf.generateProtoTasks.ofSourceSet('nano').collect({ it.name }) as Set) + assert ['generateProto', 'generateNanoProto', 'generateGrpcProto', 'generateGrpc_nanoProto', + 'generateTestProto'] as Set == protobuf.generateProtoTasks.all().collect({ it.name }) as Set + + assert ['generateProto'] as Set == protobuf.generateProtoTasks.ofSourceSet('main').collect({ it.name }) as Set + + assert ['generateNanoProto'] as Set == protobuf.generateProtoTasks.ofSourceSet('nano').collect({ it.name }) as Set + assertJavaCompileHasProtoGeneratedDir('main', ['java']) assertJavaCompileHasProtoGeneratedDir('test', ['java']) assertJavaCompileHasProtoGeneratedDir('nano', ['javanano']) @@ -169,6 +166,6 @@ rootProject.ext { file = file.parentFile } } - org.junit.Assert.assertEquals("sourceSet=${sourceSet}", expectedDirs, actualDirs) + assert expectedDirs == actualDirs } } diff --git a/testProjectAndroid/build.gradle b/testProjectAndroid/build.gradle index 15f554b1..00ebce9d 100644 --- a/testProjectAndroid/build.gradle +++ b/testProjectAndroid/build.gradle @@ -6,11 +6,8 @@ apply plugin: 'com.google.protobuf' buildscript { dependencies { classpath 'com.android.tools.build:gradle:2.1.0' - classpath 'org.jacoco:org.jacoco.core:0.7.4.201502262128' } repositories { - mavenCentral() - jcenter() maven { url "https://plugins.gradle.org/m2/" } @@ -94,9 +91,7 @@ protobuf { dependencies { compile 'com.android.support:appcompat-v7:23.4.0' - compile 'com.google.code.findbugs:jsr305:3.0.0' - compile 'com.squareup.okhttp:okhttp:2.2.0' - compile 'com.google.guava:guava:18.0' + compile 'com.squareup.okhttp:okhttp:2.7.5' compile 'javax.annotation:javax.annotation-api:1.2' compile 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-3' compile 'io.grpc:grpc-core:0.7.0' @@ -104,6 +99,7 @@ dependencies { compile 'io.grpc:grpc-stub:0.7.0' compile 'io.grpc:grpc-protobuf-nano:0.7.0' compile project(':testProject') + protobuf files('lib/protos.jar') } @@ -120,72 +116,72 @@ afterEvaluate { } test.doLast { - org.junit.Assert.assertEquals( - ['generateArmFreeappDebugAndroidTestProto', 'generateArmFreeappDebugProto', + assert ['generateArmFreeappDebugAndroidTestProto', 'generateArmFreeappDebugProto', 'generateArmFreeappReleaseProto', 'generateArmRetailappDebugAndroidTestProto', 'generateArmRetailappDebugProto', 'generateArmRetailappReleaseProto', 'generateX86FreeappDebugAndroidTestProto', 'generateX86FreeappDebugProto', 'generateX86FreeappReleaseProto', 'generateX86RetailappDebugAndroidTestProto', - 'generateX86RetailappDebugProto', 'generateX86RetailappReleaseProto'] as Set, - protobuf.generateProtoTasks.all().collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateArmFreeappDebugAndroidTestProto', + 'generateX86RetailappDebugProto', 'generateX86RetailappReleaseProto'] as Set == + protobuf.generateProtoTasks.all().collect({ it.name }) as Set + + assert ['generateArmFreeappDebugAndroidTestProto', 'generateArmRetailappDebugAndroidTestProto', 'generateX86FreeappDebugAndroidTestProto', - 'generateX86RetailappDebugAndroidTestProto'] as Set, - protobuf.generateProtoTasks.ofTest().collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateArmFreeappDebugProto', 'generateArmFreeappReleaseProto', + 'generateX86RetailappDebugAndroidTestProto'] as Set == + protobuf.generateProtoTasks.ofTest().collect({ it.name }) as Set + + assert ['generateArmFreeappDebugProto', 'generateArmFreeappReleaseProto', 'generateArmRetailappDebugProto', 'generateArmRetailappReleaseProto', 'generateX86FreeappDebugProto', 'generateX86FreeappReleaseProto', - 'generateX86RetailappDebugProto', 'generateX86RetailappReleaseProto'] as Set, - protobuf.generateProtoTasks.ofNonTest().collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateArmFreeappDebugAndroidTestProto', 'generateArmFreeappDebugProto', + 'generateX86RetailappDebugProto', 'generateX86RetailappReleaseProto'] as Set == + protobuf.generateProtoTasks.ofNonTest().collect({ it.name }) as Set + + assert ['generateArmFreeappDebugAndroidTestProto', 'generateArmFreeappDebugProto', 'generateArmFreeappReleaseProto', 'generateX86FreeappDebugAndroidTestProto', 'generateX86FreeappDebugProto', - 'generateX86FreeappReleaseProto'] as Set, - protobuf.generateProtoTasks.ofFlavor('freeapp').collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateArmRetailappDebugAndroidTestProto', 'generateArmRetailappDebugProto', + 'generateX86FreeappReleaseProto'] as Set == + protobuf.generateProtoTasks.ofFlavor('freeapp').collect({ it.name }) as Set + + assert ['generateArmRetailappDebugAndroidTestProto', 'generateArmRetailappDebugProto', 'generateArmRetailappReleaseProto', 'generateX86RetailappDebugAndroidTestProto', 'generateX86RetailappDebugProto', - 'generateX86RetailappReleaseProto'] as Set, - protobuf.generateProtoTasks.ofFlavor('retailapp').collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateX86FreeappDebugAndroidTestProto', 'generateX86FreeappDebugProto', + 'generateX86RetailappReleaseProto'] as Set == + protobuf.generateProtoTasks.ofFlavor('retailapp').collect({ it.name }) as Set + + assert ['generateX86FreeappDebugAndroidTestProto', 'generateX86FreeappDebugProto', 'generateX86FreeappReleaseProto', 'generateX86RetailappDebugAndroidTestProto', - 'generateX86RetailappDebugProto', 'generateX86RetailappReleaseProto'] as Set, - protobuf.generateProtoTasks.ofFlavor('x86').collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateArmFreeappDebugAndroidTestProto', 'generateArmFreeappDebugProto', + 'generateX86RetailappDebugProto', 'generateX86RetailappReleaseProto'] as Set == + protobuf.generateProtoTasks.ofFlavor('x86').collect({ it.name }) as Set + + assert ['generateArmFreeappDebugAndroidTestProto', 'generateArmFreeappDebugProto', 'generateArmFreeappReleaseProto', 'generateArmRetailappDebugAndroidTestProto', - 'generateArmRetailappDebugProto', 'generateArmRetailappReleaseProto'] as Set, - protobuf.generateProtoTasks.ofFlavor('arm').collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateArmFreeappDebugAndroidTestProto', 'generateArmFreeappDebugProto', + 'generateArmRetailappDebugProto', 'generateArmRetailappReleaseProto'] as Set == + protobuf.generateProtoTasks.ofFlavor('arm').collect({ it.name }) as Set + + assert ['generateArmFreeappDebugAndroidTestProto', 'generateArmFreeappDebugProto', 'generateArmRetailappDebugAndroidTestProto', 'generateArmRetailappDebugProto', 'generateX86FreeappDebugAndroidTestProto', 'generateX86FreeappDebugProto', 'generateX86RetailappDebugAndroidTestProto', - 'generateX86RetailappDebugProto'] as Set, - protobuf.generateProtoTasks.ofBuildType('debug').collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateArmFreeappReleaseProto', 'generateArmRetailappReleaseProto', - 'generateX86FreeappReleaseProto', 'generateX86RetailappReleaseProto'] as Set, - protobuf.generateProtoTasks.ofBuildType('release').collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateX86FreeappDebugAndroidTestProto'] as Set, - protobuf.generateProtoTasks.ofVariant('x86FreeappDebugAndroidTest').collect({ it.name }) as Set) + 'generateX86RetailappDebugProto'] as Set == + protobuf.generateProtoTasks.ofBuildType('debug').collect({ it.name }) as Set + + assert ['generateArmFreeappReleaseProto', 'generateArmRetailappReleaseProto', + 'generateX86FreeappReleaseProto', 'generateX86RetailappReleaseProto'] as Set == + protobuf.generateProtoTasks.ofBuildType('release').collect({ it.name }) as Set + + assert ['generateX86FreeappDebugAndroidTestProto'] as Set == + protobuf.generateProtoTasks.ofVariant('x86FreeappDebugAndroidTest').collect({ it.name }) as Set + // "androidTest" sourceSet is not a flavor - org.junit.Assert.assertEquals( - [] as Set, - protobuf.generateProtoTasks.ofFlavor('androidTest').collect({ it.name }) as Set) - android.applicationVariants.each { variant -> + assert [] as Set == protobuf.generateProtoTasks.ofFlavor('androidTest').collect({ it.name }) as Set + + android.applicationVariants.each { variant -> assertJavaCompileHasProtoGeneratedDir(variant, ['javanano', 'grpc']) } + android.testVariants.each { variant -> assertJavaCompileHasProtoGeneratedDir(variant, ['javanano']) } } -} +} \ No newline at end of file diff --git a/testProjectDependent/build.gradle b/testProjectDependent/build.gradle index 8645da1f..982eba7b 100644 --- a/testProjectDependent/build.gradle +++ b/testProjectDependent/build.gradle @@ -10,7 +10,7 @@ apply plugin: 'com.google.protobuf' dependencies { compile 'com.google.protobuf:protobuf-java:3.0.0-alpha-3' compile project(':testProject') - testCompile 'junit:junit:4.7' + testCompile 'junit:junit:4.12' } protobuf.protoc { @@ -22,5 +22,5 @@ test.doLast { // other proto files from dependencies. def generatedFiles = project.fileTree(protobuf.generatedFilesBaseDir + "/main") File onlyGeneratedFile = generatedFiles.singleFile - org.junit.Assert.assertEquals('Dependent.java', onlyGeneratedFile.name) + assert 'Dependent.java' == onlyGeneratedFile.name } From e2cdb60d58c8e7bdca5e16aa98c32aa28fbbfee2 Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Sat, 11 Jun 2016 00:39:51 +0800 Subject: [PATCH 19/27] Ignore InvalidPackage in lint due to okio --- testProjectAndroid/build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/testProjectAndroid/build.gradle b/testProjectAndroid/build.gradle index 00ebce9d..686e2330 100644 --- a/testProjectAndroid/build.gradle +++ b/testProjectAndroid/build.gradle @@ -61,6 +61,11 @@ android { exclude 'io/grpc/testing/integration/messages.proto' exclude 'tmp/stuff.proto' } + + // https://github.com/square/okio/issues/58 + lintOptions { + warning 'InvalidPackage' + } } protobuf { From b8a19893325acb71f768fe22b9977293c3961c7c Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Sat, 11 Jun 2016 10:15:10 +0800 Subject: [PATCH 20/27] Refactor on test cases with multiple projects --- .../protobuf/gradle/GenerateProtoTask.groovy | 3 ++- .../plugins/ProtobufAndroidPluginTest.groovy | 16 ++-------------- .../plugins/ProtobufJavaPluginTest.groovy | 18 +++--------------- .../plugins/ProtobufPluginTestHelper.groovy | 18 +++++++++++++++++- 4 files changed, 24 insertions(+), 31 deletions(-) diff --git a/src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy b/src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy index 56e03e58..45951dd0 100644 --- a/src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy @@ -202,7 +202,8 @@ public class GenerateProtoTask extends DefaultTask { throw new IllegalStateException( "requested descriptor path but descriptor generation is off") } - return descriptorSetOptions.path != null ? descriptorSetOptions.path : "${outputBaseDir}/descriptor_set.desc" + return descriptorSetOptions.path != null + ? descriptorSetOptions.path : "${outputBaseDir}/descriptor_set.desc" } public GenerateProtoTask() { diff --git a/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy b/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy index c57087cc..c0b624f0 100644 --- a/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy +++ b/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy @@ -17,20 +17,8 @@ class ProtobufAndroidPluginTest extends Specification { def "testProjectAndroid should be successfully executed"() { given: "project from testProject & testProjectAndroid" - def mainProjectDir = tempDir.newFolder() - def settingsFile = new File(mainProjectDir, 'settings.gradle') - settingsFile.createNewFile() - - ['testProject', 'testProjectAndroid'].each { - ProtobufPluginTestHelper.copyTestProject(it, new File(mainProjectDir.path, it)) - settingsFile << """ - include ':$it' - project(':$it').projectDir = "\$rootDir/$it" as File - """ - } - - def buildFile = new File(mainProjectDir, 'build.gradle') - buildFile.createNewFile() + def mainProjectDir = tempDir.newFolder('test') + ProtobufPluginTestHelper.copyTestProjects(mainProjectDir, 'testProject', 'testProjectAndroid') when: "build is invoked" def result = GradleRunner.create() diff --git a/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy b/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy index 144e96a0..fb79fccb 100644 --- a/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy +++ b/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy @@ -59,7 +59,7 @@ class ProtobufJavaPluginTest extends Specification { def "testProject should be successfully executed"() { given: "project from testProject" def projectDir = tempDir.newFolder() - ProtobufPluginTestHelper.copyTestProject('testProject', projectDir) + ProtobufPluginTestHelper.copyTestProject(projectDir, 'testProject') when: "build is invoked" def result = GradleRunner.create() @@ -84,19 +84,7 @@ class ProtobufJavaPluginTest extends Specification { def "testProjectDependent should be successfully executed"() { given: "project from testProject & testProjectDependent" def mainProjectDir = tempDir.newFolder() - def settingsFile = new File(mainProjectDir, 'settings.gradle') - settingsFile.createNewFile() - - ['testProject', 'testProjectDependent'].each { - ProtobufPluginTestHelper.copyTestProject(it, new File(mainProjectDir.path, it)) - settingsFile << """ - include ':$it' - project(':$it').projectDir = "\$rootDir/$it" as File - """ - } - - def buildFile = new File(mainProjectDir, 'build.gradle') - buildFile.createNewFile() + ProtobufPluginTestHelper.copyTestProjects(mainProjectDir, 'testProject', 'testProjectDependent') when: "build is invoked" def result = GradleRunner.create() @@ -111,7 +99,7 @@ class ProtobufJavaPluginTest extends Specification { def "testProjectCustomProtoDir should be successfully executed"() { given: "project from testProjectCustomProtoDir" def projectDir = tempDir.newFolder() - ProtobufPluginTestHelper.copyTestProject('testProjectCustomProtoDir', projectDir) + ProtobufPluginTestHelper.copyTestProject(projectDir, 'testProjectCustomProtoDir', ) when: "build is invoked" def result = GradleRunner.create() diff --git a/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy b/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy index 14b93cd8..be8f3134 100644 --- a/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy +++ b/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy @@ -28,7 +28,7 @@ class ProtobufPluginTestHelper { """ } - static void copyTestProject(String testProjectName, File projectDir) { + static void copyTestProject(File projectDir, String testProjectName) { def baseDir = new File(System.getProperty("user.dir"), testProjectName) FileUtils.copyDirectory(baseDir, projectDir) @@ -36,4 +36,20 @@ class ProtobufPluginTestHelper { def buildFile = new File(projectDir.path, "build.gradle") appendPluginClasspath(buildFile) } + + static void copyTestProjects(File projectDir, String... testProjectNames) { + def settingsFile = new File(projectDir, 'settings.gradle') + settingsFile.createNewFile() + + testProjectNames.each { + copyTestProject(new File(projectDir.path, it), it) + settingsFile << """ + include ':$it' + project(':$it').projectDir = "\$rootDir/$it" as File + """ + } + + def buildFile = new File(projectDir, 'build.gradle') + buildFile.createNewFile() + } } \ No newline at end of file From 7c763524899119a5a34eeb929bdeedcd33cf4ed9 Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Wed, 15 Jun 2016 22:52:42 +0800 Subject: [PATCH 21/27] Fix according to comments - Add test projects to test inputs - Remove unnecessary settings --- build.gradle | 10 +++++++++- settings.gradle | 8 +------- .../com/google/plugins/ProtobufPluginTestHelper.groovy | 5 ----- testProject/build.gradle | 4 ++++ testProjectAndroid/build.gradle | 8 +++++--- testProjectCustomProtoDir/build.gradle | 4 ++++ testProjectDependent/build.gradle | 4 ++++ 7 files changed, 27 insertions(+), 16 deletions(-) diff --git a/build.gradle b/build.gradle index 7dd76be1..2ef38c60 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,6 @@ ext.isReleaseVersion = !version.endsWith("SNAPSHOT") buildscript { repositories { - mavenCentral() maven { url "https://plugins.gradle.org/m2/" } // Mirrors jcenter() and mavenCentral() } @@ -34,6 +33,10 @@ buildscript { } } +repositories { + maven { url "https://plugins.gradle.org/m2/" } +} + // Write the plugin's classpath to a file to share with the tests task createClasspathManifest { def outputDir = file("$buildDir/$name") @@ -67,6 +70,11 @@ dependencies { testRuntime files(createClasspathManifest) } +test.inputs.files fileTree("$projectDir/testProject") +test.inputs.files fileTree("$projectDir/testProjectCustomProtoDir") +test.inputs.files fileTree("$projectDir/testProjectDependent") +test.inputs.files fileTree("$projectDir/testProjectAndroid") + task sourcesJar(type: Jar, dependsOn:classes) { classifier = 'sources' from sourceSets.main.allSource diff --git a/settings.gradle b/settings.gradle index 08e71515..6be86fae 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,7 +1 @@ -rootProject.name = 'protobuf-gradle-plugin' - -if (gradle.startParameter.taskNames.intersect(['install', 'uploadArchives', 'publishPlugins'])) { - if (gradle.startParameter.taskNames.size > 1) { - throw new GradleException("'install', 'uploadArchives' or 'publishPlugins' can only be used alone") - } -} \ No newline at end of file +rootProject.name = 'protobuf-gradle-plugin' \ No newline at end of file diff --git a/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy b/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy index be8f3134..935d92bd 100644 --- a/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy +++ b/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy @@ -20,11 +20,6 @@ class ProtobufPluginTestHelper { classpath files($pluginClasspath) } } - - repositories { - mavenCentral() - jcenter() - } """ } diff --git a/testProject/build.gradle b/testProject/build.gradle index 23bb23d7..0cd13071 100644 --- a/testProject/build.gradle +++ b/testProject/build.gradle @@ -4,6 +4,10 @@ apply plugin: 'java' apply plugin: 'com.google.protobuf' +repositories { + maven { url "https://plugins.gradle.org/m2/" } +} + sourceCompatibility = JavaVersion.VERSION_1_7 targetCompatibility = JavaVersion.VERSION_1_7 diff --git a/testProjectAndroid/build.gradle b/testProjectAndroid/build.gradle index 57b68bbb..013d30ba 100644 --- a/testProjectAndroid/build.gradle +++ b/testProjectAndroid/build.gradle @@ -8,12 +8,14 @@ buildscript { classpath 'com.android.tools.build:gradle:2.1.0' } repositories { - maven { - url "https://plugins.gradle.org/m2/" - } + maven { url "https://plugins.gradle.org/m2/" } } } +repositories { + maven { url "https://plugins.gradle.org/m2/" } +} + android { compileSdkVersion 23 buildToolsVersion "23.0.3" diff --git a/testProjectCustomProtoDir/build.gradle b/testProjectCustomProtoDir/build.gradle index ce811d45..70ccda5c 100644 --- a/testProjectCustomProtoDir/build.gradle +++ b/testProjectCustomProtoDir/build.gradle @@ -4,6 +4,10 @@ apply plugin: 'java' apply plugin: 'com.google.protobuf' +repositories { + maven { url "https://plugins.gradle.org/m2/" } +} + sourceSets { main { proto { diff --git a/testProjectDependent/build.gradle b/testProjectDependent/build.gradle index c2be5323..892aae6c 100644 --- a/testProjectDependent/build.gradle +++ b/testProjectDependent/build.gradle @@ -7,6 +7,10 @@ apply plugin: 'java' apply plugin: 'com.google.protobuf' +repositories { + maven { url "https://plugins.gradle.org/m2/" } +} + dependencies { compile 'com.google.protobuf:protobuf-java:3.0.0-alpha-3' compile project(':testProject') From 083d41df19267ef1fddece46a7dadcc717cf2083 Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Thu, 21 Jul 2016 22:16:52 +0800 Subject: [PATCH 22/27] Update indentation Minor changes from comments as well --- .../gradle/plugins/ProtobufBasePlugin.groovy | 76 +++++++++---------- .../gradle/plugins/ProtobufPlugin.groovy | 75 +++++++++--------- 2 files changed, 75 insertions(+), 76 deletions(-) diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy index d4c591c1..1657cf7c 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufBasePlugin.groovy @@ -6,44 +6,42 @@ import org.gradle.api.Project class ProtobufBasePlugin implements Plugin { - private static final List protobufPlugins = [ - 'com.google.protobuf.java', - 'com.google.protobuf.android', - 'com.google.protobuf.csharp'] - - void apply(final Project project) { - def gv = project.gradle.gradleVersion =~ "(\\d*)\\.(\\d*).*" - if (!gv || !gv.matches() || gv.group(1).toInteger() != 2 || gv.group(2).toInteger() < 12) { - project.logger.error("You are using Gradle ${project.gradle.gradleVersion}: " - + " This version of the protobuf plugin works with Gradle version 2.12+") - } - - // Provides the osdetector extension - project.apply plugin: 'com.google.osdetector' - - project.convention.plugins.protobuf = new ProtobufConvention(project) - - project.afterEvaluate { - def appliedPlugins = protobufPlugins - .findAll { project.plugins.hasPlugin(it) } - .collect { project.plugins.getPlugin(it) } - - // The Android variants are only available at this point. - appliedPlugins.each { it.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() - - appliedPlugins.each { it.afterTaskAdded() } - - // 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.generateProtoTasks.all()) - } + private static final List protobufPlugins = [ + 'com.google.protobuf.java', + 'com.google.protobuf.android'] + + void apply(final Project project) { + def gv = project.gradle.gradleVersion =~ "(\\d*)\\.(\\d*).*" + if (!gv || !gv.matches() || gv.group(1).toInteger() != 2 || gv.group(2).toInteger() < 12) { + project.logger.error("You are using Gradle ${project.gradle.gradleVersion}: " + + " This version of the protobuf plugin works with Gradle version 2.12+") } + + // Provides the osdetector extension + project.apply plugin: 'com.google.osdetector' + project.convention.plugins.protobuf = new ProtobufConvention(project) + + project.afterEvaluate { + def appliedPlugins = protobufPlugins + .findAll { project.plugins.hasPlugin(it) } + .collect { project.plugins.getPlugin(it) } + + // The Android variants are only available at this point. + appliedPlugins.each { it.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() + + appliedPlugins.each { it.afterTaskAdded() } + + // 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.generateProtoTasks.all()) + } + } } \ No newline at end of file diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy index 72c3c1a3..792fcd73 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy @@ -37,50 +37,51 @@ import org.gradle.api.Project import org.gradle.api.plugins.AppliedPlugin class ProtobufPlugin implements Plugin { - // any one of these plugins should be sufficient to proceed with applying this plugin - private static final List javaPluginOptions = ['java'] - private static final List androidPluginOptions = ['com.android.application', - 'com.android.library', - 'android', - 'android-library'] + // any one of these plugins should be sufficient to proceed with applying this plugin + private static final List javaPluginOptions = ['java'] + private static final List androidPluginOptions = ['com.android.application', + 'com.android.library', + 'android', + 'android-library'] - private List pluginsApplied = []; - private Project project; + private List pluginsApplied = []; + private Project project; - void apply(final Project project) { - this.project = project + 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. - applyWithPrerequisitePlugin(javaPluginOptions, 'com.google.protobuf.java') - applyWithPrerequisitePlugin(androidPluginOptions, 'com.google.protobuf.android') + // 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. + applyWithPrerequisitePlugin(javaPluginOptions, 'com.google.protobuf.java') + applyWithPrerequisitePlugin(androidPluginOptions, 'com.google.protobuf.android') - project.afterEvaluate { - if (pluginsApplied.empty) { - 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.') - } - } + project.afterEvaluate { + if (pluginsApplied.empty) { + throw new GradleException('The com.google.protobuf plugin could not be applied during ' + + 'project evaluation. Prerequiste plugins (`java` or one of the Android plugins) ' + + 'must be applied to the project first.') + } } + } - void applyWithPrerequisitePlugin(List possiblePluginNames, String pluginToBeApplied) { - possiblePluginNames.each { pluginName -> - project.pluginManager.withPlugin(pluginName, { prerequisitePlugin -> - applyWithPrerequisitePlugin (prerequisitePlugin, pluginToBeApplied) - }) - } + void applyWithPrerequisitePlugin(List prerequisitePlugin, String pluginToBeApplied) { + prerequisitePlugin.each { pluginName -> + project.pluginManager.withPlugin(pluginName, { prerequisitePlugin -> + applyWithPrerequisitePlugin(prerequisitePlugin, pluginToBeApplied) + }) } + } - void applyWithPrerequisitePlugin(AppliedPlugin prerequisitePlugin, String pluginToBeApplied) { - if (pluginToBeApplied in pluginsApplied) { - 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 { - pluginsApplied.add pluginToBeApplied - project.apply plugin: pluginToBeApplied - } + void applyWithPrerequisitePlugin(AppliedPlugin prerequisitePlugin, String pluginToBeApplied) { + if (pluginToBeApplied in pluginsApplied) { + project.logger.warn("The ${pluginToBeApplied} plugin was already applied to the project: " + + "${project.path} and will not be applied again after plugin: ${prerequisitePlugin.id}") + } else { + pluginsApplied.add pluginToBeApplied + project.apply plugin: pluginToBeApplied } + } } From e364dc65991e8ede0fe77bec3e9a6108ea963c0b Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Thu, 21 Jul 2016 22:40:57 +0800 Subject: [PATCH 23/27] Fix typo on var name --- .../com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy index 792fcd73..ecd122a5 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPlugin.groovy @@ -67,8 +67,8 @@ class ProtobufPlugin implements Plugin { } } - void applyWithPrerequisitePlugin(List prerequisitePlugin, String pluginToBeApplied) { - prerequisitePlugin.each { pluginName -> + void applyWithPrerequisitePlugin(List prerequisitePlugins, String pluginToBeApplied) { + prerequisitePlugins.each { pluginName -> project.pluginManager.withPlugin(pluginName, { prerequisitePlugin -> applyWithPrerequisitePlugin(prerequisitePlugin, pluginToBeApplied) }) From b9e52791c8763c9fad12664d6c41a47b215dde46 Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Fri, 22 Jul 2016 22:21:36 +0800 Subject: [PATCH 24/27] Update indentation --- .../plugins/ProtobufAndroidPlugin.groovy | 183 +++++++++--------- .../gradle/plugins/ProtobufJavaPlugin.groovy | 109 ++++++----- 2 files changed, 148 insertions(+), 144 deletions(-) diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy index 354edbf2..f29a5412 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufAndroidPlugin.groovy @@ -11,104 +11,105 @@ import javax.inject.Inject class ProtobufAndroidPlugin implements Plugin { - private Project project - - void apply(final Project project) { - this.project = project - - project.apply plugin: 'com.google.protobuf.base' - - Utils.setupSourceSets(project, project.android.sourceSets) + private Project project + + void apply(final Project project) { + this.project = project + project.apply plugin: 'com.google.protobuf.base' + Utils.setupSourceSets(project, project.android.sourceSets) + } + + /** + * Adds Protobuf-related tasks to the project. + */ + void addProtoTasks() { + getNonTestVariants().each { variant -> + addTasksForVariant(variant, false) } - - /** - * Adds Protobuf-related tasks to the project. - */ - void addProtoTasks() { - getNonTestVariants().each { variant -> - addTasksForVariant(variant, false) - } - project.android.testVariants.each { testVariant -> - addTasksForVariant(testVariant, true) - } + project.android.testVariants.each { testVariant -> + addTasksForVariant(testVariant, true) } - - /** - * Performs after task are added and configured - */ - void afterTaskAdded() { - linkGenerateProtoTasksToJavaCompile() + } + + /** + * Performs after task are added and configured + */ + void afterTaskAdded() { + linkGenerateProtoTasksToJavaCompile() + } + + private Object getNonTestVariants() { + return project.android.hasProperty('libraryVariants') ? + project.android.libraryVariants : project.android.applicationVariants + } + + /** + * 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 + def sourceSetNames = new ArrayList() + def sourceSets = new ArrayList() + 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) } - private Object getNonTestVariants() { - return project.android.hasProperty('libraryVariants') ? - project.android.libraryVariants : project.android.applicationVariants + def generateProtoTask = TaskGenerator.addGenerateProtoTask(project, variant.name, sourceSets) + generateProtoTask.setVariant(variant, isTestVariant) + generateProtoTask.flavors = flavorListBuilder.build() + generateProtoTask.buildType = variant.buildType.name + generateProtoTask.doneInitializing() + generateProtoTask.builtins { + javanano {} } - /** - * 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 - def sourceSetNames = new ArrayList() - def sourceSets = new ArrayList() - 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) - } - - def generateProtoTask = TaskGenerator.addGenerateProtoTask(project, variant.name, sourceSets) - generateProtoTask.setVariant(variant, isTestVariant) - generateProtoTask.flavors = flavorListBuilder.build() - generateProtoTask.buildType = variant.buildType.name - generateProtoTask.doneInitializing() - generateProtoTask.builtins { - javanano {} - } - - sourceSetNames.each { sourceSetName -> - def extractProtosTask = TaskGenerator.maybeAddExtractProtosTask(project, sourceSetName) - generateProtoTask.dependsOn(extractProtosTask) - - def extractIncludeProtosTask - - // TODO(zhangkun83): Android sourceSet doesn't have compileClasspath. If it did, we - // haven't figured out a way to put source protos in 'resources'. For now we use an ad-hoc - // solution that manually includes the source protos of 'main' and its dependencies. - if (sourceSetName == 'androidTest') { - extractIncludeProtosTask = TaskGenerator.maybeAddExtractIncludeProtosTask(project, sourceSetName, project.android.sourceSets['main'].proto, project.configurations['compile']) - } else { - extractIncludeProtosTask = TaskGenerator.maybeAddExtractIncludeProtosTask(project, sourceSetName) - } - - generateProtoTask.dependsOn(extractIncludeProtosTask) - } - - // TODO(zhangkun83): Include source proto files in the compiled archive, - // so that proto files from dependent projects can import them. + sourceSetNames.each { sourceSetName -> + def extractProtosTask = TaskGenerator.maybeAddExtractProtosTask(project, sourceSetName) + generateProtoTask.dependsOn(extractProtosTask) + + def extractIncludeProtosTask + + // TODO(zhangkun83): Android sourceSet doesn't have compileClasspath. If it did, we + // haven't figured out a way to put source protos in 'resources'. For now we use an ad-hoc + // solution that manually includes the source protos of 'main' and its dependencies. + if (sourceSetName == 'androidTest') { + extractIncludeProtosTask = + TaskGenerator.maybeAddExtractIncludeProtosTask (project, sourceSetName, + project.android.sourceSets['main'].proto, project.configurations['compile']) + } else { + extractIncludeProtosTask = + TaskGenerator.maybeAddExtractIncludeProtosTask(project, sourceSetName) + } + + generateProtoTask.dependsOn(extractIncludeProtosTask) } - private void linkGenerateProtoTasksToJavaCompile() { - (getNonTestVariants() + project.android.testVariants).each { variant -> - project.protobuf.generateProtoTasks.ofVariant(variant.name).each { generateProtoTask -> - // This cannot be called once task execution has started - variant.registerJavaGeneratingTask(generateProtoTask, generateProtoTask.getAllOutputDirs()) - } - } + // TODO(zhangkun83): Include source proto files in the compiled archive, + // so that proto files from dependent projects can import them. + } + + private void linkGenerateProtoTasksToJavaCompile() { + (getNonTestVariants() + project.android.testVariants).each { variant -> + project.protobuf.generateProtoTasks.ofVariant(variant.name).each { generateProtoTask -> + // This cannot be called once task execution has started + variant.registerJavaGeneratingTask(generateProtoTask, generateProtoTask.getAllOutputDirs()) + } } + } } \ No newline at end of file diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy index a534a7eb..4649ab3e 100644 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufJavaPlugin.groovy @@ -8,71 +8,74 @@ import org.gradle.api.tasks.SourceSet class ProtobufJavaPlugin implements Plugin { - private Project project + private Project project - void apply(final Project project) { - this.project = project + void apply(final Project project) { + this.project = project - project.apply plugin: 'com.google.protobuf.base' + project.apply plugin: 'com.google.protobuf.base' - Utils.setupSourceSets(project, project.sourceSets) - } + Utils.setupSourceSets(project, project.sourceSets) + } - /** - * Adds Protobuf-related tasks to the project. - */ - void addProtoTasks() { - project.sourceSets.each { sourceSet -> - addTasksForSourceSet(sourceSet) - } + /** + * Adds Protobuf-related tasks to the project. + */ + void addProtoTasks() { + project.sourceSets.each { sourceSet -> + addTasksForSourceSet(sourceSet) } + } - /** - * Performs after task are added and configured - */ - void afterTaskAdded() { - linkGenerateProtoTasksToJavaCompile() - } + /** + * Performs after task are added and configured + */ + void afterTaskAdded() { + linkGenerateProtoTasksToJavaCompile() + } - /** - * Creates Protobuf tasks for a sourceSet in a Java project. - */ - private addTasksForSourceSet(final SourceSet sourceSet) { - def generateProtoTask = TaskGenerator.addGenerateProtoTask(project, sourceSet.name, [sourceSet]) - generateProtoTask.sourceSet = sourceSet - generateProtoTask.doneInitializing() - generateProtoTask.builtins { - java {} - } + /** + * Creates Protobuf tasks for a sourceSet in a Java project. + */ + private addTasksForSourceSet(final SourceSet sourceSet) { + def generateProtoTask = + TaskGenerator.addGenerateProtoTask(project, sourceSet.name, [sourceSet]) + generateProtoTask.sourceSet = sourceSet + generateProtoTask.doneInitializing() + generateProtoTask.builtins { + java {} + } - def extractProtosTask = TaskGenerator.maybeAddExtractProtosTask(project, sourceSet.name) - generateProtoTask.dependsOn(extractProtosTask) + def extractProtosTask = TaskGenerator.maybeAddExtractProtosTask(project, sourceSet.name) + generateProtoTask.dependsOn(extractProtosTask) - // In Java projects, the compileClasspath of the 'test' sourceSet includes all the - // 'resources' of the output of 'main', in which the source protos are placed. - // This is nicer than the ad-hoc solution that Android has, because it works for any - // extended configuration, not just 'testCompile'. - def extractIncludeProtosTask = TaskGenerator.maybeAddExtractIncludeProtosTask(project, sourceSet.name, sourceSet.compileClasspath) - generateProtoTask.dependsOn(extractIncludeProtosTask) + // In Java projects, the compileClasspath of the 'test' sourceSet includes all the + // 'resources' of the output of 'main', in which the source protos are placed. + // This is nicer than the ad-hoc solution that Android has, because it works for any + // extended configuration, not just 'testCompile'. + def extractIncludeProtosTask = + TaskGenerator.maybeAddExtractIncludeProtosTask(project, sourceSet.name, + sourceSet.compileClasspath) + generateProtoTask.dependsOn(extractIncludeProtosTask) - // Include source proto files in the compiled archive, so that proto files from - // dependent projects can import them. - def processResourcesTask = - project.tasks.getByName(sourceSet.getTaskName('process', 'resources')) - processResourcesTask.from(generateProtoTask.inputs.sourceFiles) { - include '**/*.proto' - } + // Include source proto files in the compiled archive, so that proto files from + // dependent projects can import them. + def processResourcesTask = + project.tasks.getByName(sourceSet.getTaskName('process', 'resources')) + processResourcesTask.from(generateProtoTask.inputs.sourceFiles) { + include '**/*.proto' } + } - private linkGenerateProtoTasksToJavaCompile() { - project.sourceSets.each { sourceSet -> - def javaCompileTask = project.tasks.getByName(sourceSet.getCompileTaskName("java")) - project.protobuf.generateProtoTasks.ofSourceSet(sourceSet.name).each { generateProtoTask -> - javaCompileTask.dependsOn(generateProtoTask) - generateProtoTask.getAllOutputDirs().each { dir -> - javaCompileTask.source project.fileTree(dir: dir) - } - } + private linkGenerateProtoTasksToJavaCompile() { + project.sourceSets.each { sourceSet -> + def javaCompileTask = project.tasks.getByName(sourceSet.getCompileTaskName("java")) + project.protobuf.generateProtoTasks.ofSourceSet(sourceSet.name).each { generateProtoTask -> + javaCompileTask.dependsOn(generateProtoTask) + generateProtoTask.getAllOutputDirs().each { dir -> + javaCompileTask.source project.fileTree(dir: dir) } + } } + } } \ No newline at end of file From 7b2b399cca5de08823ee038f48dc431c6e86f53a Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Sun, 24 Jul 2016 19:27:35 +0800 Subject: [PATCH 25/27] Improve running of test projects - Set classpath to test projects to remove the need for installing plugin before running test cases --- .travis.yml | 3 - README.md | 9 +- build.gradle | 82 +++++-------- settings.gradle | 26 +--- .../gradle/plugins/ProtobufPluginBase.groovy | 43 +++++++ .../plugins/ProtobufAndroidPluginTest.groovy | 32 +++++ .../plugins/ProtobufJavaPluginTest.groovy | 113 ++++++++++++++++++ .../plugins/ProtobufPluginTestHelper.groovy | 50 ++++++++ testProject/build.gradle | 56 +++++++-- testProjectAndroid/build.gradle | 106 +++++++++------- testProjectCustomProtoDir/build.gradle | 5 +- testProjectDependent/build.gradle | 6 +- 12 files changed, 387 insertions(+), 144 deletions(-) create mode 100644 src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPluginBase.groovy create mode 100644 src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy create mode 100644 src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy create mode 100644 src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy diff --git a/.travis.yml b/.travis.yml index c01643b5..23503936 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,9 +14,6 @@ android: licenses: - '.+' -install: - - ./gradlew install - script: - ./gradlew clean assemble test --stacktrace diff --git a/README.md b/README.md index ce6a37a0..8ce69bf5 100644 --- a/README.md +++ b/README.md @@ -433,10 +433,5 @@ Tools](https://developer.android.com/sdk/index.html#Other). After you made any change to the plugin, be sure to run these tests. ``` -$ ./gradlew install && ./gradlew clean test && ./gradlew test -``` -The tests use the plugin installed in Maven local repo, so you must install -it before testing it. We cannot make the tests depend the plugin project -directly, because the test projects apply the plugin at evaluation time. At -evaluation time the plugin project has not been compiled yet. The second test -run is to make sure incremental build works. +$ ./gradlew test +``` \ No newline at end of file diff --git a/build.gradle b/build.gradle index 766ae1ee..2ef38c60 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,8 @@ apply plugin: 'groovy' apply plugin: 'maven' apply plugin: 'signing' apply plugin: 'com.jfrog.bintray' -apply plugin: "com.gradle.plugin-publish" +apply plugin: 'com.gradle.plugin-publish' +apply plugin: 'com.github.ben-manes.versions' group = 'com.google.protobuf' version = '0.7.8-SNAPSHOT' @@ -26,44 +27,54 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.1.0' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6' classpath "com.gradle.publish:plugin-publish-plugin:0.9.4" classpath "com.github.ben-manes:gradle-versions-plugin:0.12.0" - classpath "junit:junit:4.12" } } -allprojects { - apply plugin: "com.github.ben-manes.versions" - - repositories { - maven { url "https://plugins.gradle.org/m2/" } // Mirrors jcenter() and mavenCentral() - } +repositories { + maven { url "https://plugins.gradle.org/m2/" } } -subprojects { - buildscript { - repositories { - mavenLocal() - maven { url "https://plugins.gradle.org/m2/" } // Mirrors jcenter() and mavenCentral() - } +// Write the plugin's classpath to a file to share with the tests +task createClasspathManifest { + def outputDir = file("$buildDir/$name") - dependencies { - classpath "com.google.protobuf:protobuf-gradle-plugin:${rootProject.version}" - } + inputs.files sourceSets.main.runtimeClasspath + outputs.dir outputDir + + doLast { + outputDir.mkdirs() + file("$outputDir/plugin-classpath.txt").text = sourceSets.main.runtimeClasspath.join("\n") } } dependencies { - compile gradleApi() - compile localGroovy() + compileOnly gradleApi() + compileOnly localGroovy() + compile 'com.google.guava:guava:18.0' compile 'com.google.gradle:osdetector-gradle-plugin:1.4.0' compile 'commons-lang:commons-lang:2.6' + testCompile gradleTestKit() + testCompile gradleApi() + testCompile localGroovy() testCompile 'junit:junit:4.12' + testCompile ('org.spockframework:spock-core:1.0-groovy-2.4') { + exclude module : 'groovy-all' + } + testCompile 'commons-io:commons-io:2.5' + + // Add the classpath file to the test runtime classpath + testRuntime files(createClasspathManifest) } +test.inputs.files fileTree("$projectDir/testProject") +test.inputs.files fileTree("$projectDir/testProjectCustomProtoDir") +test.inputs.files fileTree("$projectDir/testProjectDependent") +test.inputs.files fileTree("$projectDir/testProjectAndroid") + task sourcesJar(type: Jar, dependsOn:classes) { classifier = 'sources' from sourceSets.main.allSource @@ -162,34 +173,3 @@ task checkJavaVersion << { } } [uploadArchives, publishPlugins]*.dependsOn checkJavaVersion - -ext { - // Shared test utility. Checks a JavaCompile task for the given sourceSet - // includes the generated source dirs for the given codegenPlugins, and does - // not include any other dirs under the generated code base dir. - assertJavaCompileHasProtoGeneratedDir = { - Project project, String sourceSet, JavaCompile compileJavaTask, Collection codegenPlugins -> - def baseDir = "${project.buildDir}/generated/source/proto/$sourceSet" as File - // The expected direct subdirectories under baseDir - def expectedDirs = codegenPlugins.collect { codegenPlugin -> - "${project.buildDir}/generated/source/proto/$sourceSet/$codegenPlugin" as File - } as Set - - def actualDirs = new HashSet() - compileJavaTask.source.visit { fileVisitDetails -> - // If the visited file is or is under a direct subdirectory of baseDir, add - // that subdirectory to actualDirs. - def file = fileVisitDetails.file - while (true) { - if (file.parentFile == baseDir) { - actualDirs.add file - } - if (file.parentFile == null) { - break - } - file = file.parentFile - } - } - org.junit.Assert.assertEquals("sourceSet=${sourceSet}", expectedDirs, actualDirs) - } -} diff --git a/settings.gradle b/settings.gradle index 021b1bab..6be86fae 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,25 +1 @@ -rootProject.name = 'protobuf-gradle-plugin' - -// Include test projects only when we intend to run the tests. -// We have to exclude them when we intend to build the plugin, because the test -// projects apply the plugin at evaluation time. At evaluation time the plugin -// has not been compiled yet. -if (gradle.startParameter.taskNames.intersect(['install', 'uploadArchives', 'publishPlugins'])) { - if (gradle.startParameter.taskNames.size > 1) { - throw new GradleException("'install', 'uploadArchives' or 'publishPlugins' can only be used alone") - } -} else { - logger.warn('***************************************************************************') - logger.warn('The tests will be run against the plugin that is installed locally.') - logger.warn('To test the current code, make sure you have run "./gradlew install" first.') - logger.warn('***************************************************************************') - - include ':testProject' - project(':testProject').projectDir = "$rootDir/testProject" as File - include ':testProjectCustomProtoDir' - project(':testProjectCustomProtoDir').projectDir = "$rootDir/testProjectCustomProtoDir" as File - include ':testProjectDependent' - project(':testProjectDependent').projectDir = "$rootDir/testProjectDependent" as File - include ':testProjectAndroid' - project(':testProjectAndroid').projectDir = "$rootDir/testProjectAndroid" as File -} +rootProject.name = 'protobuf-gradle-plugin' \ No newline at end of file diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPluginBase.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPluginBase.groovy new file mode 100644 index 00000000..c2c8a122 --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPluginBase.groovy @@ -0,0 +1,43 @@ +package com.google.protobuf.gradle.plugins + +import com.google.protobuf.gradle.ProtobufConvention +import org.gradle.api.Plugin +import org.gradle.api.Project + +class ProtobufBasePlugin implements Plugin { + + void apply(final Project project) { + def gv = project.gradle.gradleVersion =~ "(\\d*)\\.(\\d*).*" + if (!gv || !gv.matches() || gv.group(1).toInteger() != 2 || gv.group(2).toInteger() < 12) { + project.logger.error("You are using Gradle ${project.gradle.gradleVersion}: " + + " This version of the protobuf plugin works with Gradle version 2.12+") + } + + // Provides the osdetector extension + if (!project.plugins.hasPlugin('com.google.osdetector')) { + project.apply plugin: 'com.google.osdetector' + } + if (!project.convention.plugins.protobuf) { + project.convention.plugins.protobuf = new ProtobufConvention(project) + } + + 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() + + appliedPlugins.each { it.afterTaskAdded() } + + // 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.generateProtoTasks.all()) + } + } +} \ No newline at end of file diff --git a/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy b/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy new file mode 100644 index 00000000..c0b624f0 --- /dev/null +++ b/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy @@ -0,0 +1,32 @@ +package com.google.protobuf.gradle.plugins + +import com.google.protobuf.gradle.GenerateProtoTask +import com.google.protobuf.gradle.ProtobufExtract +import org.gradle.api.Project +import org.gradle.testfixtures.ProjectBuilder +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import spock.lang.Specification + +class ProtobufAndroidPluginTest extends Specification { + + @Rule + final TemporaryFolder tempDir = new TemporaryFolder() + + def "testProjectAndroid should be successfully executed"() { + given: "project from testProject & testProjectAndroid" + def mainProjectDir = tempDir.newFolder('test') + ProtobufPluginTestHelper.copyTestProjects(mainProjectDir, 'testProject', 'testProjectAndroid') + + when: "build is invoked" + def result = GradleRunner.create() + .withProjectDir(mainProjectDir) + .withArguments('testProjectAndroid:build') + .build() + + then: "it succeed" + result.task(":testProjectAndroid:build").outcome == TaskOutcome.SUCCESS + } +} \ No newline at end of file diff --git a/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy b/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy new file mode 100644 index 00000000..fb79fccb --- /dev/null +++ b/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy @@ -0,0 +1,113 @@ +package com.google.protobuf.gradle.plugins + +import com.google.protobuf.gradle.GenerateProtoTask +import com.google.protobuf.gradle.ProtobufExtract +import org.gradle.api.Project +import org.gradle.testfixtures.ProjectBuilder +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import spock.lang.Specification + +class ProtobufJavaPluginTest extends Specification { + + @Rule + final TemporaryFolder tempDir = new TemporaryFolder() + + private Project setupBasicProject() { + Project project = ProjectBuilder.builder().build() + project.apply plugin: 'java' + project.apply plugin: 'com.google.protobuf' + return project + } + + def "Applying java and com.google.protobuf adds corresponding task to project"() { + given: "a basic project with java and com.google.protobuf" + def project = setupBasicProject() + + when: "project evaluated" + project.evaluate() + + then: "generate tasks added" + assert project.tasks.generateProto instanceof GenerateProtoTask + assert project.tasks.generateTestProto instanceof GenerateProtoTask + + assert project.tasks.extractIncludeProto instanceof ProtobufExtract + assert project.tasks.extractIncludeTestProto instanceof ProtobufExtract + assert project.tasks.extractProto instanceof ProtobufExtract + assert project.tasks.extractTestProto instanceof ProtobufExtract + } + + def "Custom sourceSet should get its own GenerateProtoTask"() { + given: "a basic project with java and com.google.protobuf" + def project = setupBasicProject() + + when: "adding custom sourceSet nano" + project.sourceSets.create('nano') + + and: "project evaluated" + project.evaluate() + + then: "tasks for nano added" + assert project.tasks.generateNanoProto instanceof GenerateProtoTask + + assert project.tasks.extractIncludeNanoProto instanceof ProtobufExtract + assert project.tasks.extractNanoProto instanceof ProtobufExtract + } + + def "testProject should be successfully executed"() { + given: "project from testProject" + def projectDir = tempDir.newFolder() + ProtobufPluginTestHelper.copyTestProject(projectDir, 'testProject') + + when: "build is invoked" + def result = GradleRunner.create() + .withProjectDir(projectDir) + .withArguments('build') + .build() + + then: "it succeed" + result.task(":build").outcome == TaskOutcome.SUCCESS + ['grpc', 'grpc_nano', 'main', 'nano', 'test'].each { + def generatedSrcDir = new File(projectDir.path, "build/generated/source/proto/$it") + def fileList = [] + generatedSrcDir.eachFileRecurse { file -> + if (file.path.endsWith('.java')) { + fileList.add (file) + } + } + assert fileList.size > 0 + } + } + + def "testProjectDependent should be successfully executed"() { + given: "project from testProject & testProjectDependent" + def mainProjectDir = tempDir.newFolder() + ProtobufPluginTestHelper.copyTestProjects(mainProjectDir, 'testProject', 'testProjectDependent') + + when: "build is invoked" + def result = GradleRunner.create() + .withProjectDir(mainProjectDir) + .withArguments('testProjectDependent:build') + .build() + + then: "it succeed" + result.task(":testProjectDependent:build").outcome == TaskOutcome.SUCCESS + } + + def "testProjectCustomProtoDir should be successfully executed"() { + given: "project from testProjectCustomProtoDir" + def projectDir = tempDir.newFolder() + ProtobufPluginTestHelper.copyTestProject(projectDir, 'testProjectCustomProtoDir', ) + + when: "build is invoked" + def result = GradleRunner.create() + .withProjectDir(projectDir) + .withArguments('build') + .build() + + then: "it succeed" + result.task(":build").outcome == TaskOutcome.SUCCESS + } +} \ No newline at end of file diff --git a/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy b/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy new file mode 100644 index 00000000..935d92bd --- /dev/null +++ b/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy @@ -0,0 +1,50 @@ +import org.apache.commons.io.FileUtils + +class ProtobufPluginTestHelper { + + static void appendPluginClasspath(File buildFile) { + def pluginClasspathResource = ProtobufPluginTestHelper.class.classLoader.findResource("plugin-classpath.txt") + if (pluginClasspathResource == null) { + throw new IllegalStateException("Did not find plugin classpath resource, run `testClasses` build task.") + } + + def pluginClasspath = pluginClasspathResource.readLines() + .collect { it.replace('\\', '\\\\') } // escape backslashes in Windows paths + .collect { "'$it'" } + .join(", ") + + // Add the logic under test to the test build + buildFile << """ + buildscript { + dependencies { + classpath files($pluginClasspath) + } + } + """ + } + + static void copyTestProject(File projectDir, String testProjectName) { + def baseDir = new File(System.getProperty("user.dir"), testProjectName) + + FileUtils.copyDirectory(baseDir, projectDir) + + def buildFile = new File(projectDir.path, "build.gradle") + appendPluginClasspath(buildFile) + } + + static void copyTestProjects(File projectDir, String... testProjectNames) { + def settingsFile = new File(projectDir, 'settings.gradle') + settingsFile.createNewFile() + + testProjectNames.each { + copyTestProject(new File(projectDir.path, it), it) + settingsFile << """ + include ':$it' + project(':$it').projectDir = "\$rootDir/$it" as File + """ + } + + def buildFile = new File(projectDir, 'build.gradle') + buildFile.createNewFile() + } +} \ No newline at end of file diff --git a/testProject/build.gradle b/testProject/build.gradle index 937490fc..0cd13071 100644 --- a/testProject/build.gradle +++ b/testProject/build.gradle @@ -4,6 +4,10 @@ apply plugin: 'java' apply plugin: 'com.google.protobuf' +repositories { + maven { url "https://plugins.gradle.org/m2/" } +} + sourceCompatibility = JavaVersion.VERSION_1_7 targetCompatibility = JavaVersion.VERSION_1_7 @@ -110,23 +114,20 @@ def assertJavaCompileHasProtoGeneratedDir(String sourceSet, Collection c def assertFileExists(boolean exists, String path) { if (exists) { - org.junit.Assert.assertTrue("\"${path}\" exists", (path as File).exists()); + assert (path as File).exists() } else { - org.junit.Assert.assertFalse("\"${path}\" doesn't exist", (path as File).exists()); + assert !(path as File).exists() } } test.doLast { - org.junit.Assert.assertEquals( - ['generateProto', 'generateNanoProto', 'generateGrpcProto', - 'generateGrpc_nanoProto', 'generateTestProto'] as Set, - protobuf.generateProtoTasks.all().collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateProto'] as Set, - protobuf.generateProtoTasks.ofSourceSet('main').collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateNanoProto'] as Set, - protobuf.generateProtoTasks.ofSourceSet('nano').collect({ it.name }) as Set) + assert ['generateProto', 'generateNanoProto', 'generateGrpcProto', 'generateGrpc_nanoProto', + 'generateTestProto'] as Set == protobuf.generateProtoTasks.all().collect({ it.name }) as Set + + assert ['generateProto'] as Set == protobuf.generateProtoTasks.ofSourceSet('main').collect({ it.name }) as Set + + assert ['generateNanoProto'] as Set == protobuf.generateProtoTasks.ofSourceSet('nano').collect({ it.name }) as Set + assertJavaCompileHasProtoGeneratedDir('main', ['java']) assertJavaCompileHasProtoGeneratedDir('test', ['java']) assertJavaCompileHasProtoGeneratedDir('nano', ['javanano']) @@ -139,3 +140,34 @@ test.doLast { } assertFileExists(true, "$buildDir/generated/source/proto/grpc_nano/descriptor_set.desc") } + +rootProject.ext { + // Shared test utility. Checks a JavaCompile task for the given sourceSet + // includes the generated source dirs for the given codegenPlugins, and does + // not include any other dirs under the generated code base dir. + assertJavaCompileHasProtoGeneratedDir = { + Project project, String sourceSet, JavaCompile compileJavaTask, Collection codegenPlugins -> + def baseDir = "${project.buildDir}/generated/source/proto/$sourceSet" as File + // The expected direct subdirectories under baseDir + def expectedDirs = codegenPlugins.collect { codegenPlugin -> + "${project.buildDir}/generated/source/proto/$sourceSet/$codegenPlugin" as File + } as Set + + def actualDirs = new HashSet() + compileJavaTask.source.visit { fileVisitDetails -> + // If the visited file is or is under a direct subdirectory of baseDir, add + // that subdirectory to actualDirs. + def file = fileVisitDetails.file + while (true) { + if (file.parentFile == baseDir) { + actualDirs.add file + } + if (file.parentFile == null) { + break + } + file = file.parentFile + } + } + assert expectedDirs == actualDirs + } +} diff --git a/testProjectAndroid/build.gradle b/testProjectAndroid/build.gradle index 241ffa6d..013d30ba 100644 --- a/testProjectAndroid/build.gradle +++ b/testProjectAndroid/build.gradle @@ -3,6 +3,19 @@ apply plugin: 'com.android.application' apply plugin: 'com.google.protobuf' +buildscript { + dependencies { + classpath 'com.android.tools.build:gradle:2.1.0' + } + repositories { + maven { url "https://plugins.gradle.org/m2/" } + } +} + +repositories { + maven { url "https://plugins.gradle.org/m2/" } +} + android { compileSdkVersion 23 buildToolsVersion "23.0.3" @@ -50,6 +63,11 @@ android { exclude 'io/grpc/testing/integration/messages.proto' exclude 'tmp/stuff.proto' } + + // https://github.com/square/okio/issues/58 + lintOptions { + warning 'InvalidPackage' + } } protobuf { @@ -93,7 +111,7 @@ dependencies { } def assertJavaCompileHasProtoGeneratedDir(Object variant, Collection codegenPlugins) { - assertJavaCompileHasProtoGeneratedDir(project, variant.name, variant.javaCompile, codegenPlugins) + rootProject.assertJavaCompileHasProtoGeneratedDir(project, variant.name, variant.javaCompile, codegenPlugins) } afterEvaluate { @@ -105,70 +123,70 @@ afterEvaluate { } test.doLast { - org.junit.Assert.assertEquals( - ['generateArmFreeappDebugAndroidTestProto', 'generateArmFreeappDebugProto', + assert ['generateArmFreeappDebugAndroidTestProto', 'generateArmFreeappDebugProto', 'generateArmFreeappReleaseProto', 'generateArmRetailappDebugAndroidTestProto', 'generateArmRetailappDebugProto', 'generateArmRetailappReleaseProto', 'generateX86FreeappDebugAndroidTestProto', 'generateX86FreeappDebugProto', 'generateX86FreeappReleaseProto', 'generateX86RetailappDebugAndroidTestProto', - 'generateX86RetailappDebugProto', 'generateX86RetailappReleaseProto'] as Set, - protobuf.generateProtoTasks.all().collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateArmFreeappDebugAndroidTestProto', + 'generateX86RetailappDebugProto', 'generateX86RetailappReleaseProto'] as Set == + protobuf.generateProtoTasks.all().collect({ it.name }) as Set + + assert ['generateArmFreeappDebugAndroidTestProto', 'generateArmRetailappDebugAndroidTestProto', 'generateX86FreeappDebugAndroidTestProto', - 'generateX86RetailappDebugAndroidTestProto'] as Set, - protobuf.generateProtoTasks.ofTest().collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateArmFreeappDebugProto', 'generateArmFreeappReleaseProto', + 'generateX86RetailappDebugAndroidTestProto'] as Set == + protobuf.generateProtoTasks.ofTest().collect({ it.name }) as Set + + assert ['generateArmFreeappDebugProto', 'generateArmFreeappReleaseProto', 'generateArmRetailappDebugProto', 'generateArmRetailappReleaseProto', 'generateX86FreeappDebugProto', 'generateX86FreeappReleaseProto', - 'generateX86RetailappDebugProto', 'generateX86RetailappReleaseProto'] as Set, - protobuf.generateProtoTasks.ofNonTest().collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateArmFreeappDebugAndroidTestProto', 'generateArmFreeappDebugProto', + 'generateX86RetailappDebugProto', 'generateX86RetailappReleaseProto'] as Set == + protobuf.generateProtoTasks.ofNonTest().collect({ it.name }) as Set + + assert ['generateArmFreeappDebugAndroidTestProto', 'generateArmFreeappDebugProto', 'generateArmFreeappReleaseProto', 'generateX86FreeappDebugAndroidTestProto', 'generateX86FreeappDebugProto', - 'generateX86FreeappReleaseProto'] as Set, - protobuf.generateProtoTasks.ofFlavor('freeapp').collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateArmRetailappDebugAndroidTestProto', 'generateArmRetailappDebugProto', + 'generateX86FreeappReleaseProto'] as Set == + protobuf.generateProtoTasks.ofFlavor('freeapp').collect({ it.name }) as Set + + assert ['generateArmRetailappDebugAndroidTestProto', 'generateArmRetailappDebugProto', 'generateArmRetailappReleaseProto', 'generateX86RetailappDebugAndroidTestProto', 'generateX86RetailappDebugProto', - 'generateX86RetailappReleaseProto'] as Set, - protobuf.generateProtoTasks.ofFlavor('retailapp').collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateX86FreeappDebugAndroidTestProto', 'generateX86FreeappDebugProto', + 'generateX86RetailappReleaseProto'] as Set == + protobuf.generateProtoTasks.ofFlavor('retailapp').collect({ it.name }) as Set + + assert ['generateX86FreeappDebugAndroidTestProto', 'generateX86FreeappDebugProto', 'generateX86FreeappReleaseProto', 'generateX86RetailappDebugAndroidTestProto', - 'generateX86RetailappDebugProto', 'generateX86RetailappReleaseProto'] as Set, - protobuf.generateProtoTasks.ofFlavor('x86').collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateArmFreeappDebugAndroidTestProto', 'generateArmFreeappDebugProto', + 'generateX86RetailappDebugProto', 'generateX86RetailappReleaseProto'] as Set == + protobuf.generateProtoTasks.ofFlavor('x86').collect({ it.name }) as Set + + assert ['generateArmFreeappDebugAndroidTestProto', 'generateArmFreeappDebugProto', 'generateArmFreeappReleaseProto', 'generateArmRetailappDebugAndroidTestProto', - 'generateArmRetailappDebugProto', 'generateArmRetailappReleaseProto'] as Set, - protobuf.generateProtoTasks.ofFlavor('arm').collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateArmFreeappDebugAndroidTestProto', 'generateArmFreeappDebugProto', + 'generateArmRetailappDebugProto', 'generateArmRetailappReleaseProto'] as Set == + protobuf.generateProtoTasks.ofFlavor('arm').collect({ it.name }) as Set + + assert ['generateArmFreeappDebugAndroidTestProto', 'generateArmFreeappDebugProto', 'generateArmRetailappDebugAndroidTestProto', 'generateArmRetailappDebugProto', 'generateX86FreeappDebugAndroidTestProto', 'generateX86FreeappDebugProto', 'generateX86RetailappDebugAndroidTestProto', - 'generateX86RetailappDebugProto'] as Set, - protobuf.generateProtoTasks.ofBuildType('debug').collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateArmFreeappReleaseProto', 'generateArmRetailappReleaseProto', - 'generateX86FreeappReleaseProto', 'generateX86RetailappReleaseProto'] as Set, - protobuf.generateProtoTasks.ofBuildType('release').collect({ it.name }) as Set) - org.junit.Assert.assertEquals( - ['generateX86FreeappDebugAndroidTestProto'] as Set, - protobuf.generateProtoTasks.ofVariant('x86FreeappDebugAndroidTest').collect({ it.name }) as Set) + 'generateX86RetailappDebugProto'] as Set == + protobuf.generateProtoTasks.ofBuildType('debug').collect({ it.name }) as Set + + assert ['generateArmFreeappReleaseProto', 'generateArmRetailappReleaseProto', + 'generateX86FreeappReleaseProto', 'generateX86RetailappReleaseProto'] as Set == + protobuf.generateProtoTasks.ofBuildType('release').collect({ it.name }) as Set + + assert ['generateX86FreeappDebugAndroidTestProto'] as Set == + protobuf.generateProtoTasks.ofVariant('x86FreeappDebugAndroidTest').collect({ it.name }) as Set + // "androidTest" sourceSet is not a flavor - org.junit.Assert.assertEquals( - [] as Set, - protobuf.generateProtoTasks.ofFlavor('androidTest').collect({ it.name }) as Set) - android.applicationVariants.each { variant -> + assert [] as Set == protobuf.generateProtoTasks.ofFlavor('androidTest').collect({ it.name }) as Set + + android.applicationVariants.each { variant -> assertJavaCompileHasProtoGeneratedDir(variant, ['javanano', 'grpc']) } + android.testVariants.each { variant -> assertJavaCompileHasProtoGeneratedDir(variant, ['javanano']) } diff --git a/testProjectCustomProtoDir/build.gradle b/testProjectCustomProtoDir/build.gradle index 5dc3bd70..70ccda5c 100644 --- a/testProjectCustomProtoDir/build.gradle +++ b/testProjectCustomProtoDir/build.gradle @@ -4,6 +4,10 @@ apply plugin: 'java' apply plugin: 'com.google.protobuf' +repositories { + maven { url "https://plugins.gradle.org/m2/" } +} + sourceSets { main { proto { @@ -40,7 +44,6 @@ task printDeps(dependsOn: build) << { sourceSets.each { println it.getCompileTaskName("proto") } sourceSets.each { println it.getCompileTaskName("java") } sourceSets.each { println it } - println tasks['generateProto'].source println tasks['compileJava'].source println project.buildDir println project.buildDir.path diff --git a/testProjectDependent/build.gradle b/testProjectDependent/build.gradle index 5990a78e..892aae6c 100644 --- a/testProjectDependent/build.gradle +++ b/testProjectDependent/build.gradle @@ -7,6 +7,10 @@ apply plugin: 'java' apply plugin: 'com.google.protobuf' +repositories { + maven { url "https://plugins.gradle.org/m2/" } +} + dependencies { compile 'com.google.protobuf:protobuf-java:3.0.0-alpha-3' compile project(':testProject') @@ -23,5 +27,5 @@ test.doLast { // other proto files from dependencies. def generatedFiles = project.fileTree(protobuf.generatedFilesBaseDir + "/main") File onlyGeneratedFile = generatedFiles.singleFile - org.junit.Assert.assertEquals('Dependent.java', onlyGeneratedFile.name) + assert 'Dependent.java' == onlyGeneratedFile.name } From 7f6036b601e1b4435082bbaaec037feab25878a2 Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Sun, 24 Jul 2016 19:36:31 +0800 Subject: [PATCH 26/27] Update tests indentation --- .../plugins/ProtobufAndroidPluginTest.groovy | 28 +-- .../plugins/ProtobufJavaPluginTest.groovy | 192 +++++++++--------- .../plugins/ProtobufPluginTestHelper.groovy | 76 +++---- 3 files changed, 149 insertions(+), 147 deletions(-) diff --git a/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy b/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy index c0b624f0..427d01f7 100644 --- a/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy +++ b/src/test/groovy/com/google/plugins/ProtobufAndroidPluginTest.groovy @@ -12,21 +12,21 @@ import spock.lang.Specification class ProtobufAndroidPluginTest extends Specification { - @Rule - final TemporaryFolder tempDir = new TemporaryFolder() + @Rule + final TemporaryFolder tempDir = new TemporaryFolder() - def "testProjectAndroid should be successfully executed"() { - given: "project from testProject & testProjectAndroid" - def mainProjectDir = tempDir.newFolder('test') - ProtobufPluginTestHelper.copyTestProjects(mainProjectDir, 'testProject', 'testProjectAndroid') + def "testProjectAndroid should be successfully executed"() { + given: "project from testProject & testProjectAndroid" + def mainProjectDir = tempDir.newFolder('test') + ProtobufPluginTestHelper.copyTestProjects(mainProjectDir, 'testProject', 'testProjectAndroid') - when: "build is invoked" - def result = GradleRunner.create() - .withProjectDir(mainProjectDir) - .withArguments('testProjectAndroid:build') - .build() + when: "build is invoked" + def result = GradleRunner.create() + .withProjectDir(mainProjectDir) + .withArguments('testProjectAndroid:build') + .build() - then: "it succeed" - result.task(":testProjectAndroid:build").outcome == TaskOutcome.SUCCESS - } + then: "it succeed" + result.task(":testProjectAndroid:build").outcome == TaskOutcome.SUCCESS + } } \ No newline at end of file diff --git a/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy b/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy index fb79fccb..4a3fb5f7 100644 --- a/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy +++ b/src/test/groovy/com/google/plugins/ProtobufJavaPluginTest.groovy @@ -12,102 +12,102 @@ import spock.lang.Specification class ProtobufJavaPluginTest extends Specification { - @Rule - final TemporaryFolder tempDir = new TemporaryFolder() - - private Project setupBasicProject() { - Project project = ProjectBuilder.builder().build() - project.apply plugin: 'java' - project.apply plugin: 'com.google.protobuf' - return project - } - - def "Applying java and com.google.protobuf adds corresponding task to project"() { - given: "a basic project with java and com.google.protobuf" - def project = setupBasicProject() - - when: "project evaluated" - project.evaluate() - - then: "generate tasks added" - assert project.tasks.generateProto instanceof GenerateProtoTask - assert project.tasks.generateTestProto instanceof GenerateProtoTask - - assert project.tasks.extractIncludeProto instanceof ProtobufExtract - assert project.tasks.extractIncludeTestProto instanceof ProtobufExtract - assert project.tasks.extractProto instanceof ProtobufExtract - assert project.tasks.extractTestProto instanceof ProtobufExtract - } - - def "Custom sourceSet should get its own GenerateProtoTask"() { - given: "a basic project with java and com.google.protobuf" - def project = setupBasicProject() - - when: "adding custom sourceSet nano" - project.sourceSets.create('nano') - - and: "project evaluated" - project.evaluate() - - then: "tasks for nano added" - assert project.tasks.generateNanoProto instanceof GenerateProtoTask - - assert project.tasks.extractIncludeNanoProto instanceof ProtobufExtract - assert project.tasks.extractNanoProto instanceof ProtobufExtract - } - - def "testProject should be successfully executed"() { - given: "project from testProject" - def projectDir = tempDir.newFolder() - ProtobufPluginTestHelper.copyTestProject(projectDir, 'testProject') - - when: "build is invoked" - def result = GradleRunner.create() - .withProjectDir(projectDir) - .withArguments('build') - .build() - - then: "it succeed" - result.task(":build").outcome == TaskOutcome.SUCCESS - ['grpc', 'grpc_nano', 'main', 'nano', 'test'].each { - def generatedSrcDir = new File(projectDir.path, "build/generated/source/proto/$it") - def fileList = [] - generatedSrcDir.eachFileRecurse { file -> - if (file.path.endsWith('.java')) { - fileList.add (file) - } - } - assert fileList.size > 0 + @Rule + final TemporaryFolder tempDir = new TemporaryFolder() + + private Project setupBasicProject() { + Project project = ProjectBuilder.builder().build() + project.apply plugin: 'java' + project.apply plugin: 'com.google.protobuf' + return project + } + + def "Applying java and com.google.protobuf adds corresponding task to project"() { + given: "a basic project with java and com.google.protobuf" + def project = setupBasicProject() + + when: "project evaluated" + project.evaluate() + + then: "generate tasks added" + assert project.tasks.generateProto instanceof GenerateProtoTask + assert project.tasks.generateTestProto instanceof GenerateProtoTask + + assert project.tasks.extractIncludeProto instanceof ProtobufExtract + assert project.tasks.extractIncludeTestProto instanceof ProtobufExtract + assert project.tasks.extractProto instanceof ProtobufExtract + assert project.tasks.extractTestProto instanceof ProtobufExtract + } + + def "Custom sourceSet should get its own GenerateProtoTask"() { + given: "a basic project with java and com.google.protobuf" + def project = setupBasicProject() + + when: "adding custom sourceSet nano" + project.sourceSets.create('nano') + + and: "project evaluated" + project.evaluate() + + then: "tasks for nano added" + assert project.tasks.generateNanoProto instanceof GenerateProtoTask + + assert project.tasks.extractIncludeNanoProto instanceof ProtobufExtract + assert project.tasks.extractNanoProto instanceof ProtobufExtract + } + + def "testProject should be successfully executed"() { + given: "project from testProject" + def projectDir = tempDir.newFolder() + ProtobufPluginTestHelper.copyTestProject(projectDir, 'testProject') + + when: "build is invoked" + def result = GradleRunner.create() + .withProjectDir(projectDir) + .withArguments('build') + .build() + + then: "it succeed" + result.task(":build").outcome == TaskOutcome.SUCCESS + ['grpc', 'grpc_nano', 'main', 'nano', 'test'].each { + def generatedSrcDir = new File(projectDir.path, "build/generated/source/proto/$it") + def fileList = [] + generatedSrcDir.eachFileRecurse { file -> + if (file.path.endsWith('.java')) { + fileList.add (file) } + } + assert fileList.size > 0 } - - def "testProjectDependent should be successfully executed"() { - given: "project from testProject & testProjectDependent" - def mainProjectDir = tempDir.newFolder() - ProtobufPluginTestHelper.copyTestProjects(mainProjectDir, 'testProject', 'testProjectDependent') - - when: "build is invoked" - def result = GradleRunner.create() - .withProjectDir(mainProjectDir) - .withArguments('testProjectDependent:build') - .build() - - then: "it succeed" - result.task(":testProjectDependent:build").outcome == TaskOutcome.SUCCESS - } - - def "testProjectCustomProtoDir should be successfully executed"() { - given: "project from testProjectCustomProtoDir" - def projectDir = tempDir.newFolder() - ProtobufPluginTestHelper.copyTestProject(projectDir, 'testProjectCustomProtoDir', ) - - when: "build is invoked" - def result = GradleRunner.create() - .withProjectDir(projectDir) - .withArguments('build') - .build() - - then: "it succeed" - result.task(":build").outcome == TaskOutcome.SUCCESS - } + } + + def "testProjectDependent should be successfully executed"() { + given: "project from testProject & testProjectDependent" + def mainProjectDir = tempDir.newFolder() + ProtobufPluginTestHelper.copyTestProjects(mainProjectDir, 'testProject', 'testProjectDependent') + + when: "build is invoked" + def result = GradleRunner.create() + .withProjectDir(mainProjectDir) + .withArguments('testProjectDependent:build') + .build() + + then: "it succeed" + result.task(":testProjectDependent:build").outcome == TaskOutcome.SUCCESS + } + + def "testProjectCustomProtoDir should be successfully executed"() { + given: "project from testProjectCustomProtoDir" + def projectDir = tempDir.newFolder() + ProtobufPluginTestHelper.copyTestProject(projectDir, 'testProjectCustomProtoDir', ) + + when: "build is invoked" + def result = GradleRunner.create() + .withProjectDir(projectDir) + .withArguments('build') + .build() + + then: "it succeed" + result.task(":build").outcome == TaskOutcome.SUCCESS + } } \ No newline at end of file diff --git a/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy b/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy index 935d92bd..e39c466e 100644 --- a/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy +++ b/src/test/groovy/com/google/plugins/ProtobufPluginTestHelper.groovy @@ -2,49 +2,51 @@ import org.apache.commons.io.FileUtils class ProtobufPluginTestHelper { - static void appendPluginClasspath(File buildFile) { - def pluginClasspathResource = ProtobufPluginTestHelper.class.classLoader.findResource("plugin-classpath.txt") - if (pluginClasspathResource == null) { - throw new IllegalStateException("Did not find plugin classpath resource, run `testClasses` build task.") - } - - def pluginClasspath = pluginClasspathResource.readLines() - .collect { it.replace('\\', '\\\\') } // escape backslashes in Windows paths - .collect { "'$it'" } - .join(", ") - - // Add the logic under test to the test build - buildFile << """ - buildscript { - dependencies { - classpath files($pluginClasspath) - } - } - """ + static void appendPluginClasspath(File buildFile) { + def pluginClasspathResource = + ProtobufPluginTestHelper.class.classLoader.findResource("plugin-classpath.txt") + if (pluginClasspathResource == null) { + throw new IllegalStateException('Did not find plugin classpath resource, ' + + 'run `testClasses` build task.') } - static void copyTestProject(File projectDir, String testProjectName) { - def baseDir = new File(System.getProperty("user.dir"), testProjectName) + def pluginClasspath = pluginClasspathResource.readLines() + .collect { it.replace('\\', '\\\\') } // escape backslashes in Windows paths + .collect { "'$it'" } + .join(", ") - FileUtils.copyDirectory(baseDir, projectDir) + // Add the logic under test to the test build + buildFile << """ + buildscript { + dependencies { + classpath files($pluginClasspath) + } + } + """ + } - def buildFile = new File(projectDir.path, "build.gradle") - appendPluginClasspath(buildFile) - } + static void copyTestProject(File projectDir, String testProjectName) { + def baseDir = new File(System.getProperty("user.dir"), testProjectName) - static void copyTestProjects(File projectDir, String... testProjectNames) { - def settingsFile = new File(projectDir, 'settings.gradle') - settingsFile.createNewFile() + FileUtils.copyDirectory(baseDir, projectDir) - testProjectNames.each { - copyTestProject(new File(projectDir.path, it), it) - settingsFile << """ - include ':$it' - project(':$it').projectDir = "\$rootDir/$it" as File - """ - } + def buildFile = new File(projectDir.path, "build.gradle") + appendPluginClasspath(buildFile) + } - def buildFile = new File(projectDir, 'build.gradle') - buildFile.createNewFile() + static void copyTestProjects(File projectDir, String... testProjectNames) { + def settingsFile = new File(projectDir, 'settings.gradle') + settingsFile.createNewFile() + + testProjectNames.each { + copyTestProject(new File(projectDir.path, it), it) + settingsFile << """ + include ':$it' + project(':$it').projectDir = "\$rootDir/$it" as File + """ } + + def buildFile = new File(projectDir, 'build.gradle') + buildFile.createNewFile() + } } \ No newline at end of file From caa4c593ee61ef8b07b579efe941b1d7ee1dcdbd Mon Sep 17 00:00:00 2001 From: Peter Ng Date: Mon, 1 Aug 2016 21:40:47 +0800 Subject: [PATCH 27/27] Remove duplicate file --- .../gradle/plugins/ProtobufPluginBase.groovy | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100644 src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPluginBase.groovy diff --git a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPluginBase.groovy b/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPluginBase.groovy deleted file mode 100644 index c2c8a122..00000000 --- a/src/main/groovy/com/google/protobuf/gradle/plugins/ProtobufPluginBase.groovy +++ /dev/null @@ -1,43 +0,0 @@ -package com.google.protobuf.gradle.plugins - -import com.google.protobuf.gradle.ProtobufConvention -import org.gradle.api.Plugin -import org.gradle.api.Project - -class ProtobufBasePlugin implements Plugin { - - void apply(final Project project) { - def gv = project.gradle.gradleVersion =~ "(\\d*)\\.(\\d*).*" - if (!gv || !gv.matches() || gv.group(1).toInteger() != 2 || gv.group(2).toInteger() < 12) { - project.logger.error("You are using Gradle ${project.gradle.gradleVersion}: " - + " This version of the protobuf plugin works with Gradle version 2.12+") - } - - // Provides the osdetector extension - if (!project.plugins.hasPlugin('com.google.osdetector')) { - project.apply plugin: 'com.google.osdetector' - } - if (!project.convention.plugins.protobuf) { - project.convention.plugins.protobuf = new ProtobufConvention(project) - } - - 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() - - appliedPlugins.each { it.afterTaskAdded() } - - // 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.generateProtoTasks.all()) - } - } -} \ No newline at end of file