google/protobuf-gradle-plugin

How to set output directory for generated Java files.

maddin79 opened this issue · 9 comments

Hello,

first thanks for your work. I'm pretty new to Gradle, coming from Maven, and find no way to set the output directory for the generated Java files. In the moment they are copied to the build folder (working with Intellij by the way), but that does not make any sense to me. I need the compiled .class files in the build directory and the Java files in my src/... folder. I want to copy them to a specific location to use them in a Apache Kafka Streams app. Sorry to say, but in Maven this is a 5 minutes job. Can not be that difficult to configure the generation path or do I miss something here?

Any help is welcome. Thanks in advance.

Best Martin

I have the same question. Seems like none of the options work at the moment. I tried below but still the code was generated in the default directory.

    generateProtoTasks {
        all().forEach { task ->
            task.plugins {
                id("grpc") {
                    println("$rootDir/buildpo")
                    outputSubDir = "$rootDir/buildpo"
                }
            }
        }
    }

and this as well

generatedFilesBaseDir = "${rootDir}/buildpo"

@manish7-thakur I wrote a dirty workaround. I did not have the time to dig into the problem myself and using protoc directly was faster. I used an exec task to run a bash script that uses the protoc command.

Here is the script (generate_java.sh):

#!/usr/bin/env bash

$(dirname "$0")/protoc --proto_path=$1 --java_out=$2 $1/*.proto

Here is the gradle exec task:

tasks.register<Exec>("generateJava"){
    var generate_java = layout.projectDirectory.file("bin/generate_java.sh")
    var src_dir = layout.projectDirectory.dir("src/main/proto")
    var dest_dir = layout.projectDirectory.dir("src/main/java")
    commandLine(generate_java, src_dir, dest_dir)
}

It is not flexible at all, but works for now. I hope someone looks into the problem soon. Would be great

@manish7-thakur, see https://github.com/google/protobuf-gradle-plugin/releases/tag/v0.9.2 . generatedFilesBaseDir is fundamentally broken because it mixes the generated code output with whatever source files you have laying around. Using it can produce incorrect builds, because old generated code is not deleted. You need to use Sync, not Copy. But generatedFilesBaseDir will still cause the files to be output to that directory in 0.9.2+. Note that generatedFilesBaseDir is not input to the build task, so if you change it it won't cause the plugin to regenerate the output. Try a clean and rebuild.

@ejona86 Thanks for the clarification. In my opinion is does not make sense to copy the files into the build folder in the first place. Java files are not needed there and the developer is responsible to keep his source folder clean.
Is there no possibility to tell the program where to copy the file and also a flag to delete files created before? I'm mean protoc does exactly that. I do not have enough experience with gradle to dig into it.

The plugin will use its own directory in the build/ directory. It deletes the existing files and dumps the new files. The generatedFilesBaseDir feature was used by people to mix the generated code with their existing code, which is just trouble.

The plugin now interacts properly with the sourceSets, so other plugins looking for source code should find the generated code. IDEs are a bit of a problem, but for the inane reason that they ignore the directory if it does not already exist (although we have some workarounds for that).

The Sync task can be used to copy files, deleting existing files in the output directory. If you need to copy the files, I suggest using that instead of generatedFilesBaseDir.

I do actually want to allow changing the destination directory, but so that you can create your own GenerateProtoTask tasks. But there's some trouble doing that today because of the wide range of Gradle versions we support (I can do it an old way, or a new way, but it is hard to support both). After we drop some versions, it will be easier.

I would love to have that possibility. Looking forward to that.

I still do not get why copying the .java files into the build directory. No IDE will look there for Java files. I would have to make a really dirty configuration to also use the generated java files in the build directory in my code.
In my opinion it would be a lot more straight forward to have an option to set the output directory directly (even a mandatory setting) in the plugin instead of programming around it with sync tasks. This could also then be cleaned before every build.
Does that make sense? Maybe I miss a certain information and do not have the full picture.

No IDE will look there for Java files.

Eclipse, IntelliJ, and VSCode all will, if you are using their Gradle integration. Again, there's a problem with them ignoring the directory if the directory doesn't already exist, so right now you also need to apply the eclipse (Eclipse, VSCode) or idea (IntelliJ) plugin in your build script to cause this plugin to eagerly create the directories.

In my opinion it would be a lot more straight forward to have an option to set the output directory directly (even a mandatory setting) in the plugin instead of programming around it with sync tasks. This could also then be cleaned before every build.

This does not help the build/IDE problem, because you can't use directories like src/main/java because those contain your hand-written source code and so you can't delete the files within them. You need to use a different directory which means you still have the build/IDE problem.

In my opinion that is not true what you are saying. Gradle is definitely not looking for java source files in the build folder. That would also make no sense, because this build folder is created from gradle doing the build and is used for compiled classes. Why should a build tool look for source files in a folder, that was created by that build tool before?
Furthermore, that has nothing to do with the IDE you using. I seldom use plugins for eclipse or idea and the build folder is always created from gradle. You can read about it here:
https://docs.gradle.org/current/userguide/organizing_gradle_projects.html

and here:
https://docs.gradle.org/current/userguide/java_plugin.html

You could change that behavior with source sets, but I would not do that. Makes it difficult for others to get your project configuration. I also saw that only for really special cases.

The deletion of files is pretty easy with an own task and the delete command. You can delete files all over your projects. As example:

tasks.register('customCleanUp', Delete) {
    delete "${rootDir}/src/main/java/org/example/Test.java"
}

tasks.build.dependsOn(tasks.customCleanUp)

Works perfect even if the file is not there. Read about it here: https://docs.gradle.org/current/userguide/working_with_files.html#sec:deleting_files_example
If you want you could even use ant tasks in gradle for this jobs, but I also would not do that.

I was also a curious about what you said and tried it myself. Never tested this before. The result was as I described above.
If you point me to the code where to set options for the plugin, use the options while running and creating an special task for deletion, I try to look into it in the next weeks.