diff --git a/README.md b/README.md index 984c12c3..0ec66ba5 100644 --- a/README.md +++ b/README.md @@ -17,36 +17,27 @@ For more information about the Protobuf Compiler, please refer to [Google Developers Site](https://developers.google.com/protocol-buffers/docs/reference/java-generated?csw=1). ## Latest Version -The latest version is ``0.9.2``. It requires at least __Gradle 5.6__ and __Java 8__. -To use it with Groovy DSL: -```gradle -plugins { - id "com.google.protobuf" version "0.9.2" -} -``` +The latest version is ``0.10.0``. It requires at least __Gradle 5.6__ and __Java 8__. -## Development Version +
+ To use it with Groovy DSL -To try out the head version, you can download the source and build it -with ``./gradlew publishToMavenLocal -x test`` (we skip tests here because they -require Android SDK), then in `settings.gradle`: - -```gradle -pluginManagement { - repositories { - gradlePluginPortal() - mavenLocal() + ```gradle + plugins { + id "com.google.protobuf" version "0.10.0" } -} -``` + ``` +
-And in `build.gradle`: +
+ To use it with Kotlin DSL -```gradle -plugins { - id "com.google.protobuf" version "0.10.0-SNAPSHOT" -} -``` + ```gradle + plugins { + id("com.google.protobuf") version "0.10.0" + } + ``` +
## Examples @@ -65,6 +56,14 @@ individual projects. ## Adding the plugin to your project This plugin must work with either the Java plugin or the Android plugin. +## Important for gradle scripts written in Kotlin + +Add this import in each gradle script file, were `protobuf-gradle-plugin` is used. +`protobuf-gradle-plugin` contains hacks for scripts in kotlin and android plugin. +Import is not needed for gradle scripts in groovy. +``` +import com.google.protobuf.gradle.* +``` ## Configuring Protobuf compilation @@ -80,50 +79,111 @@ sourceSet. By default, it includes all ``*.proto`` files under ``src/$sourceSetName/proto``. You can customize it in the same way as you would customize the ``java`` sources. -**Java** projects: use the top-level ``sourceSet``: - -```gradle -sourceSets { - main { - proto { - // In addition to the default 'src/main/proto' - srcDir 'src/main/protobuf' - srcDir 'src/main/protocolbuffers' - // In addition to the default '**/*.proto' (use with caution). - // Using an extension other than 'proto' is NOT recommended, - // because when proto files are published along with class files, we can - // only tell the type of a file from its extension. - include '**/*.protodevel' - } - java { - ... +#### Java projects +Use the top-level `sourceSet` + +
+ To configure with Groovy DSL + + ```gradle + sourceSets { + main { + proto { + // In addition to the default "src/main/proto" + srcDir "src/main/protobuf" + srcDir "src/main/protocolbuffers" + + // In addition to the default "**/*.proto" (use with caution). + // Using an extension other than "proto" is NOT recommended, + // because when proto files are published along with class files, we can + // only tell the type of a file from its extension. + include "**/*.protodevel" + } + java { + ... + } } - } - test { - proto { - // In addition to the default 'src/test/proto' - srcDir 'src/test/protocolbuffers' + test { + proto { + // In addition to the default "src/test/proto" + srcDir "src/test/protocolbuffers" + } } } -} -``` + ``` +
-**Android** projects: use ``android.sourceSets``: +
+ To configure with Kotlin DSL -```gradle -android { + ```kotlin sourceSets { main { proto { - ... + // In addition to the default "src/main/proto" + srcDir("src/main/protobuf") + srcDir("src/main/protocolbuffers") + + // In addition to the default "**/*.proto" (use with caution). + // Using an extension other than "proto" is NOT recommended, + // because when proto files are published along with class files, we can + // only tell the type of a file from its extension. + include("**/*.protodevel") } java { ... } } + test { + proto { + // In addition to the default "src/test/proto" + srcDir("src/test/protocolbuffers") + } + } } -} -``` + ``` +
+ +#### Android projects +Use `android.sourceSets` + +
+ To configure with Groovy DSL + + ```gradle + android { + sourceSets { + main { + proto { + ... + } + java { + ... + } + } + } + } + ``` +
+ +
+ To configure with Kotlin DSL + + ```kotlin + android { + sourceSets { + main { + proto { + ... + } + java { + ... + } + } + } + } + ``` +
### Customizing Protobuf compilation The plugin adds a ``protobuf`` block to the project. It provides all the @@ -132,32 +192,70 @@ configuration knobs. #### Locate external executables -By default the plugin will search for the ``protoc`` executable in the system +By default, the plugin will search for the ``protoc`` executable in the system search path. We recommend you to take the advantage of pre-compiled ``protoc`` that we have published on Maven Central: -```gradle -protobuf { - ... - // Configure the protoc executable -  protoc { - // Download from repositories -    artifact = 'com.google.protobuf:protoc:3.0.0' +
+ To configure with Groovy DSL + + ```gradle + protobuf { + ... + // Configure the protoc executable + protoc { + // Download from repositories + artifact = 'com.google.protobuf:protoc:3.0.0' + } + ... } - ... -} -``` + ``` +
+ +
+ To configure with Kotlin DSL + + ```kotlin + protobuf { + ... + // Configure the protoc executable + protoc { + // Download from repositories + artifact = "com.google.protobuf:protoc:3.0.0" + } + ... + } + ``` +
You may also specify a local path. -```gradle -protobuf { - ... -  protoc { -    path = '/usr/local/bin/protoc' - } - ... -} -``` +
+ To configure with Groovy DSL + + ```gradle + protobuf { + ... + protoc { + path = '/usr/local/bin/protoc' + } + ... + } + ``` +
+ +
+ To configure with Kotlin DSL + + ```kotlin + protobuf { + ... + protoc { + path = "/usr/local/bin/protoc" + } + ... + } + ``` +
Multiple assignments are allowed in the ``protoc`` block. The last one wins. @@ -170,25 +268,55 @@ apply the plugins. You need to configure the tasks in the ``generateProtoTasks`` block introduced below to apply the plugins defined here. -```gradle -protobuf { - ... - // Locate the codegen plugins -  plugins { - // Locate a plugin with name 'grpc'. This step is optional. - // If you don't locate it, protoc will try to use "protoc-gen-grpc" from - // system search path. -    grpc { -      artifact = 'io.grpc:protoc-gen-grpc-java:1.0.0-pre2' -      // or -      // path = 'tools/protoc-gen-grpc-java' -    } - // Any other plugins +
+ To configure with Groovy DSL + + ```gradle + protobuf { ... -  } - ... -} -``` + // Locate the codegen plugins + plugins { + // Locate a plugin with name 'grpc'. This step is optional. + // If you don't locate it, protoc will try to use "protoc-gen-grpc" from + // system search path. + grpc { + artifact = 'io.grpc:protoc-gen-grpc-java:1.0.0-pre2' + // or + // path = 'tools/protoc-gen-grpc-java' + } + // Any other plugins + ... + } + ... + } + ``` +
+ +
+ To configure with Kotlin DSL + + ```kotlin + import com.google.protobuf.gradle.* // Important import, do not forget + + protobuf { + ... + // Locate the codegen plugins + plugins { + // Locate a plugin with name 'grpc'. This step is optional. + // If you don't locate it, protoc will try to use "protoc-gen-grpc" from + // system search path. + id("grpc") { + artifact = "io.grpc:protoc-gen-grpc-java:1.0.0-pre2" + // or + // path = "tools/protoc-gen-grpc-java" + } + // Any other plugins + ... + } + ... + } + ``` +
The syntax for `artifact` follows [Artifact Classifiers](https://docs.gradle.org/3.3/userguide/dependency_management.html#sub:classifiers) where the default classifier is `project.osdetector.classifier` (ie @@ -214,35 +342,103 @@ correctly by the plugin. because there are subtle timing constraints on when the tasks should be configured. -```gradle -protobuf { - ... -  generateProtoTasks { -    // all() returns the collection of all protoc tasks -    all().configureEach { task -> - // Here you can configure the task -    } - - // In addition to all(), you may select tasks by various criteria: - - // (Java-only) returns tasks for a sourceSet - ofSourceSet('main') - - // (Android-only selectors) - // Returns tasks for a flavor - ofFlavor('demo') - // Returns tasks for a buildType - ofBuildType('release') - // Returns tasks for a variant - ofVariant('demoRelease') - // Returns non-androidTest tasks - ofNonTest() - // Return androidTest tasks - ofTest() - } -} -``` +
+ To configure with Groovy DSL + ```gradle + protobuf { + generateProtoTasks { + // all {} runs over the of all protoc tasks + all { + // configure here + } + + // In addition to all {}, you may select tasks by various criteria: + + // (Java-only) returns tasks for a sourceSet + ofSourceSet('main') { + // configure here + } + + // (Android-only selectors) + // Returns tasks for a flavor + ofFlavor('demo') { + // configure here + } + + // Returns tasks for a buildType + ofBuildType('release') { + // configure here + } + + // Returns tasks for a variant + ofVariant('demoRelease') { + // configure here + } + + // Returns non-androidTest tasks + ofNonTest { + // configure here + } + + // Return androidTest tasks + ofTest { + // configure here + } + } + } + ``` +
+ +
+ To configure with Kotlin DSL + + ```kotlin + import com.google.protobuf.gradle.* // Important import, do not forget + + protobuf { + generateProtoTasks { + // all {} runs over the of all protoc tasks + all { + // configure here + } + + // In addition to all {}, you may select tasks by various criteria: + + // (Java-only) returns tasks for a sourceSet + ofSourceSet("main") { + // configure here + } + + // (Android-only selectors) + // Returns tasks for a flavor + ofFlavor("demo") { + // configure here + } + + // Returns tasks for a buildType + ofBuildType("release") { + // configure here + } + + // Returns tasks for a variant + ofVariant("demoRelease") { + // configure here + } + + // Returns non-androidTest tasks + ofNonTest { + // configure here + } + + // Return androidTest tasks + ofTest { + // configure here + } + } + } + ``` +
Each code generation task has two collections: - `builtins`: code generators built in `protoc`, e.g., `java`, `cpp`, @@ -257,34 +453,85 @@ Each code generation task has two collections: Code generation is done by protoc builtins and plugins. Each builtin/plugin generates a certain type of code. To add or configure a builtin/plugin on a task, list its name followed by a braces block. -Put options in the braces if wanted. For example: - -```gradle -task.builtins { - // This yields - // "--java_out=example_option1=true,example_option2:/path/to/output" - // on the protoc commandline, which is equivalent to - // "--java_out=/path/to/output --java_opt=example_option1=true,example_option2" - // with the latest version of protoc. - java { - option 'example_option1=true' - option 'example_option2' - } - // Add cpp output without any option. - // DO NOT omit the braces if you want this builtin to be added. - // This yields - // "--cpp_out=/path/to/output" on the protoc commandline. - cpp { } -} - -task.plugins { - // Add grpc output without any option. grpc must have been defined in the - // protobuf.plugins block. - // This yields - // "--grpc_out=/path/to/output" on the protoc commandline. - grpc { } -} -``` +Put options in the braces if wanted. + +For example: + +
+ To configure with Groovy DSL + + ```gradle + protobuf { + generateProtoTasks { + all { + builtins { + // This yields + // "--java_out=example_option1=true,example_option2:/path/to/output" + // on the protoc commandline, which is equivalent to + // "--java_out=/path/to/output --java_opt=example_option1=true,example_option2" + // with the latest version of protoc. + java { + option 'example_option1=true' + option 'example_option2' + } + // Add cpp output without any option. + // DO NOT omit the braces if you want this builtin to be added. + // This yields + // "--cpp_out=/path/to/output" on the protoc commandline. + cpp { } + } + + plugins { + // Add grpc output without any option. grpc must have been defined in the + // protobuf.plugins block. + // This yields + // "--grpc_out=/path/to/output" on the protoc commandline. + grpc { } + } + } + } + } + ``` +
+ +
+ To configure with Kotlin DSL + + ```kotlin + import com.google.protobuf.gradle.* // Important import, do not forget + + protobuf { + generateProtoTasks { + all { + builtins { + // This yields + // "--java_out=example_option1=true,example_option2:/path/to/output" + // on the protoc commandline, which is equivalent to + // "--java_out=/path/to/output --java_opt=example_option1=true,example_option2" + // with the latest version of protoc. + id("java") { + option("example_option1=true") + option("example_option2") + } + // Add cpp output without any option. + // DO NOT omit the braces if you want this builtin to be added. + // This yields + // "--cpp_out=/path/to/output" on the protoc commandline. + id("cpp") { } + } + + plugins { + // Add grpc output without any option. grpc must have been defined in the + // protobuf.plugins block. + // This yields + // "--grpc_out=/path/to/output" on the protoc commandline. + id("grpc") { } + } + } + } + } + ``` +
#### Default outputs @@ -292,21 +539,47 @@ task.plugins { **Python** output can be generated by adding the `python` builtin: -```gradle -protobuf { - generateProtoTasks { - all().configureEach { task -> - task.builtins { - // Generates Python code - python { } - - // If you wish to avoid generating Java files: - remove java +
+ To configure with Groovy DSL + + ```gradle + protobuf { + generateProtoTasks { + all { + builtins { + // Generates Python code + python { } + + // If you wish to avoid generating Java files: + remove java + } } } } -} -``` + ``` +
+ +
+ To configure with Kotlin DSL + + ```kotlin + import com.google.protobuf.gradle.* // Important import, do not forget + + protobuf { + generateProtoTasks { + all { + builtins { + // Generates Python code + id("python") { } + + // If you wish to avoid generating Java files: + remove("java") + } + } + } + } + ``` +
Note the generated Python code will not be included for compilation, you will need to add them as sources to Python's compilation tasks manually. @@ -323,87 +596,196 @@ provided as a protoc plugin ([protobuf-lite](http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22protobuf-lite%22)). Example: -```gradle - -dependencies { - // You need to depend on the lite runtime library, not protobuf-java - implementation 'com.google.protobuf:protobuf-lite:3.0.0' -} +
+ To configure with Groovy DSL -protobuf { -  protoc { - // You still need protoc like in the non-Android case -    artifact = 'com.google.protobuf:protoc:3.7.0' - } - plugins { - javalite { - // The codegen for lite comes as a separate artifact - artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0' + ```gradle + protobuf { + protoc { + // You still need protoc like in the non-Android case + artifact = 'com.google.protobuf:protoc:3.7.0' + } + plugins { + javalite { + // The codegen for lite comes as a separate artifact + artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0' + } } + generateProtoTasks { + all { + builtins { + // In most cases you don't need the full Java output + // if you use the lite output. + remove java + } + plugins { + javalite { } + } + } + } + } + + dependencies { + // You need to depend on the lite runtime library, not protobuf-java + implementation 'com.google.protobuf:protobuf-lite:3.0.0' } - generateProtoTasks { - all().configureEach { task -> - task.builtins { - // In most cases you don't need the full Java output - // if you use the lite output. - remove java + ``` +
+ +
+ To configure with Kotlin DSL + + ```kotlin + import com.google.protobuf.gradle.* // Important import, do not forget + + protobuf { + protoc { + // You still need protoc like in the non-Android case + artifact = "com.google.protobuf:protoc:3.7.0" + } + plugins { + id("javalite") { + // The codegen for lite comes as a separate artifact + artifact = "com.google.protobuf:protoc-gen-javalite:3.0.0" } - task.plugins { - javalite { } + } + generateProtoTasks { + all { + builtins { + // In most cases you don't need the full Java output + // if you use the lite output. + remove("java") + } + plugins { + id("javalite") { } + } } } } -} -``` + + dependencies { + // You need to depend on the lite runtime library, not protobuf-java + implementation("com.google.protobuf:protobuf-lite:3.0.0") + } + ``` +
Starting from Protobuf 3.8.0, lite code generation is built into protoc's "java" output. Example: -```gradle -dependencies { - // You need to depend on the lite runtime library, not protobuf-java - implementation 'com.google.protobuf:protobuf-javalite:3.8.0' -} +
+ To configure with Groovy DSL -protobuf { - protoc { - artifact = 'com.google.protobuf:protoc:3.8.0' - } - generateProtoTasks { - all().configureEach { task -> - task.builtins { - java { - option "lite" + ```gradle + protobuf { + protoc { + artifact = 'com.google.protobuf:protoc:3.8.0' + } + generateProtoTasks { + all { + builtins { + java { + option "lite" + } } } } } -} -``` + + dependencies { + // You need to depend on the lite runtime library, not protobuf-java + implementation 'com.google.protobuf:protobuf-javalite:3.8.0' + } + ``` +
+ +
+ To configure with Kotlin DSL + ```kotlin + import com.google.protobuf.gradle.* // Important import, do not forget + + protobuf { + protoc { + artifact = "com.google.protobuf:protoc:3.8.0" + } + generateProtoTasks { + all { + builtins { + id("java") { + option("lite") + } + } + } + } + } + + dependencies { + // You need to depend on the lite runtime library, not protobuf-java + implementation("com.google.protobuf:protobuf-javalite:3.8.0") + } + ``` +
#### Generate descriptor set files -```gradle -{ task -> - // If true, will generate a descriptor_set.desc file under - // task.outputBaseDir. Default is false. - // See --descriptor_set_out in protoc documentation about what it is. - task.generateDescriptorSet = true - - // Allows to override the default for the descriptor set location - task.descriptorSetOptions.path = - "${projectDir}/build/descriptors/${task.sourceSet.name}.dsc" - - // If true, the descriptor set will contain line number information - // and comments. Default is false. - task.descriptorSetOptions.includeSourceInfo = true - - // If true, the descriptor set will contain all transitive imports and - // is therefore self-contained. Default is false. - task.descriptorSetOptions.includeImports = true -} -``` +
+ To configure with Groovy DSL + + ```gradle + protobuf { + generateProtoTasks { + all { + // If true, will generate a descriptor_set.desc file under + // task.outputBaseDir. Default is false. + // See --descriptor_set_out in protoc documentation about what it is. + generateDescriptorSet = true + + // Allows to override the default for the descriptor set location + descriptorSetOptions.path = "${projectDir}/build/descriptors/${variantName}.dsc" + + // If true, the descriptor set will contain line number information + // and comments. Default is false. + descriptorSetOptions.includeSourceInfo = true + + // If true, the descriptor set will contain all transitive imports and + // is therefore self-contained. Default is false. + descriptorSetOptions.includeImports = true + } + } + } + ``` +
+ +
+ To configure with Kotlin DSL + + ```kotlin + import com.google.protobuf.gradle.* // Important import, do not forget + + protobuf { + generateProtoTasks { + all { + // If true, will generate a descriptor_set.desc file under + // task.outputBaseDir. Default is false. + // See --descriptor_set_out in protoc documentation about what it is. + generateDescriptorSet = true + + // Allows to override the default for the descriptor set location + descriptorSetOptions.path = "${projectDir}/build/descriptors/${variantName}.dsc" + + // If true, the descriptor set will contain line number information + // and comments. Default is false. + descriptorSetOptions.includeSourceInfo = true + + // If true, the descriptor set will contain all transitive imports and + // is therefore self-contained. Default is false. + descriptorSetOptions.includeImports = true + } + } + } + ``` +
#### Change where files are generated @@ -416,16 +798,45 @@ changed by setting the ``outputSubDir`` property in the ``builtins`` or ``plugins`` block of a task configuration within ``generateProtoTasks`` block (see previous section). E.g., -```gradle -{ task -> - task.plugins { - grpc { - // Use subdirectory 'grpcjava' instead of the default 'grpc' - outputSubDir = 'grpcjava' +
+ To configure with Groovy DSL + + ```gradle + protobuf { + generateProtoTasks { + all { + plugins { + grpc { + // Use subdirectory 'grpcjava' instead of the default 'grpc' + outputSubDir = "grpcjava" + } + } + } } } -} -``` + ``` +
+ +
+ To configure with Kotlin DSL + + ```kotlin + import com.google.protobuf.gradle.* // Important import, do not forget + + protobuf { + generateProtoTasks { + all { + plugins { + id("grpc") { + // Use subdirectory 'grpcjava' instead of the default 'grpc' + outputSubDir = "grpcjava" + } + } + } + } + } + ``` +
### Protos in dependencies @@ -441,28 +852,63 @@ flag of the protoc command line, so that they can be imported by the proto files of the current project. The imported proto files will not be compiled since they have already been compiled in their own projects. Example: -```gradle -dependencies { - implementation project(':someProjectWithProtos') - testImplementation files("lib/some-testlib-with-protos.jar") -} -``` +
+ To configure with Groovy DSL + + ```gradle + dependencies { + implementation project(':someProjectWithProtos') + testImplementation files('lib/some-testlib-with-protos.jar') + } + ``` +
+ +
+ To configure with Kotlin DSL + + ```kotlin + dependencies { + implementation(project(":someProjectWithProtos")) + testImplementation(files("lib/some-testlib-with-protos.jar")) + } + ``` +
If the dependency is put in the ``protobuf`` configuration, the proto files are extracted to a ``extracted-protos`` directory and added to the protoc command line as files to compile, in the same protoc invocation as the current project's proto files (if any). Example: -```gradle -dependencies { - // protos can be from a local package, - protobuf files('lib/protos.tar.gz') - // ... a local directory, - protobuf files('ext/') // NEVER use fileTree(). See issue #248. - // ... or an artifact from a repository - testProtobuf 'com.example:published-protos:1.0.0' -} -``` +
+ To configure with Groovy DSL + + ```gradle + dependencies { + // protos can be from a local package, + protobuf files('lib/protos.tar.gz') + // ... a local directory, + protobuf files('ext/') // NEVER use fileTree(). See issue #248. + // ... or an artifact from a repository + testProtobuf 'com.example:published-protos:1.0.0' + } + ``` +
+ +
+ To configure with Kotlin DSL + + ```kotlin + dependencies { + // protos can be from a local package, + protobuf(files("lib/protos.tar.gz")) + // ... a local directory, + protobuf(files("ext/")) // NEVER use fileTree(). See issue #248. + // ... or an artifact from a repository + testProtobuf("com.example:published-protos:1.0.0") + } + ``` +
+ ## Pre-compiled ``protoc`` artifacts This [Maven Central directory](https://repo1.maven.org/maven2/com/google/protobuf/protoc/) @@ -500,3 +946,51 @@ After you made any change to the plugin, be sure to run these tests. ``` $ ./gradlew test ``` + +## Development Version + +To try out the head version, you can download the source and build it +with ``./gradlew publishToMavenLocal -x test`` (we skip tests here because they +require Android SDK), then add `mavenLocal` repository. + +
+ To configure with Groovy DSL + +Add in `settings.gradle`: + ```gradle + pluginManagement { + repositories { + gradlePluginPortal() + mavenLocal() + } + } + ``` + +And in `build.gradle`: + ```gradle + plugins { + id "com.google.protobuf" version "0.11.0-SNAPSHOT" + } + ``` +
+ +
+ To configure with Kotlin DSL + +Add in `settings.gradle.kts`: + ```kotlin + pluginManagement { + repositories { + gradlePluginPortal() + mavenLocal() + } + } + ``` + +And in `build.gradle.kts`: + ```kotlin + plugins { + id("com.google.protobuf") version "0.11.0-SNAPSHOT" + } + ``` +
diff --git a/build.gradle b/build.gradle index 6e9e027f..787b6819 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ configurations { } dependencies { - compileOnly "com.android.tools.build:gradle:4.1.0" + compileOnly "com.android.tools.build:gradle:4.2.2" compileOnly "org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin:1.7.22" implementation 'com.google.gradle:osdetector-gradle-plugin:1.7.2' diff --git a/examples/exampleKotlinDslProject/build.gradle.kts b/examples/exampleKotlinDslProject/build.gradle.kts index 75451acf..81576145 100644 --- a/examples/exampleKotlinDslProject/build.gradle.kts +++ b/examples/exampleKotlinDslProject/build.gradle.kts @@ -46,8 +46,8 @@ protobuf { } } generateProtoTasks { - ofSourceSet("main").forEach { - it.plugins { + ofSourceSet("main") { + plugins { // Apply the "grpc" plugin whose spec is defined above, without // options. Note the braces cannot be omitted, otherwise the // plugin will not be added. This is because of the implicit way diff --git a/examples/exampleProject/build.gradle b/examples/exampleProject/build.gradle index 4fd561f1..9c3561a3 100644 --- a/examples/exampleProject/build.gradle +++ b/examples/exampleProject/build.gradle @@ -44,7 +44,7 @@ protobuf { } } generateProtoTasks { - ofSourceSet('main').configureEach { + ofSourceSet('main') { plugins { // Apply the "grpc" plugin whose spec is defined above, without // options. Note the braces cannot be omitted, otherwise the diff --git a/src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy b/src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy index c38506c9..ab258430 100644 --- a/src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy @@ -31,19 +31,19 @@ package com.google.protobuf.gradle import static java.nio.charset.StandardCharsets.US_ASCII +import com.google.protobuf.gradle.internal.PluginSpecExt +import com.google.protobuf.gradle.internal.DefaultGenerateProtoTaskSpec +import com.google.protobuf.gradle.tasks.GenerateProtoTaskSpec +import com.google.protobuf.gradle.tasks.PluginSpec +import org.gradle.api.file.DeleteSpec +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Nested import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import groovy.transform.TypeChecked -import groovy.transform.TypeCheckingMode -import org.gradle.api.Action import org.gradle.api.DefaultTask import org.gradle.api.GradleException -import org.gradle.api.Named -import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.FileCollection import org.gradle.api.file.ProjectLayout -import org.gradle.api.file.SourceDirectorySet import org.gradle.api.logging.LogLevel import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Provider @@ -53,17 +53,10 @@ import org.gradle.api.tasks.IgnoreEmptyDirectories import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.Nested -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.SkipWhenEmpty -import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.TaskAction - -import javax.annotation.Nullable import javax.inject.Inject /** @@ -72,7 +65,7 @@ import javax.inject.Inject // TODO(zhangkun83): add per-plugin output dir reconfiguraiton. @CompileStatic @CacheableTask -public abstract class GenerateProtoTask extends DefaultTask { +abstract class GenerateProtoTask extends DefaultTask { // Windows CreateProcess has command line limit of 32768: // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx static final int WINDOWS_CMD_LENGTH_LIMIT = 32760 @@ -90,76 +83,14 @@ public abstract class GenerateProtoTask extends DefaultTask { private final ConfigurableFileCollection includeDirs = objectFactory.fileCollection() // source files are proto files that will be compiled by protoc private final ConfigurableFileCollection sourceDirs = objectFactory.fileCollection() - private final NamedDomainObjectContainer builtins = objectFactory.domainObjectContainer(PluginOptions) - private final NamedDomainObjectContainer plugins = objectFactory.domainObjectContainer(PluginOptions) private final ProjectLayout projectLayout = project.layout private final ToolsLocator toolsLocator = project.extensions.findByType(ProtobufExtension).tools - // These fields are set by the Protobuf plugin only when initializing the - // task. Ideally they should be final fields, but Gradle task cannot have - // constructor arguments. We use the initializing flag to prevent users from - // accidentally modifying them. - private Provider outputBaseDir - // Tags for selectors inside protobuf.generateProtoTasks; do not serialize with Gradle configuration caching - @SuppressWarnings("UnnecessaryTransientModifier") // It is not necessary for task to implement Serializable - transient private SourceSet sourceSet - @SuppressWarnings("UnnecessaryTransientModifier") // It is not necessary for task to implement Serializable - transient private Object variant - private List flavors - private String buildType - private boolean isTestVariant - private final Provider isAndroidProject = providerFactory.provider { Utils.isAndroidProject(project) } - private final Provider isTestProvider = providerFactory.provider { - if (Utils.isAndroidProject(project)) { - return isTestVariant - } - return Utils.isTest(sourceSet.name) + @SuppressWarnings("AbstractClassWithPublicConstructor") // required to configure properties convention values + GenerateProtoTask() { + this.spec.convention(new DefaultGenerateProtoTaskSpec(name, objectFactory)) } - /** - * If true, will set the protoc flag - * --descriptor_set_out="${outputBaseDir}/descriptor_set.desc" - * - * Default: false - */ - @Internal("Handled as input via getDescriptorSetOptionsForCaching()") - boolean generateDescriptorSet - - /** - * Configuration object for descriptor generation details. - */ - public class DescriptorSetOptions { - /** - * If set, specifies an alternative location than the default for storing the descriptor - * set. - * - * Default: null - */ - @Nullable - @Optional - @OutputFile - String path - - /** - * If true, source information (comments, locations) will be included in the descriptor set. - * - * Default: false - */ - @Input - boolean includeSourceInfo - - /** - * If true, imports are included in the descriptor set, such that it is self-containing. - * - * Default: false - */ - @Input - boolean includeImports - } - - @Internal("Handled as input via getDescriptorSetOptionsForCaching()") - final DescriptorSetOptions descriptorSetOptions = new DescriptorSetOptions() - // protoc allows you to prefix comma-delimited options to the path in // the --*_out flags, e.g., // - Without options: --java_out=/path/to/output @@ -251,54 +182,6 @@ public abstract class GenerateProtoTask extends DefaultTask { return java.path } - void setOutputBaseDir(Provider outputBaseDir) { - checkInitializing() - Preconditions.checkState(this.outputBaseDir == null, 'outputBaseDir is already set') - this.outputBaseDir = outputBaseDir - } - - @OutputDirectory - String getOutputBaseDir() { - return outputBaseDir.get() - } - - void setSourceSet(SourceSet sourceSet) { - checkInitializing() - Preconditions.checkState(!isAndroidProject.get(), - 'sourceSet should not be set in an Android project') - this.sourceSet = sourceSet - } - - void setVariant(Object variant, boolean isTestVariant) { - checkInitializing() - Preconditions.checkState(isAndroidProject.get(), - 'variant should not be set in a Java project') - this.variant = variant - this.isTestVariant = isTestVariant - } - - void setFlavors(List flavors) { - checkInitializing() - Preconditions.checkState(isAndroidProject.get(), - 'flavors should not be set in a Java project') - this.flavors = Collections.unmodifiableList(new ArrayList(flavors)) - } - - void setBuildType(String buildType) { - checkInitializing() - Preconditions.checkState(isAndroidProject.get(), - 'buildType should not be set in a Java project') - this.buildType = buildType - } - - @Internal("Inputs tracked in getSourceDirs()") - SourceSet getSourceSet() { - Preconditions.checkState(!isAndroidProject.get(), - 'sourceSet should not be used in an Android project') - Preconditions.checkNotNull(sourceSet, 'sourceSet is not set') - return sourceSet - } - @SkipWhenEmpty @PathSensitive(PathSensitivity.RELATIVE) @IgnoreEmptyDirectories @@ -313,14 +196,6 @@ public abstract class GenerateProtoTask extends DefaultTask { return includeDirs } - @Internal("Not an actual input to the task, only used to find tasks belonging to a variant") - Object getVariant() { - Preconditions.checkState(isAndroidProject.get(), - 'variant should not be used in a Java project') - Preconditions.checkNotNull(variant, 'variant is not set') - return variant - } - /** * Not for external use. Used to expose inputs to Gradle. * @@ -355,58 +230,18 @@ public abstract class GenerateProtoTask extends DefaultTask { } private List getAllExecutableLocators() { - [toolsLocator.protoc] + plugins.collect { PluginOptions it -> toolsLocator.plugins.getByName(it.name) } - } - - @Internal("Not an actual input to the task, only used to find tasks belonging to a variant") - Provider getIsAndroidProject() { - return isAndroidProject - } - - @Internal("Not an actual input to the task, only used to find tasks belonging to a variant") - boolean getIsTestVariant() { - Preconditions.checkState(isAndroidProject.get(), - 'isTestVariant should not be used in a Java project') - Preconditions.checkNotNull(variant, 'variant is not set') - return isTestVariant - } - - @Internal("Not an actual input to the task, only used to find tasks belonging to a variant") - List getFlavors() { - Preconditions.checkState(isAndroidProject.get(), - 'flavors should not be used in a Java project') - Preconditions.checkNotNull(flavors, 'flavors is not set') - return flavors - } - - @TypeChecked(TypeCheckingMode.SKIP) // Don't depend on AGP - @Internal("Not an actual input to the task, only used to find tasks belonging to a variant") - String getBuildType() { - Preconditions.checkState(isAndroidProject.get(), - 'buildType should not be used in a Java project') - Preconditions.checkState( - variant.name == 'test' || buildType, - 'buildType is not set and task is not for local unit test variant') - return buildType - } - - void doneInitializing() { - Preconditions.checkState(state == State.INIT, "Invalid state: ${state}") - state = State.CONFIG - } - - void doneConfig() { - Preconditions.checkState(state == State.CONFIG, "Invalid state: ${state}") - state = State.FINALIZED + [toolsLocator.protoc] + requireSpec().plugins.collect { PluginSpec it -> toolsLocator.plugins.getByName(it.name) } } @Internal("Tracked as an input via getDescriptorSetOptionsForCaching()") String getDescriptorPath() { - if (!generateDescriptorSet) { + GenerateProtoTaskSpec spec = requireSpec() + if (!spec.generateDescriptorSet) { throw new IllegalStateException( "requested descriptor path but descriptor generation is off") } - return descriptorSetOptions.path != null ? descriptorSetOptions.path : "${outputBaseDir.get()}/descriptor_set.desc" + return spec.descriptorSetOptions.path != null ? spec.descriptorSetOptions.path + : "${spec.outputDir.get()}/descriptor_set.desc" } @Inject @@ -415,58 +250,17 @@ public abstract class GenerateProtoTask extends DefaultTask { @Inject abstract ObjectFactory getObjectFactory() + @Nested + abstract Property getSpec() + //=========================================================================== // Configuration methods //=========================================================================== - /** - * Configures the protoc builtins in a closure, which will be manipulating a - * NamedDomainObjectContainer. - */ - public void builtins(Action> configureAction) { - checkCanConfig() - configureAction.execute(this.builtins) - } - - /** - * Returns the container of protoc builtins. - */ - @Internal("Tracked as an input via getBuiltinsForCaching()") - public NamedDomainObjectContainer getBuiltins() { - checkCanConfig() - return builtins - } - - /** - * Configures the protoc plugins in a closure, which will be maniuplating a - * NamedDomainObjectContainer. - */ - public void plugins(Action> configureAction) { - checkCanConfig() - configureAction.execute(this.plugins) - } - - /** - * Returns the container of protoc plugins. - */ - @Internal("Tracked as an input via getPluginsForCaching()") - public NamedDomainObjectContainer getPlugins() { - checkCanConfig() - return plugins - } - - /** - * Returns true if the task has a plugin with the given name, false otherwise. - */ - public boolean hasPlugin(String name) { - return plugins.findByName(name) != null - } - /** * Add a directory to protoc's include path. */ public void addIncludeDir(FileCollection dir) { - checkCanConfig() includeDirs.from(dir) } @@ -474,132 +268,26 @@ public abstract class GenerateProtoTask extends DefaultTask { * Add a collection of proto source files to be compiled. */ public void addSourceDirs(FileCollection dirs) { - checkCanConfig() sourceDirs.from(dirs) } - /** - * Returns true if the Java source set or Android variant is test related. - */ - @Input - public boolean getIsTest() { - return isTestProvider.get() - } - - @Internal("Already captured with getIsTest()") - Provider getIsTestProvider() { - return isTestProvider - } - - /** - * The container of command-line options for a protoc plugin or a built-in output. - */ - public static class PluginOptions implements Named { - private final List options = [] - private final String name - private String outputSubDir - - public PluginOptions(String name) { - this.name = name - } - - /** - * Adds a plugin option. - */ - public PluginOptions option(String option) { - options.add(option) - return this - } - - @Input - public List getOptions() { - return options - } - - /** - * Returns the name of the plugin or builtin. - */ - @Input - @Override - public String getName() { - return name - } - - /** - * Set the output directory for this plugin, relative to {@link GenerateProtoTask#outputBaseDir}. - */ - void setOutputSubDir(String outputSubDir) { - this.outputSubDir = outputSubDir - } - - /** - * Returns the relative outputDir for this plugin. If outputDir is not specified, name is used. - */ - @Input - public String getOutputSubDir() { - if (outputSubDir != null) { - return outputSubDir - } - return name - } - } - //=========================================================================== // protoc invocation logic //=========================================================================== - String getOutputDir(PluginOptions plugin) { - return "${outputBaseDir.get()}/${plugin.outputSubDir}" - } - - /** - * Returns a {@code SourceDirectorySet} representing the generated source - * directories. - */ - @Internal - @Deprecated - SourceDirectorySet getOutputSourceDirectorySet() { - String srcSetName = "generate-proto-" + name - SourceDirectorySet srcSet - srcSet = objectFactory.sourceDirectorySet(srcSetName, srcSetName) - srcSet.srcDirs objectFactory.fileCollection().builtBy(this).from(providerFactory.provider { - getOutputSourceDirectories() - }) - return srcSet - } - - @Internal - @PackageScope - Collection getOutputSourceDirectories() { - Collection srcDirs = [] - builtins.each { builtin -> - File dir = new File(getOutputDir(builtin)) - if (!dir.name.endsWith(".zip") && !dir.name.endsWith(".jar")) { - srcDirs.add(dir) - } - } - plugins.each { plugin -> - File dir = new File(getOutputDir(plugin)) - if (!dir.name.endsWith(".zip") && !dir.name.endsWith(".jar")) { - srcDirs.add(dir) - } - } - return srcDirs - } - @TaskAction void compile() { - Preconditions.checkState(state == State.FINALIZED, 'doneConfig() has not been called') + GenerateProtoTaskSpec spec = requireSpec() - copyActionFacade.delete { spec -> - spec.delete(outputBaseDir) + copyActionFacade.delete { DeleteSpec deleteSpec -> + deleteSpec.delete(spec.outputDir) } // Sort to ensure generated descriptors have a canonical representation // to avoid triggering unnecessary rebuilds downstream List protoFiles = sourceDirs.asFileTree.files.sort() - [builtins, plugins]*.forEach { PluginOptions plugin -> - String outputPath = getOutputDir(plugin) + [spec.builtins, spec.plugins]*.forEach { PluginSpec plugin -> + String outputPath = PluginSpecExt.getOutputDir(plugin, spec.outputDir.get()) File outputDir = new File(outputPath) // protoc is capable of output generated files directly to a JAR file // or ZIP archive if the output location ends with .jar/.zip @@ -621,14 +309,15 @@ public abstract class GenerateProtoTask extends DefaultTask { baseCmd.addAll(dirs) // Handle code generation built-ins - builtins.each { builtin -> + spec.builtins.each { builtin -> String outPrefix = makeOptionsPrefix(builtin.options) - baseCmd += "--${builtin.name}_out=${outPrefix}${getOutputDir(builtin)}".toString() + String outputBuiltinPath = PluginSpecExt.getOutputDir(builtin, spec.outputDir.get()) + baseCmd += "--${builtin.name}_out=${outPrefix}${outputBuiltinPath}".toString() } Map executableLocations = toolsLocator.plugins.asMap // Handle code generation plugins - plugins.each { plugin -> + spec.plugins.each { PluginSpec plugin -> String name = plugin.name ExecutableLocator locator = executableLocations.get(name) if (locator != null) { @@ -637,10 +326,11 @@ public abstract class GenerateProtoTask extends DefaultTask { logger.warn "protoc plugin '${name}' not defined. Trying to use 'protoc-gen-${name}' from system path" } String pluginOutPrefix = makeOptionsPrefix(plugin.options) - baseCmd += "--${name}_out=${pluginOutPrefix}${getOutputDir(plugin)}".toString() + String outputPluginPath = PluginSpecExt.getOutputDir(plugin, spec.outputDir.get()) + baseCmd += "--${name}_out=${pluginOutPrefix}${outputPluginPath}".toString() } - if (generateDescriptorSet) { + if (spec.generateDescriptorSet) { String path = getDescriptorPath() // Ensure that the folder for the descriptor exists; // the user may have set it to point outside an existing tree @@ -649,10 +339,10 @@ public abstract class GenerateProtoTask extends DefaultTask { folder.mkdirs() } baseCmd += "--descriptor_set_out=${path}".toString() - if (descriptorSetOptions.includeImports) { + if (spec.descriptorSetOptions.includeImports) { baseCmd += "--include_imports" } - if (descriptorSetOptions.includeSourceInfo) { + if (spec.descriptorSetOptions.includeSourceInfo) { baseCmd += "--include_source_info" } } @@ -663,44 +353,8 @@ public abstract class GenerateProtoTask extends DefaultTask { } } - /** - * Used to expose inputs to Gradle, not to be called directly. - */ - @Optional - @Nested - protected DescriptorSetOptions getDescriptorSetOptionsForCaching() { - return generateDescriptorSet ? descriptorSetOptions : null - } - - /** - * Used to expose inputs to Gradle, not to be called directly. - */ - @Nested - protected Collection getBuiltinsForCaching() { - return builtins - } - - /** - * Used to expose inputs to Gradle, not to be called directly. - */ - @Nested - protected Collection getPluginsForCaching() { - return plugins - } - - private static enum State { - INIT, CONFIG, FINALIZED - } - - private State state = State.INIT - - private void checkInitializing() { - Preconditions.checkState(state == State.INIT, 'Should not be called after initilization has finished') - } - - private void checkCanConfig() { - Preconditions.checkState(state == State.CONFIG || state == State.INIT, - 'Should not be called after configuration has finished') + private GenerateProtoTaskSpec requireSpec() { + return spec.get() } private void compileFiles(List cmd) { diff --git a/src/main/groovy/com/google/protobuf/gradle/ProtobufExtension.groovy b/src/main/groovy/com/google/protobuf/gradle/ProtobufExtension.groovy index 0de530f8..399d5c79 100644 --- a/src/main/groovy/com/google/protobuf/gradle/ProtobufExtension.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/ProtobufExtension.groovy @@ -28,17 +28,20 @@ */ package com.google.protobuf.gradle -import com.google.protobuf.gradle.internal.DefaultProtoSourceSet +import com.google.protobuf.gradle.internal.DefaultGenerateProtoTaskCollection +import com.google.protobuf.gradle.internal.ProtoSourceSetObjectFactory +import com.google.protobuf.gradle.internal.ProtoVariantObjectFactory +import com.google.protobuf.gradle.tasks.GenerateProtoTaskCollection import com.google.protobuf.gradle.tasks.ProtoSourceSet +import com.google.protobuf.gradle.tasks.ProtoVariant import groovy.transform.CompileStatic import groovy.transform.PackageScope -import groovy.transform.TypeChecked -import groovy.transform.TypeCheckingMode import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project +import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property -import org.gradle.api.tasks.TaskCollection +import org.gradle.util.ConfigureUtil /** * Adds the protobuf {} block as a property of the project. @@ -50,29 +53,33 @@ abstract class ProtobufExtension { private final Project project private final GenerateProtoTaskCollection tasks private final ToolsLocator tools - private final ArrayList> taskConfigActions private final NamedDomainObjectContainer sourceSets + private final NamedDomainObjectContainer variants @PackageScope final String defaultGeneratedFilesBaseDir public ProtobufExtension(final Project project) { this.project = project - this.tasks = new GenerateProtoTaskCollection(project) + this.tasks = new DefaultGenerateProtoTaskCollection(project) this.tools = new ToolsLocator(project) - this.taskConfigActions = [] + this.defaultGeneratedFilesBaseDir = "${project.buildDir}/generated/source/proto" this.generatedFilesBaseDirProperty.convention(defaultGeneratedFilesBaseDir) - this.sourceSets = project.objects.domainObjectContainer(ProtoSourceSet) { String name -> - new DefaultProtoSourceSet(name, project.objects) - } + + ObjectFactory objects = project.objects + this.sourceSets = project.objects.domainObjectContainer(ProtoSourceSet, new ProtoSourceSetObjectFactory(objects)) + this.variants = project.objects.domainObjectContainer(ProtoVariant, new ProtoVariantObjectFactory(objects)) } - @PackageScope NamedDomainObjectContainer getSourceSets() { return this.sourceSets } + NamedDomainObjectContainer getVariants() { + return variants + } + @PackageScope ToolsLocator getTools() { return tools @@ -94,13 +101,6 @@ abstract class ProtobufExtension { @PackageScope abstract Property getGeneratedFilesBaseDirProperty() - @PackageScope - void configureTasks() { - this.taskConfigActions.each { action -> - action.execute(tasks) - } - } - //=========================================================================== // Configuration methods //=========================================================================== @@ -113,6 +113,10 @@ abstract class ProtobufExtension { configureAction.execute(tools.protoc) } + public void protoc(@DelegatesTo(ExecutableLocator) Closure closure) { + ConfigureUtil.configure(closure, tools.protoc) + } + /** * Locate the codegen plugin executables. The closure will be manipulating a * NamedDomainObjectContainer. @@ -121,6 +125,10 @@ abstract class ProtobufExtension { configureAction.execute(tools.plugins) } + public void plugins(Closure> closure) { + ConfigureUtil.configure(closure, tools.plugins) + } + /** * Configures the generateProto tasks in the given closure. * @@ -132,8 +140,12 @@ abstract class ProtobufExtension { * change the task in your own afterEvaluate closure, as the change may not * be picked up correctly by the wired javaCompile task. */ - public void generateProtoTasks(Action configureAction) { - taskConfigActions.add(configureAction) + void generateProtoTasks(Action action) { + action.execute(tasks) + } + + void generateProtoTasks(@DelegatesTo(GenerateProtoTaskCollection) Closure closure) { + ConfigureUtil.configure(closure, tasks) } /** @@ -147,53 +159,4 @@ abstract class ProtobufExtension { public GenerateProtoTaskCollection getGenerateProtoTasks() { return tasks } - - public class GenerateProtoTaskCollection { - private final Project project - - GenerateProtoTaskCollection(final Project project) { - this.project = project - } - - public TaskCollection all() { - return project.tasks.withType(GenerateProtoTask) - } - - public TaskCollection ofSourceSet(String sourceSet) { - return all().matching { GenerateProtoTask task -> - !Utils.isAndroidProject(project) && task.sourceSet.name == sourceSet - } - } - - public TaskCollection ofFlavor(String flavor) { - return all().matching { GenerateProtoTask task -> - Utils.isAndroidProject(project) && task.flavors.contains(flavor) - } - } - - public TaskCollection ofBuildType(String buildType) { - return all().matching { GenerateProtoTask task -> - Utils.isAndroidProject(project) && task.buildType == buildType - } - } - - @TypeChecked(TypeCheckingMode.SKIP) // Don't depend on AGP - public TaskCollection ofVariant(String variant) { - return all().matching { GenerateProtoTask task -> - Utils.isAndroidProject(project) && task.variant.name == variant - } - } - - public TaskCollection ofNonTest() { - return all().matching { GenerateProtoTask task -> - Utils.isAndroidProject(project) && !task.isTestVariant - } - } - - public TaskCollection ofTest() { - return all().matching { GenerateProtoTask task -> - Utils.isAndroidProject(project) && task.isTestVariant - } - } - } } diff --git a/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy index c0c2bae5..0a3f4c75 100644 --- a/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy @@ -34,8 +34,11 @@ import com.android.build.gradle.api.TestVariant import com.android.build.gradle.api.UnitTestVariant import com.android.builder.model.SourceProvider import com.google.protobuf.gradle.internal.DefaultProtoSourceSet +import com.google.protobuf.gradle.internal.GenerateProtoTaskSpecExt import com.google.protobuf.gradle.internal.ProjectExt +import com.google.protobuf.gradle.tasks.GenerateProtoTaskSpec import com.google.protobuf.gradle.tasks.ProtoSourceSet +import com.google.protobuf.gradle.tasks.ProtoVariant import groovy.transform.CompileStatic import groovy.transform.TypeChecked import groovy.transform.TypeCheckingMode @@ -153,11 +156,6 @@ class ProtobufPlugin implements Plugin { } } project.afterEvaluate { - this.protobufExtension.configureTasks() - // Disallow user configuration outside the config closures, because the operations just - // after the doneConfig() loop over the generated outputs and will be out-of-date if - // plugin output is added after this point. - this.protobufExtension.generateProtoTasks.all().configureEach { it.doneConfig() } postConfigure.each { it.call() } // protoc and codegen plugin configuration may change through the protobuf{} // block. Only at this point the configuration has been finalized. @@ -237,6 +235,7 @@ class ProtobufPlugin implements Plugin { private void addTasksForSourceSet( SourceSet sourceSet, ProtoSourceSet protoSourceSet, Configuration protobufConfig, Configuration compileProtoPath, Collection postConfigure) { + ProtoVariant protoVariant = protobufExtension.variants.create(sourceSet.name) Provider extractProtosTask = setupExtractProtosTask(protoSourceSet, protobufConfig) Provider extractIncludeProtosTask = setupExtractIncludeProtosTask( @@ -248,11 +247,10 @@ class ProtobufPlugin implements Plugin { protoSourceSet.includesFrom(protobufExtension.sourceSets.getByName("main")) } - Provider generateProtoTask = addGenerateProtoTask(protoSourceSet) { - it.sourceSet = sourceSet - it.doneInitializing() - it.builtins.maybeCreate("java") - } + protoVariant.sourceSet = sourceSet.name + protoVariant.isTest = Utils.isTest(sourceSet.name) + protoVariant.generateProtoTaskSpec.builtins.maybeCreate("java") + addGenerateProtoTask(protoVariant, protoSourceSet) sourceSet.java.srcDirs(protoSourceSet.output) @@ -265,12 +263,13 @@ class ProtobufPlugin implements Plugin { } postConfigure.add { + GenerateProtoTaskSpec spec = protoVariant.generateProtoTaskSpec + Collection outputDirs = GenerateProtoTaskSpecExt.getOutputSourceDirectories(spec) + project.plugins.withId("eclipse") { // This is required because the intellij/eclipse plugin does not allow adding source directories // that do not exist. The intellij/eclipse config files should be valid from the start. - generateProtoTask.get().getOutputSourceDirectories().each { File outputDir -> - outputDir.mkdirs() - } + outputDirs.each { File outputDir -> outputDir.mkdirs() } } project.plugins.withId("idea") { @@ -280,9 +279,7 @@ class ProtobufPlugin implements Plugin { } Utils.addToIdeSources(project, isTest, project.files(extractProtosTask).singleFile, true) Utils.addToIdeSources(project, isTest, project.files(extractIncludeProtosTask).singleFile, true) - generateProtoTask.get().getOutputSourceDirectories().each { File outputDir -> - Utils.addToIdeSources(project, isTest, outputDir, true) - } + outputDirs.each { File outputDir -> Utils.addToIdeSources(project, isTest, outputDir, true) } } } } @@ -296,6 +293,7 @@ class ProtobufPlugin implements Plugin { NamedDomainObjectContainer variantSourceSets, Collection postConfigure ) { + ProtoVariant protoVariant = protobufExtension.variants.create(variant.name) Boolean isTestVariant = variant instanceof TestVariant || variant instanceof UnitTestVariant ProtoSourceSet variantSourceSet = variantSourceSets.create(variant.name) @@ -310,7 +308,7 @@ class ProtobufPlugin implements Plugin { // Make protos in 'test' variant able to import protos from the 'main' variant. // Pass include proto files from main to test. - if (variant instanceof TestVariant || variant instanceof UnitTestVariant) { + if (isTestVariant) { postConfigure.add { variantSourceSet.includesFrom(protobufExtension.sourceSets.getByName("main")) variantSourceSet.includesFrom(variantSourceSets.getByName(variant.testedVariant.name)) @@ -324,14 +322,10 @@ class ProtobufPlugin implements Plugin { variantSourceSet.extendsFrom(protobufExtension.sourceSets.getByName(sourceProvider.name)) } - Provider generateProtoTask = addGenerateProtoTask(variantSourceSet) { - it.setVariant(variant, isTestVariant) - it.flavors = variant.productFlavors.collect { it.name } - if (variant.hasProperty('buildType')) { - it.buildType = variant.buildType.name - } - it.doneInitializing() - } + protoVariant.isTest = isTestVariant + protoVariant.flavors = variant.productFlavors.collect { it.name } as Set + protoVariant.buildType = variant.hasProperty('buildType') ? variant.buildType.name : null + Provider generateProtoTask = addGenerateProtoTask(protoVariant, variantSourceSet) if (project.android.hasProperty('libraryVariants')) { // Include source proto files in the compiled archive, so that proto files from @@ -343,15 +337,22 @@ class ProtobufPlugin implements Plugin { } } postConfigure.add { + Collection outputDirs = + GenerateProtoTaskSpecExt.getOutputSourceDirectories(protoVariant.generateProtoTaskSpec) + // This cannot be called once task execution has started. - variant.registerJavaGeneratingTask(generateProtoTask.get(), generateProtoTask.get().outputSourceDirectories) + if (ProjectExt.isAgpAbove422(project)) { + variant.registerJavaGeneratingTask(generateProtoTask, outputDirs) + } else { + variant.registerJavaGeneratingTask(generateProtoTask.get(), outputDirs) + } project.plugins.withId("org.jetbrains.kotlin.android") { project.afterEvaluate { String compileKotlinTaskName = Utils.getKotlinAndroidCompileTaskName(project, variant.name) project.tasks.named(compileKotlinTaskName, KotlinCompile) { KotlinCompile task -> task.dependsOn(generateProtoTask) - task.source(generateProtoTask.get().outputSourceDirectories) + task.source(outputDirs) } } } @@ -369,19 +370,18 @@ class ProtobufPlugin implements Plugin { * for; for Android it's the collection of sourceSets that the variant includes. */ private Provider addGenerateProtoTask( - ProtoSourceSet protoSourceSet, - Action configureAction + ProtoVariant protoVariant, + ProtoSourceSet protoSourceSet ) { String sourceSetName = protoSourceSet.name String taskName = 'generate' + Utils.getSourceSetSubstringForTaskNames(sourceSetName) + 'Proto' String defaultGeneratedFilesBaseDir = protobufExtension.defaultGeneratedFilesBaseDir Provider generatedFilesBaseDirProvider = protobufExtension.generatedFilesBaseDirProperty + protoVariant.generateProtoTaskSpec.outputDir.set("${defaultGeneratedFilesBaseDir}/${sourceSetName}".toString()) Provider task = project.tasks.register(taskName, GenerateProtoTask) { + it.spec.set(protoVariant.generateProtoTaskSpec) CopyActionFacade copyActionFacade = CopyActionFacade.Loader.create(it.project, it.objectFactory) it.description = "Compiles Proto source for '${sourceSetName}'".toString() - it.outputBaseDir = project.providers.provider { - "${defaultGeneratedFilesBaseDir}/${sourceSetName}".toString() - } it.addSourceDirs(protoSourceSet.proto) it.addIncludeDir(protoSourceSet.proto.sourceDirectories) it.addIncludeDir(protoSourceSet.includeProtoDirs) @@ -393,13 +393,14 @@ class ProtobufPlugin implements Plugin { // Purposefully don't wire this up to outputs, as it can be mixed with other files. copyActionFacade.copy { CopySpec spec -> spec.includeEmptyDirs = false - spec.from(it.outputBaseDir) + spec.from(protoVariant.generateProtoTaskSpec.outputDir.get()) spec.into("${generatedFilesBaseDir}/${sourceSetName}") } } - configureAction.execute(it) } - protoSourceSet.output.from(task.map { GenerateProtoTask it -> it.outputSourceDirectories }) + protoSourceSet.output.from(task.map { + GenerateProtoTaskSpecExt.getOutputSourceDirectories(protoVariant.generateProtoTaskSpec) + }) return task } diff --git a/src/main/groovy/com/google/protobuf/gradle/internal/DefaultDescriptorSetSpec.groovy b/src/main/groovy/com/google/protobuf/gradle/internal/DefaultDescriptorSetSpec.groovy new file mode 100644 index 00000000..2e3956ad --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/internal/DefaultDescriptorSetSpec.groovy @@ -0,0 +1,51 @@ +package com.google.protobuf.gradle.internal + +import com.google.protobuf.gradle.tasks.DescriptorSetSpec +import groovy.transform.CompileStatic +import org.gradle.api.model.ObjectFactory + +@CompileStatic +@SuppressWarnings("JUnitPublicNonTestMethod") // it is not a test class +class DefaultDescriptorSetSpec implements DescriptorSetSpec { + private final ObjectFactory objects + private String path + private boolean includeSourceInfo + private boolean includeImports + + DefaultDescriptorSetSpec(ObjectFactory objects) { + this.objects = objects + this.path = null + this.includeSourceInfo = false + this.includeImports = false + } + + @Override + String getPath() { + return this.path + } + + @Override + void setPath(String value) { + this.path = value + } + + @Override + boolean getIncludeSourceInfo() { + return this.includeSourceInfo + } + + @Override + void setIncludeSourceInfo(boolean value) { + this.includeSourceInfo = value + } + + @Override + boolean getIncludeImports() { + return this.includeImports + } + + @Override + void setIncludeImports(boolean value) { + this.includeImports = value + } +} diff --git a/src/main/groovy/com/google/protobuf/gradle/internal/DefaultGenerateProtoTaskCollection.groovy b/src/main/groovy/com/google/protobuf/gradle/internal/DefaultGenerateProtoTaskCollection.groovy new file mode 100644 index 00000000..ee2537aa --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/internal/DefaultGenerateProtoTaskCollection.groovy @@ -0,0 +1,131 @@ +package com.google.protobuf.gradle.internal + +import com.google.protobuf.gradle.ProtobufExtension +import com.google.protobuf.gradle.Utils +import com.google.protobuf.gradle.tasks.GenerateProtoTaskCollection +import com.google.protobuf.gradle.tasks.GenerateProtoTaskSpec +import com.google.protobuf.gradle.tasks.ProtoVariant +import groovy.transform.CompileStatic +import org.gradle.api.Action +import org.gradle.api.NamedDomainObjectSet +import org.gradle.api.Project +import org.gradle.util.ConfigureUtil + +@CompileStatic +class DefaultGenerateProtoTaskCollection implements GenerateProtoTaskCollection { + private final Project project + + DefaultGenerateProtoTaskCollection(Project project) { + this.project = project + } + + NamedDomainObjectSet all() { + return project.extensions.getByType(ProtobufExtension).variants + } + + @Override + void all(Action action) { + all().all { ProtoVariant variant -> action.execute(variant.generateProtoTaskSpec) } + } + + @Override + void all(Closure closure) { + all(ConfigureUtil.configureUsing(closure)) + } + + NamedDomainObjectSet ofSourceSet(String sourceSet) { + return all() + .matching { ProtoVariant variant -> !Utils.isAndroidProject(project) && variant.sourceSet == sourceSet } + } + + @Override + void ofSourceSet(String sourceSet, Action action) { + ofSourceSet(sourceSet) + .all { ProtoVariant variant -> action.execute(variant.generateProtoTaskSpec) } + } + + @Override + void ofSourceSet(String sourceSet, Closure closure) { + ofSourceSet(sourceSet, ConfigureUtil.configureUsing(closure)) + } + + NamedDomainObjectSet ofFlavor(String flavor) { + return all() + .matching { ProtoVariant variant -> Utils.isAndroidProject(project) && variant.flavors.contains(flavor) } + } + + @Override + void ofFlavor(String flavor, Action action) { + ofFlavor(flavor) + .all { ProtoVariant variant -> action.execute(variant.generateProtoTaskSpec) } + } + + @Override + void ofFlavor(String flavor, Closure closure) { + ofFlavor(flavor, ConfigureUtil.configureUsing(closure)) + } + + NamedDomainObjectSet ofBuildType(String buildType) { + return all() + .matching { ProtoVariant variant -> Utils.isAndroidProject(project) && variant.buildType == buildType } + } + + @Override + void ofBuildType(String buildType, Action action) { + ofBuildType(buildType) + .all { ProtoVariant variant -> action.execute(variant.generateProtoTaskSpec) } + } + + @Override + void ofBuildType(String buildType, Closure closure) { + ofBuildType(buildType, ConfigureUtil.configureUsing(closure)) + } + + NamedDomainObjectSet ofVariant(String name) { + return all() + .matching { ProtoVariant variant -> Utils.isAndroidProject(project) && variant.name == name } + } + + @Override + void ofVariant(String name, Action action) { + ofVariant(name) + .all { ProtoVariant variant -> action.execute(variant.generateProtoTaskSpec) } + } + + @Override + void ofVariant(String name, Closure closure) { + ofVariant(name, ConfigureUtil.configureUsing(closure)) + } + + NamedDomainObjectSet ofNonTest() { + return all() + .matching { ProtoVariant variant -> Utils.isAndroidProject(project) && !variant.isTest } + } + + @Override + void ofNonTest(Action action) { + ofNonTest() + .all { ProtoVariant variant -> action.execute(variant.generateProtoTaskSpec) } + } + + @Override + void ofNonTest(Closure closure) { + ofNonTest(ConfigureUtil.configureUsing(closure)) + } + + NamedDomainObjectSet ofTest() { + all() + .matching { ProtoVariant variant -> Utils.isAndroidProject(project) && variant.isTest } + } + + @Override + void ofTest(Action action) { + ofTest() + .all { ProtoVariant variant -> action.execute(variant.generateProtoTaskSpec) } + } + + @Override + void ofTest(Closure closure) { + ofTest(ConfigureUtil.configureUsing(closure)) + } +} diff --git a/src/main/groovy/com/google/protobuf/gradle/internal/DefaultGenerateProtoTaskSpec.groovy b/src/main/groovy/com/google/protobuf/gradle/internal/DefaultGenerateProtoTaskSpec.groovy new file mode 100644 index 00000000..1732e2de --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/internal/DefaultGenerateProtoTaskSpec.groovy @@ -0,0 +1,109 @@ +package com.google.protobuf.gradle.internal + +import com.google.protobuf.gradle.tasks.DescriptorSetSpec +import com.google.protobuf.gradle.tasks.GenerateProtoTaskSpec +import com.google.protobuf.gradle.tasks.PluginSpec +import groovy.transform.CompileStatic +import org.gradle.api.Action +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.NamedDomainObjectFactory +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.util.ConfigureUtil + +@CompileStatic +@SuppressWarnings("JUnitPublicNonTestMethod") // it is not a test class +class DefaultGenerateProtoTaskSpec implements GenerateProtoTaskSpec { + private final NamedDomainObjectContainer plugins + private final NamedDomainObjectContainer builtins + private final DescriptorSetSpec descriptorSetSpec + private final Property outputDir + private final String variantName + private boolean generateDescriptorSet = false + + DefaultGenerateProtoTaskSpec(String variantName, ObjectFactory objects) { + NamedDomainObjectFactory pluginSpecObjectFactory = new PluginSpecObjectFactory(objects) + this.plugins = objects.domainObjectContainer(PluginSpec, pluginSpecObjectFactory) + this.builtins = objects.domainObjectContainer(PluginSpec, pluginSpecObjectFactory) + this.outputDir = objects.property(String) + this.descriptorSetSpec = new DefaultDescriptorSetSpec(objects) + this.variantName = variantName + } + + @Override + String getVariantName() { + return this.variantName + } + + @Override + Property getOutputDir() { + return this.outputDir + } + + @Override + @Deprecated + void setOutputBaseDir(Provider outputBaseDir) { + this.outputDir.set(outputBaseDir) + } + + @Override + boolean getGenerateDescriptorSet() { + return generateDescriptorSet + } + + @Override + void setGenerateDescriptorSet(boolean enabled) { + this.generateDescriptorSet = enabled + } + + @Override + DescriptorSetSpec getDescriptorSetOptions() { + return this.descriptorSetSpec + } + + @Override + NamedDomainObjectContainer getPlugins() { + return this.plugins + } + + @Override + NamedDomainObjectContainer getBuiltins() { + return this.builtins + } + + @Override + boolean hasPlugin(String name) { + return plugins.findByName(name) != null + } + + @Override + void builtins(Action> configureAction) { + configureAction.execute(builtins) + } + + @Override + void builtins(Closure> closure) { + ConfigureUtil.configure(closure, builtins) + } + + @Override + void plugins(Action> configureAction) { + configureAction.execute(plugins) + } + + @Override + void plugins(Closure> closure) { + ConfigureUtil.configure(closure, plugins) + } + + @Override + void generateDescriptorSet(@DelegatesTo(DescriptorSetSpec) Action configureAction) { + configureAction.execute(descriptorSetSpec) + } + + @Override + void generateDescriptorSet(Closure closure) { + ConfigureUtil.configure(closure, descriptorSetSpec) + } +} diff --git a/src/main/groovy/com/google/protobuf/gradle/internal/DefaultPluginSpec.groovy b/src/main/groovy/com/google/protobuf/gradle/internal/DefaultPluginSpec.groovy new file mode 100644 index 00000000..8462519b --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/internal/DefaultPluginSpec.groovy @@ -0,0 +1,44 @@ +package com.google.protobuf.gradle.internal + +import com.google.protobuf.gradle.tasks.PluginSpec +import groovy.transform.CompileStatic +import org.gradle.api.model.ObjectFactory + +@CompileStatic +@SuppressWarnings("JUnitPublicNonTestMethod") // it is not a test class +class DefaultPluginSpec implements PluginSpec { + private final ObjectFactory objects + private final List options = [] + private final String name + private String outputSubDir + + DefaultPluginSpec(ObjectFactory objects, String name) { + this.objects = objects + this.name = name + } + + DefaultPluginSpec option(String option) { + options.add(option) + return this + } + + List getOptions() { + return options + } + + @Override + String getName() { + return name + } + + void setOutputSubDir(String outputSubDir) { + this.outputSubDir = outputSubDir + } + + String getOutputSubDir() { + if (outputSubDir != null) { + return outputSubDir + } + return name + } +} diff --git a/src/main/groovy/com/google/protobuf/gradle/internal/DefaultProtoVariant.groovy b/src/main/groovy/com/google/protobuf/gradle/internal/DefaultProtoVariant.groovy new file mode 100644 index 00000000..019b00d4 --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/internal/DefaultProtoVariant.groovy @@ -0,0 +1,72 @@ +package com.google.protobuf.gradle.internal + +import com.google.protobuf.gradle.tasks.GenerateProtoTaskSpec +import com.google.protobuf.gradle.tasks.ProtoVariant +import groovy.transform.CompileStatic +import org.gradle.api.model.ObjectFactory + +@CompileStatic +class DefaultProtoVariant implements ProtoVariant { + private final GenerateProtoTaskSpec generateProtoTaskSpec + private final String name + private String sourceSet + private String buildType + private boolean isTest + private Set flavors + + DefaultProtoVariant(String name, ObjectFactory objects) { + this.name = name + this.generateProtoTaskSpec = new DefaultGenerateProtoTaskSpec(name, objects) + this.flavors = [] as Set + } + + @Override + GenerateProtoTaskSpec getGenerateProtoTaskSpec() { + return this.generateProtoTaskSpec + } + + @Override + String getSourceSet() { + return this.sourceSet + } + + @Override + void setSourceSet(String sourceSet) { + this.sourceSet = sourceSet + } + + @Override + String getName() { + return this.name + } + + @Override + Set getFlavors() { + return this.flavors + } + + @Override + void setFlavors(Set flavors) { + this.flavors = flavors + } + + @Override + String getBuildType() { + return this.buildType + } + + @Override + void setBuildType(String buildType) { + this.buildType = buildType + } + + @Override + boolean getIsTest() { + return this.isTest + } + + @Override + void setIsTest(boolean isTest) { + this.isTest = isTest + } +} diff --git a/src/main/groovy/com/google/protobuf/gradle/internal/GenerateProtoTaskSpecExt.groovy b/src/main/groovy/com/google/protobuf/gradle/internal/GenerateProtoTaskSpecExt.groovy new file mode 100644 index 00000000..d159418f --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/internal/GenerateProtoTaskSpecExt.groovy @@ -0,0 +1,29 @@ +package com.google.protobuf.gradle.internal + +import com.google.protobuf.gradle.tasks.GenerateProtoTaskSpec +import groovy.transform.CompileStatic + +@CompileStatic +class GenerateProtoTaskSpecExt { + private GenerateProtoTaskSpecExt() { + } + + static Collection getOutputSourceDirectories(GenerateProtoTaskSpec spec) { + Collection srcDirs = [] + + spec.builtins.each { builtin -> + File dir = new File(PluginSpecExt.getOutputDir(builtin, spec.outputDir.get())) + if (!dir.name.endsWith(".zip") && !dir.name.endsWith(".jar")) { + srcDirs.add(dir) + } + } + spec.plugins.each { plugin -> + File dir = new File(PluginSpecExt.getOutputDir(plugin, spec.outputDir.get())) + if (!dir.name.endsWith(".zip") && !dir.name.endsWith(".jar")) { + srcDirs.add(dir) + } + } + + return srcDirs + } +} diff --git a/src/main/groovy/com/google/protobuf/gradle/internal/PluginSpecExt.groovy b/src/main/groovy/com/google/protobuf/gradle/internal/PluginSpecExt.groovy new file mode 100644 index 00000000..f51580e9 --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/internal/PluginSpecExt.groovy @@ -0,0 +1,14 @@ +package com.google.protobuf.gradle.internal + +import com.google.protobuf.gradle.tasks.PluginSpec +import groovy.transform.CompileStatic + +@CompileStatic +class PluginSpecExt { + private PluginSpecExt() { + } + + static String getOutputDir(PluginSpec plugin, String outputBaseDir) { + return "${outputBaseDir}/${plugin.outputSubDir}" + } +} diff --git a/src/main/groovy/com/google/protobuf/gradle/internal/PluginSpecObjectFactory.groovy b/src/main/groovy/com/google/protobuf/gradle/internal/PluginSpecObjectFactory.groovy new file mode 100644 index 00000000..db76537b --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/internal/PluginSpecObjectFactory.groovy @@ -0,0 +1,20 @@ +package com.google.protobuf.gradle.internal + +import com.google.protobuf.gradle.tasks.PluginSpec +import groovy.transform.CompileStatic +import org.gradle.api.NamedDomainObjectFactory +import org.gradle.api.model.ObjectFactory + +@CompileStatic +class PluginSpecObjectFactory implements NamedDomainObjectFactory { + private final ObjectFactory objects + + PluginSpecObjectFactory(ObjectFactory objects) { + this.objects = objects + } + + @Override + PluginSpec create(String name) { + return new DefaultPluginSpec(objects, name) + } +} diff --git a/src/main/groovy/com/google/protobuf/gradle/internal/ProjectExt.groovy b/src/main/groovy/com/google/protobuf/gradle/internal/ProjectExt.groovy index b1a261b5..8045b8e2 100644 --- a/src/main/groovy/com/google/protobuf/gradle/internal/ProjectExt.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/internal/ProjectExt.groovy @@ -65,4 +65,14 @@ class ProjectExt { (android as TestedExtension).getUnitTestVariants().all(action) } } + + static boolean isAgpAbove422(Project project) { + // Different type between agp 4.2.2 and 7.0.0 + // androidComponents exists since agp 4.2 + Object androidComponents = project.extensions.findByName("androidComponents") + + // Below 4.2 androidComponents extension does not exists + // Below 7.0.0 androidComponents extension does not have pluginVersion field + return androidComponents != null && androidComponents.hasProperty("pluginVersion") + } } diff --git a/src/main/groovy/com/google/protobuf/gradle/internal/ProtoSourceSetObjectFactory.groovy b/src/main/groovy/com/google/protobuf/gradle/internal/ProtoSourceSetObjectFactory.groovy new file mode 100644 index 00000000..9175da61 --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/internal/ProtoSourceSetObjectFactory.groovy @@ -0,0 +1,20 @@ +package com.google.protobuf.gradle.internal + +import com.google.protobuf.gradle.tasks.ProtoSourceSet +import groovy.transform.CompileStatic +import org.gradle.api.NamedDomainObjectFactory +import org.gradle.api.model.ObjectFactory + +@CompileStatic +class ProtoSourceSetObjectFactory implements NamedDomainObjectFactory { + private final ObjectFactory objects + + ProtoSourceSetObjectFactory(ObjectFactory objects) { + this.objects = objects + } + + @Override + ProtoSourceSet create(String name) { + return new DefaultProtoSourceSet(name, objects) + } +} diff --git a/src/main/groovy/com/google/protobuf/gradle/internal/ProtoVariantObjectFactory.groovy b/src/main/groovy/com/google/protobuf/gradle/internal/ProtoVariantObjectFactory.groovy new file mode 100644 index 00000000..58c728ea --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/internal/ProtoVariantObjectFactory.groovy @@ -0,0 +1,20 @@ +package com.google.protobuf.gradle.internal + +import com.google.protobuf.gradle.tasks.ProtoVariant +import groovy.transform.CompileStatic +import org.gradle.api.NamedDomainObjectFactory +import org.gradle.api.model.ObjectFactory + +@CompileStatic +class ProtoVariantObjectFactory implements NamedDomainObjectFactory { + private final ObjectFactory objects + + ProtoVariantObjectFactory(ObjectFactory objects) { + this.objects = objects + } + + @Override + ProtoVariant create(String name) { + return new DefaultProtoVariant(name, objects) + } +} diff --git a/src/main/groovy/com/google/protobuf/gradle/tasks/DescriptorSetSpec.groovy b/src/main/groovy/com/google/protobuf/gradle/tasks/DescriptorSetSpec.groovy new file mode 100644 index 00000000..4edcc85d --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/tasks/DescriptorSetSpec.groovy @@ -0,0 +1,48 @@ +package com.google.protobuf.gradle.tasks + +import groovy.transform.CompileStatic +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.OutputFile + +import javax.annotation.Nullable + +/** + * Configuration object for descriptor generation details. + */ +@CompileStatic +@SuppressWarnings("JUnitPublicNonTestMethod") // it is not a test class +interface DescriptorSetSpec { + /** + * If set, specifies an alternative location than the default for storing the descriptor + * set. + * + * Default: null + */ + @Nullable + @Optional + @OutputFile + String getPath() + + void setPath(String value) + + /** + * If true, source information (comments, locations) will be included in the descriptor set. + * + * Default: false + */ + @Input + boolean getIncludeSourceInfo() + + void setIncludeSourceInfo(boolean value) + + /** + * If true, imports are included in the descriptor set, such that it is self-containing. + * + * Default: false + */ + @Input + boolean getIncludeImports() + + void setIncludeImports(boolean value) +} diff --git a/src/main/groovy/com/google/protobuf/gradle/tasks/GenerateProtoTaskCollection.groovy b/src/main/groovy/com/google/protobuf/gradle/tasks/GenerateProtoTaskCollection.groovy new file mode 100644 index 00000000..ef9f951f --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/tasks/GenerateProtoTaskCollection.groovy @@ -0,0 +1,33 @@ +package com.google.protobuf.gradle.tasks + +import org.gradle.api.Action + +interface GenerateProtoTaskCollection { + void all(Action action) + + void all(@DelegatesTo(GenerateProtoTaskSpec) Closure closure) + + void ofSourceSet(String sourceSet, Action action) + + void ofSourceSet(String sourceSet, @DelegatesTo(GenerateProtoTaskSpec) Closure closure) + + void ofFlavor(String flavor, Action action) + + void ofFlavor(String flavor, @DelegatesTo(GenerateProtoTaskSpec) Closure closure) + + void ofBuildType(String buildType, Action action) + + void ofBuildType(String buildType, @DelegatesTo(GenerateProtoTaskSpec) Closure closure) + + void ofVariant(String name, Action action) + + void ofVariant(String name, @DelegatesTo(GenerateProtoTaskSpec) Closure closure) + + void ofNonTest(Action action) + + void ofNonTest(@DelegatesTo(GenerateProtoTaskSpec) Closure closure) + + void ofTest(Action action) + + void ofTest(@DelegatesTo(GenerateProtoTaskSpec) Closure closure) +} diff --git a/src/main/groovy/com/google/protobuf/gradle/tasks/GenerateProtoTaskSpec.groovy b/src/main/groovy/com/google/protobuf/gradle/tasks/GenerateProtoTaskSpec.groovy new file mode 100644 index 00000000..2ebe0b92 --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/tasks/GenerateProtoTaskSpec.groovy @@ -0,0 +1,91 @@ +package com.google.protobuf.gradle.tasks + +import groovy.transform.CompileStatic +import org.gradle.api.Action +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.OutputDirectory + +@CompileStatic +@SuppressWarnings("JUnitPublicNonTestMethod") // it is not a test class +interface GenerateProtoTaskSpec { + + @OutputDirectory + Property getOutputDir() + + @Deprecated + void setOutputBaseDir(Provider outputBaseDir) + + @Input + String getVariantName() + + /** + * If true, will set the protoc flag + * --descriptor_set_out="${outputBaseDir}/descriptor_set.desc" + * + * Default: false + */ + @Input + boolean getGenerateDescriptorSet() + + void setGenerateDescriptorSet(boolean enabled) + + @Nested + DescriptorSetSpec getDescriptorSetOptions() + + /** + * Returns the container of protoc plugins. + */ + @Nested + NamedDomainObjectContainer getPlugins() + + /** + * Returns the container of protoc builtins. + */ + @Nested + NamedDomainObjectContainer getBuiltins() + + /** + * Returns true if the task has a plugin with the given name, false otherwise. + */ + boolean hasPlugin(String name) + + /** + * Configures the protoc builtins in a closure, which will be manipulating a + * NamedDomainObjectContainer. + */ + void builtins(Action> configureAction) + + /** + * Configures the protoc builtins in a closure, which will be manipulating a + * NamedDomainObjectContainer. + */ + void builtins(Closure> closure) + + /** + * Configures the protoc plugins in a closure, which will be manipulating a + * NamedDomainObjectContainer. + */ + void plugins(Action> configureAction) + + /** + * Configures the protoc plugins in a closure, which will be manipulating a + * NamedDomainObjectContainer. + */ + void plugins(Closure> closure) + + /** + * Configures the protoc descriptor set in a closure, which will be manipulating a + * DescriptorSetSpec. + */ + void generateDescriptorSet(@DelegatesTo(DescriptorSetSpec) Action configureAction) + + /** + * Configures the protoc descriptor set in a closure, which will be manipulating a + * DescriptorSetSpec. + */ + void generateDescriptorSet(Closure closure) +} diff --git a/src/main/groovy/com/google/protobuf/gradle/tasks/PluginSpec.groovy b/src/main/groovy/com/google/protobuf/gradle/tasks/PluginSpec.groovy new file mode 100644 index 00000000..a517ea5a --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/tasks/PluginSpec.groovy @@ -0,0 +1,39 @@ +package com.google.protobuf.gradle.tasks + +import groovy.transform.CompileStatic +import org.gradle.api.tasks.Input + +/** + * The container of command-line options for a protoc plugin or a built-in output. + */ +@CompileStatic +@SuppressWarnings("JUnitPublicNonTestMethod") // it is not a test class +interface PluginSpec { + + /** + * Adds a plugin option. + */ + PluginSpec option(String option) + + @Input + List getOptions() + + /** + * Returns the name of the plugin or builtin. + */ + @Input + String getName() + + /** + * Set the output directory for this plugin, + * relative to {@link GenerateProtoTask#outputBaseDir}. + */ + void setOutputSubDir(String outputSubDir) + + /** + * Returns the relative outputDir for this plugin. + * If outputDir is not specified, name is used. + */ + @Input + String getOutputSubDir() +} diff --git a/src/main/groovy/com/google/protobuf/gradle/tasks/ProtoVariant.groovy b/src/main/groovy/com/google/protobuf/gradle/tasks/ProtoVariant.groovy new file mode 100644 index 00000000..8b6deacc --- /dev/null +++ b/src/main/groovy/com/google/protobuf/gradle/tasks/ProtoVariant.groovy @@ -0,0 +1,26 @@ +package com.google.protobuf.gradle.tasks + +import groovy.transform.CompileStatic + +@CompileStatic +interface ProtoVariant { + GenerateProtoTaskSpec getGenerateProtoTaskSpec() + + String getSourceSet() + + void setSourceSet(String name) + + String getName() + + Set getFlavors() + + void setFlavors(Set flavors) + + String getBuildType() + + void setBuildType(String name) + + boolean getIsTest() + + void setIsTest(boolean value) +} diff --git a/src/test/groovy/com/google/protobuf/gradle/ProtobufAndroidPluginTest.groovy b/src/test/groovy/com/google/protobuf/gradle/ProtobufAndroidPluginTest.groovy index a281e8cc..701488a7 100644 --- a/src/test/groovy/com/google/protobuf/gradle/ProtobufAndroidPluginTest.groovy +++ b/src/test/groovy/com/google/protobuf/gradle/ProtobufAndroidPluginTest.groovy @@ -173,4 +173,35 @@ class ProtobufAndroidPluginTest extends Specification { agpVersion << ANDROID_PLUGIN_VERSION.takeRight(1) gradleVersion << GRADLE_VERSION.takeRight(1) } + + @Unroll + void "tests android build should be without eager task creation [android #agpVersion, gradle #gradleVersion]"() { + given: "a project with android plugin" + File mainProjectDir = ProtobufPluginTestHelper.projectBuilder("singleModuleAndroidProject") + .copyDirs('testProjectAndroid', 'testProjectAndroidBare') + .withAndroidPlugin(agpVersion) + .build() + new File(mainProjectDir, "build.gradle") << """ + |tasks + | // Lifecycle 'clean' task always eager + | // https://github.com/gradle/gradle/issues/20864 + | .matching { it.name != "help" && it.name != "clean" } + | .configureEach { throw new IllegalStateException("\$it was eagerly created") } + """.stripMargin() + + when: "evaluate project by help task" + BuildResult result = ProtobufPluginTestHelper.getAndroidGradleRunner( + mainProjectDir, + gradleVersion, + agpVersion, + "help" + ).build() + + then: "it succeed" + assert result.task(":help").outcome == TaskOutcome.SUCCESS + + where: + agpVersion << ANDROID_PLUGIN_VERSION.takeRight(1) + gradleVersion << GRADLE_VERSION.takeRight(1) + } } diff --git a/testProjectAndroidBase/build_base.gradle b/testProjectAndroidBase/build_base.gradle index 29f9fc16..49956ccd 100644 --- a/testProjectAndroidBase/build_base.gradle +++ b/testProjectAndroidBase/build_base.gradle @@ -83,13 +83,17 @@ protobuf { } } generateProtoTasks { - all()*.plugins { - javalite { } + all { + plugins { + javalite { } + } } - ofNonTest()*.plugins { - grpc { - // Options added to --grpc_out - option 'lite' + ofNonTest { + plugins { + grpc { + // Options added to --grpc_out + option 'lite' + } } } } @@ -128,133 +132,133 @@ afterEvaluate { test.doLast { assert [ - 'generateArmFreeappDebugAndroidTestProto', - 'generateArmFreeappDebugUnitTestProto', - 'generateArmFreeappReleaseUnitTestProto', - 'generateArmFreeappDebugProto', - 'generateArmFreeappReleaseProto', - 'generateArmRetailappDebugAndroidTestProto', - 'generateArmRetailappDebugUnitTestProto', - 'generateArmRetailappReleaseUnitTestProto', - 'generateArmRetailappDebugProto', - 'generateArmRetailappReleaseProto', - 'generateX86FreeappDebugAndroidTestProto', - 'generateX86FreeappDebugUnitTestProto', - 'generateX86FreeappReleaseUnitTestProto', - 'generateX86FreeappDebugProto', - 'generateX86FreeappReleaseProto', - 'generateX86RetailappDebugAndroidTestProto', - 'generateX86RetailappDebugUnitTestProto', - 'generateX86RetailappReleaseUnitTestProto', - 'generateX86RetailappDebugProto', - 'generateX86RetailappReleaseProto', + 'armFreeappDebugAndroidTest', + 'armFreeappDebugUnitTest', + 'armFreeappReleaseUnitTest', + 'armFreeappDebug', + 'armFreeappRelease', + 'armRetailappDebugAndroidTest', + 'armRetailappDebugUnitTest', + 'armRetailappReleaseUnitTest', + 'armRetailappDebug', + 'armRetailappRelease', + 'x86FreeappDebugAndroidTest', + 'x86FreeappDebugUnitTest', + 'x86FreeappReleaseUnitTest', + 'x86FreeappDebug', + 'x86FreeappRelease', + 'x86RetailappDebugAndroidTest', + 'x86RetailappDebugUnitTest', + 'x86RetailappReleaseUnitTest', + 'x86RetailappDebug', + 'x86RetailappRelease', ] as Set == protobuf.generateProtoTasks.all().collect({ it.name }) as Set assert [ - 'generateArmFreeappDebugAndroidTestProto', - 'generateArmFreeappDebugUnitTestProto', - 'generateArmFreeappReleaseUnitTestProto', - 'generateArmRetailappDebugAndroidTestProto', - 'generateArmRetailappDebugUnitTestProto', - 'generateArmRetailappReleaseUnitTestProto', - 'generateX86FreeappDebugAndroidTestProto', - 'generateX86FreeappDebugUnitTestProto', - 'generateX86FreeappReleaseUnitTestProto', - 'generateX86RetailappDebugAndroidTestProto', - 'generateX86RetailappDebugUnitTestProto', - 'generateX86RetailappReleaseUnitTestProto', + 'armFreeappDebugAndroidTest', + 'armFreeappDebugUnitTest', + 'armFreeappReleaseUnitTest', + 'armRetailappDebugAndroidTest', + 'armRetailappDebugUnitTest', + 'armRetailappReleaseUnitTest', + 'x86FreeappDebugAndroidTest', + 'x86FreeappDebugUnitTest', + 'x86FreeappReleaseUnitTest', + 'x86RetailappDebugAndroidTest', + 'x86RetailappDebugUnitTest', + 'x86RetailappReleaseUnitTest', ] as Set == protobuf.generateProtoTasks.ofTest().collect({ it.name }) as Set assert [ - 'generateArmFreeappDebugProto', - 'generateArmFreeappReleaseProto', - 'generateArmRetailappDebugProto', - 'generateArmRetailappReleaseProto', - 'generateX86FreeappDebugProto', - 'generateX86FreeappReleaseProto', - 'generateX86RetailappDebugProto', - 'generateX86RetailappReleaseProto', + 'armFreeappDebug', + 'armFreeappRelease', + 'armRetailappDebug', + 'armRetailappRelease', + 'x86FreeappDebug', + 'x86FreeappRelease', + 'x86RetailappDebug', + 'x86RetailappRelease', ] as Set == protobuf.generateProtoTasks.ofNonTest().collect({ it.name }) as Set assert [ - 'generateArmFreeappDebugAndroidTestProto', - 'generateArmFreeappDebugUnitTestProto', - 'generateArmFreeappReleaseUnitTestProto', - 'generateArmFreeappDebugProto', - 'generateArmFreeappReleaseProto', - 'generateX86FreeappDebugAndroidTestProto', - 'generateX86FreeappDebugUnitTestProto', - 'generateX86FreeappReleaseUnitTestProto', - 'generateX86FreeappDebugProto', - 'generateX86FreeappReleaseProto', + 'armFreeappDebugAndroidTest', + 'armFreeappDebugUnitTest', + 'armFreeappReleaseUnitTest', + 'armFreeappDebug', + 'armFreeappRelease', + 'x86FreeappDebugAndroidTest', + 'x86FreeappDebugUnitTest', + 'x86FreeappReleaseUnitTest', + 'x86FreeappDebug', + 'x86FreeappRelease', ] as Set == protobuf.generateProtoTasks.ofFlavor('freeapp').collect({ it.name }) as Set assert [ - 'generateArmRetailappDebugAndroidTestProto', - 'generateArmRetailappDebugUnitTestProto', - 'generateArmRetailappReleaseUnitTestProto', - 'generateArmRetailappDebugProto', - 'generateArmRetailappReleaseProto', - 'generateX86RetailappDebugAndroidTestProto', - 'generateX86RetailappDebugUnitTestProto', - 'generateX86RetailappReleaseUnitTestProto', - 'generateX86RetailappDebugProto', - 'generateX86RetailappReleaseProto', + 'armRetailappDebugAndroidTest', + 'armRetailappDebugUnitTest', + 'armRetailappReleaseUnitTest', + 'armRetailappDebug', + 'armRetailappRelease', + 'x86RetailappDebugAndroidTest', + 'x86RetailappDebugUnitTest', + 'x86RetailappReleaseUnitTest', + 'x86RetailappDebug', + 'x86RetailappRelease', ] as Set == protobuf.generateProtoTasks.ofFlavor('retailapp').collect({ it.name }) as Set assert [ - 'generateX86FreeappDebugAndroidTestProto', - 'generateX86FreeappDebugUnitTestProto', - 'generateX86FreeappReleaseUnitTestProto', - 'generateX86FreeappDebugProto', - 'generateX86FreeappReleaseProto', - 'generateX86RetailappDebugAndroidTestProto', - 'generateX86RetailappDebugUnitTestProto', - 'generateX86RetailappReleaseUnitTestProto', - 'generateX86RetailappDebugProto', - 'generateX86RetailappReleaseProto', + 'x86FreeappDebugAndroidTest', + 'x86FreeappDebugUnitTest', + 'x86FreeappReleaseUnitTest', + 'x86FreeappDebug', + 'x86FreeappRelease', + 'x86RetailappDebugAndroidTest', + 'x86RetailappDebugUnitTest', + 'x86RetailappReleaseUnitTest', + 'x86RetailappDebug', + 'x86RetailappRelease', ] as Set == protobuf.generateProtoTasks.ofFlavor('x86').collect({ it.name }) as Set assert [ - 'generateArmFreeappDebugAndroidTestProto', - 'generateArmFreeappDebugUnitTestProto', - 'generateArmFreeappReleaseUnitTestProto', - 'generateArmFreeappDebugProto', - 'generateArmFreeappReleaseProto', - 'generateArmRetailappDebugAndroidTestProto', - 'generateArmRetailappDebugUnitTestProto', - 'generateArmRetailappReleaseUnitTestProto', - 'generateArmRetailappDebugProto', - 'generateArmRetailappReleaseProto', + 'armFreeappDebugAndroidTest', + 'armFreeappDebugUnitTest', + 'armFreeappReleaseUnitTest', + 'armFreeappDebug', + 'armFreeappRelease', + 'armRetailappDebugAndroidTest', + 'armRetailappDebugUnitTest', + 'armRetailappReleaseUnitTest', + 'armRetailappDebug', + 'armRetailappRelease', ] as Set == protobuf.generateProtoTasks.ofFlavor('arm').collect({ it.name }) as Set assert [ - 'generateArmFreeappDebugAndroidTestProto', - 'generateArmFreeappDebugUnitTestProto', - 'generateArmFreeappDebugProto', - 'generateArmRetailappDebugAndroidTestProto', - 'generateArmRetailappDebugUnitTestProto', - 'generateArmRetailappDebugProto', - 'generateX86FreeappDebugAndroidTestProto', - 'generateX86FreeappDebugUnitTestProto', - 'generateX86FreeappDebugProto', - 'generateX86RetailappDebugAndroidTestProto', - 'generateX86RetailappDebugUnitTestProto', - 'generateX86RetailappDebugProto' + 'armFreeappDebugAndroidTest', + 'armFreeappDebugUnitTest', + 'armFreeappDebug', + 'armRetailappDebugAndroidTest', + 'armRetailappDebugUnitTest', + 'armRetailappDebug', + 'x86FreeappDebugAndroidTest', + 'x86FreeappDebugUnitTest', + 'x86FreeappDebug', + 'x86RetailappDebugAndroidTest', + 'x86RetailappDebugUnitTest', + 'x86RetailappDebug' ] as Set == protobuf.generateProtoTasks.ofBuildType('debug').collect({ it.name }) as Set assert [ - 'generateArmFreeappReleaseProto', - 'generateArmFreeappReleaseUnitTestProto', - 'generateArmRetailappReleaseProto', - 'generateArmRetailappReleaseUnitTestProto', - 'generateX86FreeappReleaseProto', - 'generateX86FreeappReleaseUnitTestProto', - 'generateX86RetailappReleaseProto', - 'generateX86RetailappReleaseUnitTestProto', + 'armFreeappRelease', + 'armFreeappReleaseUnitTest', + 'armRetailappRelease', + 'armRetailappReleaseUnitTest', + 'x86FreeappRelease', + 'x86FreeappReleaseUnitTest', + 'x86RetailappRelease', + 'x86RetailappReleaseUnitTest', ] as Set == protobuf.generateProtoTasks.ofBuildType('release').collect({ it.name }) as Set - assert ['generateX86FreeappDebugAndroidTestProto'] as Set == + assert ['x86FreeappDebugAndroidTest'] as Set == protobuf.generateProtoTasks.ofVariant('x86FreeappDebugAndroidTest').collect({ it.name }) as Set // "androidTest" sourceSet is not a flavor @@ -263,7 +267,7 @@ afterEvaluate { // "unitTest" sourceset is not a flavor assert [] as Set == protobuf.generateProtoTasks.ofFlavor('unitTest').collect({ it.name }) as Set - android.applicationVariants.each { variant -> + android.applicationVariants.each { variant -> assertJavaCompileHasProtoGeneratedDir(variant, ['javalite', 'grpc']) } diff --git a/testProjectAndroidDependentBase/build_base.gradle b/testProjectAndroidDependentBase/build_base.gradle index 165aea5c..85fa0f13 100644 --- a/testProjectAndroidDependentBase/build_base.gradle +++ b/testProjectAndroidDependentBase/build_base.gradle @@ -83,13 +83,17 @@ protobuf { } } generateProtoTasks { - all()*.plugins { - javalite { } + all { + plugins { + javalite {} + } } - ofNonTest()*.plugins { - grpc { - // Options added to --grpc_out - option 'lite' + ofNonTest { + plugins { + grpc { + // Options added to --grpc_out + option 'lite' + } } } } @@ -128,133 +132,133 @@ afterEvaluate { test.doLast { assert [ - 'generateArmFreeappDebugAndroidTestProto', - 'generateArmFreeappDebugUnitTestProto', - 'generateArmFreeappReleaseUnitTestProto', - 'generateArmFreeappDebugProto', - 'generateArmFreeappReleaseProto', - 'generateArmRetailappDebugAndroidTestProto', - 'generateArmRetailappDebugUnitTestProto', - 'generateArmRetailappReleaseUnitTestProto', - 'generateArmRetailappDebugProto', - 'generateArmRetailappReleaseProto', - 'generateX86FreeappDebugAndroidTestProto', - 'generateX86FreeappDebugUnitTestProto', - 'generateX86FreeappReleaseUnitTestProto', - 'generateX86FreeappDebugProto', - 'generateX86FreeappReleaseProto', - 'generateX86RetailappDebugAndroidTestProto', - 'generateX86RetailappDebugUnitTestProto', - 'generateX86RetailappReleaseUnitTestProto', - 'generateX86RetailappDebugProto', - 'generateX86RetailappReleaseProto', + 'armFreeappDebugAndroidTest', + 'armFreeappDebugUnitTest', + 'armFreeappReleaseUnitTest', + 'armFreeappDebug', + 'armFreeappRelease', + 'armRetailappDebugAndroidTest', + 'armRetailappDebugUnitTest', + 'armRetailappReleaseUnitTest', + 'armRetailappDebug', + 'armRetailappRelease', + 'x86FreeappDebugAndroidTest', + 'x86FreeappDebugUnitTest', + 'x86FreeappReleaseUnitTest', + 'x86FreeappDebug', + 'x86FreeappRelease', + 'x86RetailappDebugAndroidTest', + 'x86RetailappDebugUnitTest', + 'x86RetailappReleaseUnitTest', + 'x86RetailappDebug', + 'x86RetailappRelease', ] as Set == protobuf.generateProtoTasks.all().collect({ it.name }) as Set assert [ - 'generateArmFreeappDebugAndroidTestProto', - 'generateArmFreeappDebugUnitTestProto', - 'generateArmFreeappReleaseUnitTestProto', - 'generateArmRetailappDebugAndroidTestProto', - 'generateArmRetailappDebugUnitTestProto', - 'generateArmRetailappReleaseUnitTestProto', - 'generateX86FreeappDebugAndroidTestProto', - 'generateX86FreeappDebugUnitTestProto', - 'generateX86FreeappReleaseUnitTestProto', - 'generateX86RetailappDebugAndroidTestProto', - 'generateX86RetailappDebugUnitTestProto', - 'generateX86RetailappReleaseUnitTestProto', + 'armFreeappDebugAndroidTest', + 'armFreeappDebugUnitTest', + 'armFreeappReleaseUnitTest', + 'armRetailappDebugAndroidTest', + 'armRetailappDebugUnitTest', + 'armRetailappReleaseUnitTest', + 'x86FreeappDebugAndroidTest', + 'x86FreeappDebugUnitTest', + 'x86FreeappReleaseUnitTest', + 'x86RetailappDebugAndroidTest', + 'x86RetailappDebugUnitTest', + 'x86RetailappReleaseUnitTest', ] as Set == protobuf.generateProtoTasks.ofTest().collect({ it.name }) as Set assert [ - 'generateArmFreeappDebugProto', - 'generateArmFreeappReleaseProto', - 'generateArmRetailappDebugProto', - 'generateArmRetailappReleaseProto', - 'generateX86FreeappDebugProto', - 'generateX86FreeappReleaseProto', - 'generateX86RetailappDebugProto', - 'generateX86RetailappReleaseProto', + 'armFreeappDebug', + 'armFreeappRelease', + 'armRetailappDebug', + 'armRetailappRelease', + 'x86FreeappDebug', + 'x86FreeappRelease', + 'x86RetailappDebug', + 'x86RetailappRelease', ] as Set == protobuf.generateProtoTasks.ofNonTest().collect({ it.name }) as Set assert [ - 'generateArmFreeappDebugAndroidTestProto', - 'generateArmFreeappDebugUnitTestProto', - 'generateArmFreeappReleaseUnitTestProto', - 'generateArmFreeappDebugProto', - 'generateArmFreeappReleaseProto', - 'generateX86FreeappDebugAndroidTestProto', - 'generateX86FreeappDebugUnitTestProto', - 'generateX86FreeappReleaseUnitTestProto', - 'generateX86FreeappDebugProto', - 'generateX86FreeappReleaseProto', + 'armFreeappDebugAndroidTest', + 'armFreeappDebugUnitTest', + 'armFreeappReleaseUnitTest', + 'armFreeappDebug', + 'armFreeappRelease', + 'x86FreeappDebugAndroidTest', + 'x86FreeappDebugUnitTest', + 'x86FreeappReleaseUnitTest', + 'x86FreeappDebug', + 'x86FreeappRelease', ] as Set == protobuf.generateProtoTasks.ofFlavor('freeapp').collect({ it.name }) as Set assert [ - 'generateArmRetailappDebugAndroidTestProto', - 'generateArmRetailappDebugUnitTestProto', - 'generateArmRetailappReleaseUnitTestProto', - 'generateArmRetailappDebugProto', - 'generateArmRetailappReleaseProto', - 'generateX86RetailappDebugAndroidTestProto', - 'generateX86RetailappDebugUnitTestProto', - 'generateX86RetailappReleaseUnitTestProto', - 'generateX86RetailappDebugProto', - 'generateX86RetailappReleaseProto', + 'armRetailappDebugAndroidTest', + 'armRetailappDebugUnitTest', + 'armRetailappReleaseUnitTest', + 'armRetailappDebug', + 'armRetailappRelease', + 'x86RetailappDebugAndroidTest', + 'x86RetailappDebugUnitTest', + 'x86RetailappReleaseUnitTest', + 'x86RetailappDebug', + 'x86RetailappRelease', ] as Set == protobuf.generateProtoTasks.ofFlavor('retailapp').collect({ it.name }) as Set assert [ - 'generateX86FreeappDebugAndroidTestProto', - 'generateX86FreeappDebugUnitTestProto', - 'generateX86FreeappReleaseUnitTestProto', - 'generateX86FreeappDebugProto', - 'generateX86FreeappReleaseProto', - 'generateX86RetailappDebugAndroidTestProto', - 'generateX86RetailappDebugUnitTestProto', - 'generateX86RetailappReleaseUnitTestProto', - 'generateX86RetailappDebugProto', - 'generateX86RetailappReleaseProto', + 'x86FreeappDebugAndroidTest', + 'x86FreeappDebugUnitTest', + 'x86FreeappReleaseUnitTest', + 'x86FreeappDebug', + 'x86FreeappRelease', + 'x86RetailappDebugAndroidTest', + 'x86RetailappDebugUnitTest', + 'x86RetailappReleaseUnitTest', + 'x86RetailappDebug', + 'x86RetailappRelease', ] as Set == protobuf.generateProtoTasks.ofFlavor('x86').collect({ it.name }) as Set assert [ - 'generateArmFreeappDebugAndroidTestProto', - 'generateArmFreeappDebugUnitTestProto', - 'generateArmFreeappReleaseUnitTestProto', - 'generateArmFreeappDebugProto', - 'generateArmFreeappReleaseProto', - 'generateArmRetailappDebugAndroidTestProto', - 'generateArmRetailappDebugUnitTestProto', - 'generateArmRetailappReleaseUnitTestProto', - 'generateArmRetailappDebugProto', - 'generateArmRetailappReleaseProto', + 'armFreeappDebugAndroidTest', + 'armFreeappDebugUnitTest', + 'armFreeappReleaseUnitTest', + 'armFreeappDebug', + 'armFreeappRelease', + 'armRetailappDebugAndroidTest', + 'armRetailappDebugUnitTest', + 'armRetailappReleaseUnitTest', + 'armRetailappDebug', + 'armRetailappRelease', ] as Set == protobuf.generateProtoTasks.ofFlavor('arm').collect({ it.name }) as Set assert [ - 'generateArmFreeappDebugAndroidTestProto', - 'generateArmFreeappDebugUnitTestProto', - 'generateArmFreeappDebugProto', - 'generateArmRetailappDebugAndroidTestProto', - 'generateArmRetailappDebugUnitTestProto', - 'generateArmRetailappDebugProto', - 'generateX86FreeappDebugAndroidTestProto', - 'generateX86FreeappDebugUnitTestProto', - 'generateX86FreeappDebugProto', - 'generateX86RetailappDebugAndroidTestProto', - 'generateX86RetailappDebugUnitTestProto', - 'generateX86RetailappDebugProto' + 'armFreeappDebugAndroidTest', + 'armFreeappDebugUnitTest', + 'armFreeappDebug', + 'armRetailappDebugAndroidTest', + 'armRetailappDebugUnitTest', + 'armRetailappDebug', + 'x86FreeappDebugAndroidTest', + 'x86FreeappDebugUnitTest', + 'x86FreeappDebug', + 'x86RetailappDebugAndroidTest', + 'x86RetailappDebugUnitTest', + 'x86RetailappDebug' ] as Set == protobuf.generateProtoTasks.ofBuildType('debug').collect({ it.name }) as Set assert [ - 'generateArmFreeappReleaseProto', - 'generateArmFreeappReleaseUnitTestProto', - 'generateArmRetailappReleaseProto', - 'generateArmRetailappReleaseUnitTestProto', - 'generateX86FreeappReleaseProto', - 'generateX86FreeappReleaseUnitTestProto', - 'generateX86RetailappReleaseProto', - 'generateX86RetailappReleaseUnitTestProto', + 'armFreeappRelease', + 'armFreeappReleaseUnitTest', + 'armRetailappRelease', + 'armRetailappReleaseUnitTest', + 'x86FreeappRelease', + 'x86FreeappReleaseUnitTest', + 'x86RetailappRelease', + 'x86RetailappReleaseUnitTest', ] as Set == protobuf.generateProtoTasks.ofBuildType('release').collect({ it.name }) as Set - assert ['generateX86FreeappDebugAndroidTestProto'] as Set == + assert ['x86FreeappDebugAndroidTest'] as Set == protobuf.generateProtoTasks.ofVariant('x86FreeappDebugAndroidTest').collect({ it.name }) as Set // "androidTest" sourceSet is not a flavor diff --git a/testProjectAndroidKotlinDsl/build.gradle.kts b/testProjectAndroidKotlinDsl/build.gradle.kts index cad04866..2011786c 100644 --- a/testProjectAndroidKotlinDsl/build.gradle.kts +++ b/testProjectAndroidKotlinDsl/build.gradle.kts @@ -1,6 +1,7 @@ import com.android.build.gradle.api.BaseVariant import com.google.protobuf.gradle.id import com.google.protobuf.gradle.proto +import com.google.protobuf.gradle.internal.DefaultGenerateProtoTaskCollection plugins { id("com.android.application") @@ -105,13 +106,13 @@ protobuf { } } generateProtoTasks { - all().forEach { task -> - task.plugins { + all { + plugins { id("javalite") { } } } - ofNonTest().forEach { task -> - task.plugins { + ofNonTest { + plugins { id("grpc") { // Options added to --grpc_out option("lite") @@ -152,138 +153,138 @@ afterEvaluate { } test { doLast { - val genProtoTasks = project.protobuf.generateProtoTasks + val genProtoTasks = project.protobuf.generateProtoTasks as DefaultGenerateProtoTaskCollection val genProtoTaskNames = setOf( - "generateArmFreeappDebugAndroidTestProto", - "generateArmFreeappDebugUnitTestProto", - "generateArmFreeappReleaseUnitTestProto", - "generateArmFreeappDebugProto", - "generateArmFreeappReleaseProto", - "generateArmRetailappDebugAndroidTestProto", - "generateArmRetailappDebugUnitTestProto", - "generateArmRetailappReleaseUnitTestProto", - "generateArmRetailappDebugProto", - "generateArmRetailappReleaseProto", - "generateX86FreeappDebugAndroidTestProto", - "generateX86FreeappDebugUnitTestProto", - "generateX86FreeappReleaseUnitTestProto", - "generateX86FreeappDebugProto", - "generateX86FreeappReleaseProto", - "generateX86RetailappDebugAndroidTestProto", - "generateX86RetailappDebugUnitTestProto", - "generateX86RetailappReleaseUnitTestProto", - "generateX86RetailappDebugProto", - "generateX86RetailappReleaseProto") + "armFreeappDebugAndroidTest", + "armFreeappDebugUnitTest", + "armFreeappReleaseUnitTest", + "armFreeappDebug", + "armFreeappRelease", + "armRetailappDebugAndroidTest", + "armRetailappDebugUnitTest", + "armRetailappReleaseUnitTest", + "armRetailappDebug", + "armRetailappRelease", + "x86FreeappDebugAndroidTest", + "x86FreeappDebugUnitTest", + "x86FreeappReleaseUnitTest", + "x86FreeappDebug", + "x86FreeappRelease", + "x86RetailappDebugAndroidTest", + "x86RetailappDebugUnitTest", + "x86RetailappReleaseUnitTest", + "x86RetailappDebug", + "x86RetailappRelease") assert(genProtoTaskNames == genProtoTasks.all().map { it.name }.toSet()) val genProtoTaskNamesTests = setOf( - "generateArmFreeappDebugAndroidTestProto", - "generateArmFreeappDebugUnitTestProto", - "generateArmFreeappReleaseUnitTestProto", - "generateArmRetailappDebugAndroidTestProto", - "generateArmRetailappDebugUnitTestProto", - "generateArmRetailappReleaseUnitTestProto", - "generateX86FreeappDebugAndroidTestProto", - "generateX86FreeappDebugUnitTestProto", - "generateX86FreeappReleaseUnitTestProto", - "generateX86RetailappDebugAndroidTestProto", - "generateX86RetailappDebugUnitTestProto", - "generateX86RetailappReleaseUnitTestProto") + "armFreeappDebugAndroidTest", + "armFreeappDebugUnitTest", + "armFreeappReleaseUnitTest", + "armRetailappDebugAndroidTest", + "armRetailappDebugUnitTest", + "armRetailappReleaseUnitTest", + "x86FreeappDebugAndroidTest", + "x86FreeappDebugUnitTest", + "x86FreeappReleaseUnitTest", + "x86RetailappDebugAndroidTest", + "x86RetailappDebugUnitTest", + "x86RetailappReleaseUnitTest") assert(genProtoTaskNamesTests == genProtoTasks.ofNonTest().map { it.name }.toSet()) val genProtoTaskNamesNonTests = setOf( - "generateArmFreeappDebugProto", - "generateArmFreeappReleaseProto", - "generateArmRetailappDebugProto", - "generateArmRetailappReleaseProto", - "generateX86FreeappDebugProto", - "generateX86FreeappReleaseProto", - "generateX86RetailappDebugProto", - "generateX86RetailappReleaseProto") + "armFreeappDebug", + "armFreeappRelease", + "armRetailappDebug", + "armRetailappRelease", + "x86FreeappDebug", + "x86FreeappRelease", + "x86RetailappDebug", + "x86RetailappRelease") assert(genProtoTaskNamesNonTests == genProtoTasks.ofNonTest().map { it.name }.toSet()) val genProtoTaskNamesFreeApp = setOf( - "generateArmFreeappDebugAndroidTestProto", - "generateArmFreeappDebugUnitTestProto", - "generateArmFreeappReleaseUnitTestProto", - "generateArmFreeappDebugProto", - "generateArmFreeappReleaseProto", - "generateX86FreeappDebugAndroidTestProto", - "generateX86FreeappDebugUnitTestProto", - "generateX86FreeappReleaseUnitTestProto", - "generateX86FreeappDebugProto", - "generateX86FreeappReleaseProto") + "armFreeappDebugAndroidTest", + "armFreeappDebugUnitTest", + "armFreeappReleaseUnitTest", + "armFreeappDebug", + "armFreeappRelease", + "x86FreeappDebugAndroidTest", + "x86FreeappDebugUnitTest", + "x86FreeappReleaseUnitTest", + "x86FreeappDebug", + "x86FreeappRelease") assert(genProtoTaskNamesFreeApp == genProtoTasks.ofFlavor("freeapp").map { it.name }.toSet()) val genProtoTaskNamesRetailApp = setOf( - "generateArmRetailappDebugAndroidTestProto", - "generateArmRetailappDebugUnitTestProto", - "generateArmRetailappReleaseUnitTestProto", - "generateArmRetailappDebugProto", - "generateArmRetailappReleaseProto", - "generateX86RetailappDebugAndroidTestProto", - "generateX86RetailappDebugUnitTestProto", - "generateX86RetailappReleaseUnitTestProto", - "generateX86RetailappDebugProto", - "generateX86RetailappReleaseProto") + "armRetailappDebugAndroidTest", + "armRetailappDebugUnitTest", + "armRetailappReleaseUnitTest", + "armRetailappDebug", + "armRetailappRelease", + "x86RetailappDebugAndroidTest", + "x86RetailappDebugUnitTest", + "x86RetailappReleaseUnitTest", + "x86RetailappDebug", + "x86RetailappRelease") assert(genProtoTaskNamesRetailApp == genProtoTasks.ofFlavor("retailapp").map { it.name }.toSet()) val genProtoTaskNamesX86 = setOf( - "generateX86FreeappDebugAndroidTestProto", - "generateX86FreeappDebugUnitTestProto", - "generateX86FreeappReleaseUnitTestProto", - "generateX86FreeappDebugProto", - "generateX86FreeappReleaseProto", - "generateX86RetailappDebugAndroidTestProto", - "generateX86RetailappDebugUnitTestProto", - "generateX86RetailappReleaseUnitTestProto", - "generateX86RetailappDebugProto", - "generateX86RetailappReleaseProto") + "x86FreeappDebugAndroidTest", + "x86FreeappDebugUnitTest", + "x86FreeappReleaseUnitTest", + "x86FreeappDebug", + "x86FreeappRelease", + "x86RetailappDebugAndroidTest", + "x86RetailappDebugUnitTest", + "x86RetailappReleaseUnitTest", + "x86RetailappDebug", + "x86RetailappRelease") assert(genProtoTaskNamesX86 == genProtoTasks.ofFlavor("x86").map { it.name }.toSet()) val genProtoTaskNamesArm = setOf( - "generateArmFreeappDebugAndroidTestProto", - "generateArmFreeappDebugUnitTestProto", - "generateArmFreeappReleaseUnitTestProto", - "generateArmFreeappDebugProto", - "generateArmFreeappReleaseProto", - "generateArmRetailappDebugAndroidTestProto", - "generateArmRetailappDebugUnitTestProto", - "generateArmRetailappReleaseUnitTestProto", - "generateArmRetailappDebugProto", - "generateArmRetailappReleaseProto" + "armFreeappDebugAndroidTest", + "armFreeappDebugUnitTest", + "armFreeappReleaseUnitTest", + "armFreeappDebug", + "armFreeappRelease", + "armRetailappDebugAndroidTest", + "armRetailappDebugUnitTest", + "armRetailappReleaseUnitTest", + "armRetailappDebug", + "armRetailappRelease" ) assert(genProtoTaskNamesArm == genProtoTasks.ofFlavor("arm").map { it.name }.toSet()) val genProtoTaskNamesDebug = setOf( - "generateArmFreeappDebugAndroidTestProto", - "generateArmFreeappDebugUnitTestProto", - "generateArmFreeappDebugProto", - "generateArmRetailappDebugAndroidTestProto", - "generateArmRetailappDebugUnitTestProto", - "generateArmRetailappDebugProto", - "generateX86FreeappDebugAndroidTestProto", - "generateX86FreeappDebugUnitTestProto", - "generateX86FreeappDebugProto", - "generateX86RetailappDebugAndroidTestProto", - "generateX86RetailappDebugUnitTestProto", - "generateX86RetailappDebugProto") + "armFreeappDebugAndroidTest", + "armFreeappDebugUnitTest", + "armFreeappDebug", + "armRetailappDebugAndroidTest", + "armRetailappDebugUnitTest", + "armRetailappDebug", + "x86FreeappDebugAndroidTest", + "x86FreeappDebugUnitTest", + "x86FreeappDebug", + "x86RetailappDebugAndroidTest", + "x86RetailappDebugUnitTest", + "x86RetailappDebug") assert(genProtoTaskNamesDebug == genProtoTasks.ofBuildType("debug").map { it.name }.toSet()) val genProtoTaskNamesRelease = setOf( - "generateArmFreeappReleaseProto", - "generateArmFreeappReleaseUnitTestProto", - "generateArmRetailappReleaseProto", - "generateArmRetailappReleaseUnitTestProto", - "generateX86FreeappReleaseProto", - "generateX86FreeappReleaseUnitTestProto", - "generateX86RetailappReleaseProto", - "generateX86RetailappReleaseUnitTestProto") + "armFreeappRelease", + "armFreeappReleaseUnitTest", + "armRetailappRelease", + "armRetailappReleaseUnitTest", + "x86FreeappRelease", + "x86FreeappReleaseUnitTest", + "x86RetailappRelease", + "x86RetailappReleaseUnitTest") assert(genProtoTaskNamesRelease == genProtoTasks.ofBuildType("release").map { it.name }.toSet()) - assert(setOf("generateX86FreeappDebugAndroidTestProto") == + assert(setOf("x86FreeappDebugAndroidTest") == genProtoTasks.ofVariant("x86FreeappDebugAndroidTest").map { it.name }.toSet()) android.applicationVariants.forEach { variant -> diff --git a/testProjectAndroidLibrary/build.gradle b/testProjectAndroidLibrary/build.gradle index 4539129c..79a23857 100644 --- a/testProjectAndroidLibrary/build.gradle +++ b/testProjectAndroidLibrary/build.gradle @@ -65,13 +65,17 @@ protobuf { } } generateProtoTasks { - all()*.plugins { - javalite { } - } - ofNonTest()*.plugins { - grpc { - // Options added to --grpc_out - option 'lite' + all { + plugins { + javalite {} + } + ofNonTest { + plugins { + grpc { + // Options added to --grpc_out + option 'lite' + } + } } } } diff --git a/testProjectBase/build_base.gradle b/testProjectBase/build_base.gradle index 287e151d..95804688 100644 --- a/testProjectBase/build_base.gradle +++ b/testProjectBase/build_base.gradle @@ -45,13 +45,13 @@ protobuf { } } generateProtoTasks { - ofSourceSet('grpc').each { task -> - task.plugins { + ofSourceSet('grpc') { + plugins { grpc { outputSubDir = 'grpc_output' } } - task.generateDescriptorSet = true + generateDescriptorSet = true } } } @@ -81,10 +81,10 @@ def assertFileExists(boolean exists, String path) { } test.doLast { - assert ['generateProto', 'generateGrpcProto', 'generateTestProto'] as Set == - protobuf.generateProtoTasks.all().collect({ it.name }) as Set + assert ['main', 'grpc', 'test'] as Set == + protobuf.generateProtoTasks.all().collect({ it.name }) as Set - assert ['generateProto'] as Set == protobuf.generateProtoTasks.ofSourceSet('main').collect({ it.name }) as Set + assert ['main'] as Set == protobuf.generateProtoTasks.ofSourceSet('main').collect({ it.name }) as Set assert [] as Set == protobuf.generateProtoTasks.ofTest().collect({ it.name }) as Set assert [] as Set == protobuf.generateProtoTasks.ofNonTest().collect({ it.name }) as Set assert [] as Set == protobuf.generateProtoTasks.ofFlavor('flavor-name').collect({ it.name }) as Set diff --git a/testProjectBuildTimeProto/build.gradle b/testProjectBuildTimeProto/build.gradle index a4ba1364..462cd343 100644 --- a/testProjectBuildTimeProto/build.gradle +++ b/testProjectBuildTimeProto/build.gradle @@ -1,3 +1,5 @@ +import com.google.protobuf.gradle.GenerateProtoTask + plugins { id 'java' id 'com.google.protobuf' @@ -40,7 +42,8 @@ protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.0.0' } - generateProtoTasks { - all().each { task -> task.dependsOn generateProtobuf } - } +} + +tasks.withType(GenerateProtoTask).configureEach { + dependsOn generateProtobuf } diff --git a/testProjectKotlinDslBase/build.gradle.kts b/testProjectKotlinDslBase/build.gradle.kts index ed2a37a5..26a3cdd5 100644 --- a/testProjectKotlinDslBase/build.gradle.kts +++ b/testProjectKotlinDslBase/build.gradle.kts @@ -1,6 +1,7 @@ import com.google.protobuf.gradle.* import org.gradle.api.internal.HasConvention import org.gradle.kotlin.dsl.provider.gradleKotlinDslOf +import com.google.protobuf.gradle.internal.DefaultGenerateProtoTaskCollection plugins { java @@ -56,13 +57,13 @@ protobuf { } } generateProtoTasks { - ofSourceSet("grpc").forEach { task -> - task.plugins { + ofSourceSet("grpc") { + plugins { id("grpc") { outputSubDir = "grpc_output" } } - task.generateDescriptorSet = true + generateDescriptorSet = true } } } @@ -84,7 +85,7 @@ tasks { "test"{ doLast{ - val generateProtoTasks = project.protobuf.generateProtoTasks + val generateProtoTasks = project.protobuf.generateProtoTasks as DefaultGenerateProtoTaskCollection val generateProtoTaskNames = generateProtoTasks.all().map { it.name }.toSet() val generateProtoTaskNamesMain = generateProtoTasks.ofSourceSet("main").map { it.name }.toSet() diff --git a/testProjectLite/build.gradle b/testProjectLite/build.gradle index 62239c94..0d14e252 100644 --- a/testProjectLite/build.gradle +++ b/testProjectLite/build.gradle @@ -28,11 +28,11 @@ protobuf { } } generateProtoTasks { - all().each { task -> - task.builtins { + all { + builtins { remove java } - task.plugins { + plugins { javalite { } } }