diff --git a/README.md b/README.md index 06f92161..290e9e8d 100644 --- a/README.md +++ b/README.md @@ -315,12 +315,10 @@ task.plugins { ```gradle protobuf { - generatedFilesBaseDir = "$projectDir/generated" - generateProtoTasks { all().each { task -> task.builtins { - // Generates Python code in the output folder: + // Generates Python code python { } // If you wish to avoid generating Java files: @@ -410,7 +408,7 @@ protobuf { ```gradle { task -> // If true, will generate a descriptor_set.desc file under - // $generatedFilesBaseDir/$sourceSet. Default is false. + // task.outputBaseDir. Default is false. // See --descriptor_set_out in protoc documentation about what it is. task.generateDescriptorSet = true @@ -430,19 +428,11 @@ protobuf { #### Change where files are generated -By default generated Java files are under -``$generatedFilesBaseDir/$sourceSet/$builtinPluginName``, where -``$generatedFilesBaseDir`` is ``$buildDir/generated/source/proto`` by default, -and is configurable. E.g., - -```gradle -protobuf { - ... -  generatedFilesBaseDir = "$projectDir/src/generated" -} -``` +Generated files are under `task.outputBaseDir` with a subdirectory per +builtin and plugin. This produces a folder structure of +``$buildDir/generated/source/proto/$sourceSet/$builtinPluginName``. -The subdirectory name, which is by default ``$builtinPluginName``, can also be +The subdirectory name, which is by default ``$builtinPluginName``, can be changed by setting the ``outputSubDir`` property in the ``builtins`` or ``plugins`` block of a task configuration within ``generateProtoTasks`` block (see previous section). E.g., @@ -451,8 +441,7 @@ changed by setting the ``outputSubDir`` property in the ``builtins`` or { task -> task.plugins { grpc { - // Write the generated files under - // "$generatedFilesBaseDir/$sourceSet/grpcjava" + // Use subdirectory 'grpcjava' instead of the default 'grpc' outputSubDir = 'grpcjava' } } @@ -521,29 +510,6 @@ This plugin integrates with the ``idea`` plugin and automatically registers the proto files and generated Java code as sources. -```gradle -apply plugin: 'idea' - -protobuf { - ... - generatedFilesBaseDir = "$projectDir/gen" -} - -clean { - delete protobuf.generatedFilesBaseDir -} - -idea { - module { - // proto files and generated Java files are automatically added as - // source dirs. - // If you have additional sources, add them here: - sourceDirs += file("/path/to/other/sources"); - } -} -``` - - ## Testing the plugin ``testProject*`` are testing projects that uses this plugin to compile diff --git a/config/codenarc/codenarc.xml b/config/codenarc/codenarc.xml index 17af312e..12e743c1 100644 --- a/config/codenarc/codenarc.xml +++ b/config/codenarc/codenarc.xml @@ -82,9 +82,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + + diff --git a/src/main/groovy/com/google/protobuf/gradle/CopyActionFacade.java b/src/main/groovy/com/google/protobuf/gradle/CopyActionFacade.java index 804f3aa7..64e08389 100644 --- a/src/main/groovy/com/google/protobuf/gradle/CopyActionFacade.java +++ b/src/main/groovy/com/google/protobuf/gradle/CopyActionFacade.java @@ -33,8 +33,11 @@ import org.gradle.api.Action; import org.gradle.api.Project; import org.gradle.api.file.CopySpec; +import org.gradle.api.file.DeleteSpec; import org.gradle.api.file.FileSystemOperations; +import org.gradle.api.model.ObjectFactory; import org.gradle.api.tasks.WorkResult; +import org.gradle.util.GradleVersion; import javax.inject.Inject; @@ -46,6 +49,18 @@ @CompileStatic interface CopyActionFacade { WorkResult copy(Action var1); + WorkResult delete(Action action); + + @CompileStatic + final class Loader { + public static CopyActionFacade create(Project project, ObjectFactory objectFactory) { + if (GradleVersion.current().compareTo(GradleVersion.version("6.0")) >= 0) { + // Use object factory to instantiate as that will inject the necessary service. + return objectFactory.newInstance(CopyActionFacade.FileSystemOperationsBased.class); + } + return new CopyActionFacade.ProjectBased(project); + } + } @CompileStatic class ProjectBased implements CopyActionFacade { @@ -59,6 +74,11 @@ public ProjectBased(Project project) { public WorkResult copy(Action action) { return project.copy(action); } + + @Override + public WorkResult delete(Action action) { + return project.delete(action); + } } @CompileStatic @@ -70,5 +90,10 @@ abstract class FileSystemOperationsBased implements CopyActionFacade { public WorkResult copy(Action action) { return getFileSystemOperations().copy(action); } + + @Override + public WorkResult delete(Action action) { + return getFileSystemOperations().delete(action); + } } } diff --git a/src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy b/src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy index 2e6f2510..ff419f2e 100644 --- a/src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy @@ -81,6 +81,7 @@ public abstract class GenerateProtoTask extends DefaultTask { static final int CMD_ARGUMENT_EXTRA_LENGTH = 3 private static final String JAR_SUFFIX = ".jar" + private final CopyActionFacade copyActionFacade = CopyActionFacade.Loader.create(project, objectFactory) // include dirs are passed to the '-I' option of protoc. They contain protos // that may be "imported" from the source protos, but will not be compiled. private final ConfigurableFileCollection includeDirs = objectFactory.fileCollection() @@ -587,6 +588,9 @@ public abstract class GenerateProtoTask extends DefaultTask { void compile() { Preconditions.checkState(state == State.FINALIZED, 'doneConfig() has not been called') + copyActionFacade.delete { spec -> + spec.delete(outputBaseDir) + } // Sort to ensure generated descriptors have a canonical representation // to avoid triggering unnecessary rebuilds downstream List protoFiles = sourceDirs.asFileTree.files.sort() diff --git a/src/main/groovy/com/google/protobuf/gradle/ProtobufExtension.groovy b/src/main/groovy/com/google/protobuf/gradle/ProtobufExtension.groovy index 74c090e0..0de530f8 100644 --- a/src/main/groovy/com/google/protobuf/gradle/ProtobufExtension.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/ProtobufExtension.groovy @@ -37,6 +37,7 @@ import groovy.transform.TypeCheckingMode import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project +import org.gradle.api.provider.Property import org.gradle.api.tasks.TaskCollection /** @@ -52,18 +53,16 @@ abstract class ProtobufExtension { private final ArrayList> taskConfigActions private final NamedDomainObjectContainer sourceSets - /** - * The base directory of generated files. The default is - * "${project.buildDir}/generated/source/proto". - */ - private String generatedFilesBaseDir + @PackageScope + final String defaultGeneratedFilesBaseDir public ProtobufExtension(final Project project) { this.project = project this.tasks = new GenerateProtoTaskCollection(project) this.tools = new ToolsLocator(project) this.taskConfigActions = [] - this.generatedFilesBaseDir = "${project.buildDir}/generated/source/proto" + this.defaultGeneratedFilesBaseDir = "${project.buildDir}/generated/source/proto" + this.generatedFilesBaseDirProperty.convention(defaultGeneratedFilesBaseDir) this.sourceSets = project.objects.domainObjectContainer(ProtoSourceSet) { String name -> new DefaultProtoSourceSet(name, project.objects) } @@ -80,13 +79,21 @@ abstract class ProtobufExtension { } String getGeneratedFilesBaseDir() { - return generatedFilesBaseDir + return generatedFilesBaseDirProperty.get() } + @Deprecated void setGeneratedFilesBaseDir(String generatedFilesBaseDir) { - this.generatedFilesBaseDir = generatedFilesBaseDir + generatedFilesBaseDirProperty.set(generatedFilesBaseDir) } + /** + * The base directory of generated files. The default is + * "${project.buildDir}/generated/source/proto". + */ + @PackageScope + abstract Property getGeneratedFilesBaseDirProperty() + @PackageScope void configureTasks() { this.taskConfigActions.each { action -> diff --git a/src/main/groovy/com/google/protobuf/gradle/ProtobufExtract.groovy b/src/main/groovy/com/google/protobuf/gradle/ProtobufExtract.groovy index 3da54eab..faafcae8 100644 --- a/src/main/groovy/com/google/protobuf/gradle/ProtobufExtract.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/ProtobufExtract.groovy @@ -57,7 +57,7 @@ import javax.inject.Inject @CompileStatic abstract class ProtobufExtract extends DefaultTask { - private final CopyActionFacade copyActionFacade = instantiateCopyActionFacade() + private final CopyActionFacade copyActionFacade = CopyActionFacade.Loader.create(project, objectFactory) private final ArchiveActionFacade archiveActionFacade = instantiateArchiveActionFacade() private final FileCollection filteredProtos = instantiateFilteredProtos() @@ -98,14 +98,6 @@ abstract class ProtobufExtract extends DefaultTask { @Inject protected abstract ProviderFactory getProviderFactory() - private CopyActionFacade instantiateCopyActionFacade() { - if (GradleVersion.current() >= GradleVersion.version("6.0")) { - // Use object factory to instantiate as that will inject the necessary service. - return objectFactory.newInstance(CopyActionFacade.FileSystemOperationsBased) - } - return new CopyActionFacade.ProjectBased(project) - } - private ArchiveActionFacade instantiateArchiveActionFacade() { if (GradleVersion.current() >= GradleVersion.version("6.0")) { // Use object factory to instantiate as that will inject the necessary service. diff --git a/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy b/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy index 8d237f4c..9853f0ee 100644 --- a/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy +++ b/src/main/groovy/com/google/protobuf/gradle/ProtobufPlugin.groovy @@ -374,15 +374,29 @@ class ProtobufPlugin implements Plugin { ) { String sourceSetName = protoSourceSet.name String taskName = 'generate' + Utils.getSourceSetSubstringForTaskNames(sourceSetName) + 'Proto' - Provider outDir = project.providers.provider { - "${this.protobufExtension.generatedFilesBaseDir}/${sourceSetName}".toString() - } + String defaultGeneratedFilesBaseDir = protobufExtension.defaultGeneratedFilesBaseDir + Provider generatedFilesBaseDirProvider = protobufExtension.generatedFilesBaseDirProperty Provider task = project.tasks.register(taskName, GenerateProtoTask) { + CopyActionFacade copyActionFacade = CopyActionFacade.Loader.create(it.project, it.objectFactory) it.description = "Compiles Proto source for '${sourceSetName}'".toString() - it.outputBaseDir = outDir + it.outputBaseDir = project.providers.provider { + "${defaultGeneratedFilesBaseDir}/${sourceSetName}".toString() + } it.addSourceDirs(protoSourceSet.proto) it.addIncludeDir(protoSourceSet.proto.sourceDirectories) it.addIncludeDir(protoSourceSet.includeProtoDirs) + it.doLast { task -> + String generatedFilesBaseDir = generatedFilesBaseDirProvider.get() + if (generatedFilesBaseDir == defaultGeneratedFilesBaseDir) { + return + } + // 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.into("${generatedFilesBaseDir}/${sourceSetName}") + } + } configureAction.execute(it) } protoSourceSet.output.from(task.map { GenerateProtoTask it -> it.outputSourceDirectories })