Bazel编译Java项目

进入到Bazel的教程中来,按照官网的编排,选择熟悉的Java项目对一些细节进行学习。我认为的整个学习路径是

最最大的框架学习 -》简单项目的代码学习 -》重要的概念总结 -》复杂一些的项目代码学习 -》加深对框架细节的理解以及更新对概念的理解 -》实际项目的入手构造 -》各种问题的解决思路 -》更新对框架的理解以及重要概念的总结 -》… 的循环往复。

下面就开始一个简单的项目代码学习吧

构造一个Java工程

在这个tutorial中,我要学习的是使用Bazel来build基础的Java应用程序。需要set up这个工程的workspace以及Java工程,来实际阐述Bazel的一些关键概念:target和BUILD文件等。

获得样例代码

git clone https://github.com/bazelbuild/examples/

在examples/java-tutoral目录下的结构为:

java-tutorial

├── BUILD
├── src
│ └── main
│ └── java
│ └── com
│ └── example
│ ├── cmdline
│ │ ├── BUILD
│ │ └── Runner.java
│ ├── Greeting.java
│ └── ProjectRunner.java
└── WORKSPACE

使用Bazel进行build

Set up the workspace

set up the workspace主要包括两个部分:

  1. WORKSPACE文件:在Bazel的workspace根目录下创建一个WORKSPACE文件。
  2. BUILD文件:一个或多个BUILD文件,用来告诉Bazel如何build工程的不同part(workspace内包含BUILD文件的文件夹称之为package)。

在这个example中,为了指示当前这个目录为Bazel的workspace,所以创建了一个名为WORKSPACE的空文件。当Bazel build工程时,所有的输入和依赖在相同的workspace中都必须是一致的。

理解BUILD文件

一个BUILD文件包含很多种不同的Bazel指令类型。其中最最重要的一种类型是build rule,这个告诉了Bazel如何build获得要想的输出(bin或者lib等)。BUILD文件中每一个build rule的实例称之为target,并且指明了所需要的源码文件和依赖。一个target也可以指到另外一个target上。

以java-tutorial/BUILD下的BUILD文件为例:

java_binary(
name = “ProjectRunner”,
srcs = glob([“src/main/java/com/example/*.java”]),
)

在这个例子中,目标ProjectRunner是通过Bazel内置的一个build rule:java_binary来实例化的。这个rule会让Bazel去build一个.jar文件。对于这个内置的build rule详细说明参见Java Rules。

Build the project

进入java-tutorial目录后,使用bazel命令行来buil:

bazel build //:ProjectRunner

其中需要说明两个符号含义:

  • //是指当前要build的BUILD文件的位置处于workspace的根目录下面的相对位置(在这个样例中是在根目录下)
  • ProjectRunner是在BUILD文件中定义的target的名字。

运行完上面这个命令后就可以在bazel-bin目录下得到可执行文件了。

Review依赖图

Bazel所需的build依赖关系都在BUILD文件中显示的声明了。Bazel可以使用这些语句来产生工程的依赖图以及保证增量编译的正确性。可以通过下面命令得到graphviz的dot图信息。

bazel query –nohost_deps –noimplicit_deps ‘deps(//:ProjectRunner)’ –output graph

上面这个命令是让bazel产生目标ProjectRunner的依赖图信息,而且不包含host依赖和隐式依赖。然后在http://www.webgraphviz.com/上可以直接复制进去生成图片。除了这种方式之外,还可以通过安装graphviz来本地得到绘图结果:

sudo apt-get install graphviz

bazel query –nohost_deps –noimplicit_deps ‘deps(//:ProjectRunner)’ –output graph > Runner.dot

dot Runner.dot -T png -o Runner.png
eog Runner.png

就可以查看图片了。但是对于项目较大的依赖图,如上操作会导致系统出错,然后自动注销。也就是说生成图片的格式和过程还需要再斟酌。别问我怎么知道的,我真是作死小能手,不然这篇文章我会再重写一遍?!!

完善Bazel build

当一个工程较大时,往往一个BUILD是不够的,我们需要把一个大工程分割成多个target以及多个package来管理。这样也能更好的快速增量编译。

1、使用多个build target

将原来的java-tutorial/BUILD文件修改成下面这个形式:

java_binary(
name = “ProjectRunner”,
srcs = [“src/main/java/com/example/ProjectRunner.java”],
main_class = “com.example.ProjectRunner”,
deps = [“:greeter”],
)

java_library(
name = “greeter”,
srcs = [“src/main/java/com/example/Greeting.java”],
)

这样就有两个target了,build的时候会首先build greeter,然后是ProjectRunner。

2、使用多packages

查看src/main/java/com/example/cmdline目录,会发现这里也有一个BUILD文件,那么就是说在当前workspace中包含有两个packages。这个BUILD文件为:

java_binary(
name = “runner”,
srcs = [“Runner.java”],
main_class = “com.example.cmdline.Runner”,
deps = [“//:greeter”]

)

如果此时在workspace的root目录下bazel build //src/main/java/com/example/cmdline:runner

会发现有错误:

Target ‘//:greeter’ is not visible from target ‘//src/main/java/com/example/cmdline:runner’. Check the visibility declaration of the former target if you think the dependency is legitimate

这是因为target的visibility属性导致的。target只默认在同一个BUILD文件中可见。此时需要修改目标greeter为:

java_library(
name = “greeter”,
srcs = [“src/main/java/com/example/Greeting.java”],
visibility = [“//src/main/java/com/example/cmdline:__pkg__”],
)

此时再运行就没有问题了。

此时生成的可执行文件在bazel-bin/src/main/java/com/example/cmdline/下面,也就是说BUILD文件在root目录中的相对路径决定了在bazel中的输出位置。

使用label来关联target

在BUILD文件中以及在命令行中,Bazel使用target label来关联target。比如之前看到的//:ProjectRunner或者//src/main/java/com/example/cmdline:runner这个命名规则是:

//path/to/package:target-name

如果target是一个rule target,那么path/to/package指的是到所包含BUILD文件的路径,并且target-name是在BUILD文件中调用rule时所写的name属性值。

如果target是一个file-target,那path/to/package指的是到package的root的路径,并且target-name是包含全路径的target file。

如果关联的target是在同一个package中,那么可以省略前面的path/to/package,直接写成//:target-name在同一个BUILD文件中,当然还可以省略//直接写成:target-name

打包Java的target用于部署

为了能够让一个jar包可以部署,不依赖开发环境运行,也就是把所有的运行依赖都打包在一起。还需要再做一些步骤。通过上面的bazel build已经得到一个可执行文件runner(也就是bash命令文件)以及一个runner.jar。但是这个jar本身并不能独立运行。通过命令查看可以看到:

jar tf bazel-bin/src/main/java/com/example/cmdline/runner.jar

META-INF/
META-INF/MANIFEST.MF
com/
com/example/
com/example/cmdline/
com/example/cmdline/Runner.class

可以看到缺少Greeting.class这个依赖,这个依赖是在runner这个可执行脚本中加入进去的。所以我们不能独立运行java -jar runner.jar这个包。为了能够独立运行以及部署。我们需要在build时加入一点修改,通过加入_deploy.jar这个后缀来实现:

bazel build //src/main/java/com/example/cmdline:runner_deploy.jar

此时可以得到runner_deploy.jar,对这个jar包再jar tf就能看到依赖的Greeting.class以及打包进去,而且java -jar也能运行了。

转载需保留链接来源:VCBeta.CN » Bazel编译Java项目

赞 (1)