grpc gradle
对不起。 没有权力游戏的笑话。 诺言。
所以,你在用Bazel 。 可能是因为你以前有过monorepo。 或者,也许你只是认为你的Android项目中模块太多。 或者仅仅因为很难管理你GitHub上整齐分离的存储库中拥有的所有那些微服务。 没关系 现在,你坚持用Bazel。
当您切换到Bazel丢失的一件事是丰富的插件生态系统,它提供了Maven和Gradle等工具。
此外,Gradle还允许您在Kotlin中编写DSL甚至定制任务。 这些善良永远消失了吗? 也许不会。
让我们看看如何使用一个Gradle插件(本例为KrotoPlus)并使其与Bazel一起使用。
在开始之前,让我们大致介绍一下KrotoPlus插件和gRPC 。
所有gRPC库都将.proto
以您选择的语言输出文件为输入:
- 留言内容
- 客户群
- 服务器接口
为便于讨论, 消息只是你的纯数据对象。 它们会有一些字段,这些字段会有类型。 这就是你现在需要知道的一切。
客户端 (也称存根)用于调用gRPC服务对象。 要创建客户端,通常需要指定gRPC服务器的主机和端口,然后调用一种方法择消息传递给这种方法。
服务器实现生成的服务器接口 ,然后开始调查正确的主机和端口,为客户提供服务。
如下:
gRPC Library:( proto files ) => (generated classes)
Gradle喜欢使用定义明确的项目结构,即:
your-gradle-project src main proto
当您运行Gradle当插件简单工作时,这是因为Gradle足够聪明,可以识别您的源集并将插件应用到它们中。
所以,如果你的话proto
有一些目录.proto
文件,我们的Gradle在build在目录中生成一些Java和Kotlin文件:
your-gradle-project build <-- your generated files will appear there src main proto
另一方面,Bazel对这种干净的结构一无所知。
此外,您的proto
文件将位于your-gradle-project
除目录外,如下所示:
some-proto-project proto <-- some proto files ... your-gradle-project src main proto <-- empty ... other-proto-project api proto <-- more proto files
我们想做的是使用它Bazel下的Gradle作为一个函数,该函数将被获得.proto
文件的路径就像它们是它们Source Set部分相同,然后根据这些文件生成类。
有几种方法可以实现Gradle但这就是其中之一:
import com.google.protobuf.gradle.* ... protobuf { ... generatedFilesBaseDir = " $buildDir /generated-sources" ... dependencies { protobuf(files(project.properties[ "protoDir" ].toString())) } generateProtoTasks { ... } }
重要部分是dependencies
块。 在这里,我们没有像往常一样硬编码依赖项,而是从命令行获取它们:
project.properties["protoDir" ].toString()
这意味着当我们运行Gradle包装时,我们现在将相对路径传输到.proto
文件:
./gradlew generateProto -PprotoDir=../path/to/protos
我们的Gradle现在就可以出发了。 接下来,让我们开始使用它Bazel。
想要使用Bazel入侵时,你的主要工具之一是gerule 。
这是表达您想在Bash shell中运行某些东西的基本方法。
但是那是什么“东西”? 它应该是gradlew
,与我们之前使用的包装相同。 但是问题是,Bazel并没有意识到这一点。
因此,让我们在gradlew
所在的目录中创建一个名为BUILD
的文件,并添加以下代码块:
filegroup(
name ="gradle" ,
srcs = [ "gradlew" ],
)
这样我们告诉Bazel:嘿,这是您应该知道的新文件。
但是,如果您尝试以这种方式运行它,Gradle会抱怨它找不到其配置。
那是因为我们只告诉Bazel将gradlew
复制到它的沙箱,而不是其他任何东西。
让我们通过添加另一个保存配置的filegroup
来解决此问题:
filegroup(
name ="gradle_config" ,
srcs = [ "build.gradle.kts" , "krotoPlusConfig.asciipb" , "settings.gradle" ]
+ glob([ "gradle/**/*" ])
+ glob([ "scripts/**/*" ]),
visibility = [ "//visibility:public" ]
)
如您所见,我们获取了build.gradle.kts
以及build.gradle.kts
配置文件以及Gradle包装器本身(位于/gradle
目录下)。
现在,到规则本身。 起初,它看起来令人生畏,但我们将逐行将其分解。
genrule(
name ="run_gradle" ,
cmd = """
./$(location gradlew) -p $$(dirname ./$(location gradlew)) generateProto -PprotoDir=../proto/ &&
cp -R $$(dirname ./$(location gradlew))/build/generated-sources $(@D)
""" ,
outs = [ "build/generated-sources" ],
message = "Generating protos" ,
srcs = [],
tools = [ ":gradle" ] + [ ":gradle_config" ],
)
-
name
是在指定库的依赖项时要引用的标签。 -
cmd
是将在Shell中运行的命令。 我们待会再分解 -
outs
是此命令产生的。 Bazel尝试跟踪脚本的每个输出,因此您需要在执行cmd之后告诉它您希望在文件系统中更改的内容。 如果目录build / generated-sources保持不变,Bazel将抱怨。 -
message
是您等待时将显示的内容 -
srcs
是您要在其上运行脚本的输入文件。 理想情况下,这些文件将是您的.proto
文件。 但是在这种情况下,我们将其保留为空,因为我们使用-PprotoDir
-
tools
是生成输出所需使用的文件列表。 在我们的例子中,它是Gradle Wrapper和所有配置。
现在,让我们回到cmd
并了解内部发生了什么。
启动Bazel时,其根目录位于WORKSPACE
文件位置,而不是gradlew
所在的目录。 因此,我们使用$(location gradlew)
将标签扩展到其相对位置。 在我们的示例中,它将类似于./your-gradle-project
找到Gradle Wrapper可执行文件后,我们需要告诉它在哪里可以找到其配置。 我们使用-p
,这是标准的Gradle标志。 我们知道该目录还是相对于我们的WORKSPACE
,因此是./$(location gradlew)
。 但是这次,它必须是目录,并且位置将扩展为文件的路径,因此我们使用$(dirname)
。 但是Bazel已经将$
用作$(location)
。 因此,我们最终筛选了以下命令: $$(dirname)
找到配置后,我们需要告诉Gradle要运行哪个任务。 那是generateProto
现在,我们需要将Gradle指向.proto
文件的位置。 但是上下文在这里从Bazel切换到Gradle,因此路径现在相对于gradlew
位置: -PprotoDir=../proto/
现在Gradle和KrotoPlus努力工作来生成我们的gRPC类。 问题是,它们是在Gradle目录中生成的,而不是在Bazel沙箱中生成的。
因此,我们需要复制它们。 cp -R
是一个标准的shell命令,我们已经介绍了$$dirname()
含义。 您还不熟悉的唯一部分是$(@D)
那就是我们用outs
指定的输出目录,因此,将生成的文件复制到我们想要的位置。
结论和下一步
如果比较麻烦,可以将Bazel和Gradle组合以生成文件。
如果您想走这条路,那么下一步应该是:
- 记得自己清理一下。
rm -rf $$gradleDir/build
会很好 - 您可能想利用这种
genrule
的功能。 语法几乎保持不变,但是在函数内部时,您需要在genrule
加上native
:native.genrule
- 从长远来看,像使用
-PprotoDir
一样使用相对路径不是正确的选择。 尽管对于测试KrotoPlus插件很有用。 更好的选择是依靠标签。
希望,如果您已经在使用Bazel,那么本文将使您走上正确的道路。
翻译自: https://hackernoon.com/gradle-bazel-and-grpc-a-song-of-ice-and-fire-n04w330hh
grpc gradle