Bazel的入门
上一篇博文讲解了在Ubuntu14.04下Bazel的安装,接下来讲解一下Bazel的简单入门。
*使用工作区*
Bazel builds应该在一个工作区内运行,这个工作区应该包括源代码和build输出目录的符号链接(eg: bazel-bin、bazel-out)。工作区目录的位置是可以随意,但工作区的根目录必须包含一个名为WORKSPACE的工作区配置文件,工作区配置文件可以是一个空文件,也可以包含引用外部构建输出所需的依赖关系。在一个工作区内,可以根据需求共享多个项目。
- 1
- 1
*创建Build文件*
Bazel通过检查BUIDL文件可以知道那些目标文件被创建在项目中,这些BUILD文件采用与Python相似的语法所写,这种语言通常是一系列规则的声明,每个规则指定相应的输入、输出以及实现输入到输出的方法。
为了进一步了解bazel的使用,在这里分别以Java和C++进行介绍。
Java教程
假设目录中已经有了一个项目,对应 ~/gitroot/my-project/ 目录,先创建一个空的 ~/gitroot/my-project/WORKSPACE 工作区配置文件,用于表示这是Bazel项目对应的根目录。
*创建自己的Build构建文件*
使用下面的命令创建一个简单的Java项目:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
Bazel通过工作区中所有名为 BUILD 的文件来解析需要构建项目的信息,因此,需要先在 ~/gitroot/my-project 目录创建一个 BUILD 构建文件。下面是BUILD构建文件的内容:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
java_binary 是一个构建规则,其中 glob([“*/.java”])表示递归包含每个子目录中以每个.java为后缀名的文件,.
其中 name 对应一个构建目标的标识符,可用用它来向Bazel指定构建哪个项目。srcs 对应一个源文件列表,Bazel需要将这些源文件编译为二进制文件。其中 glob([“*/.java”]) 表示递归包含每个子目录中以每个 .java 为后缀名的文件。com.example.ProjectRunner 指定包含main方法的类。
现在用下面的命令构建这个Java程序了:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
恭喜你刚刚成功构建了第一个Bazel项目了!
*添加依赖关系*
对于一个小项目,创建一个规则就可以了,但随着项目的变大,则需要分别构建项目的不同部件,最终再组装成产品。这种构建方式能避免因为局部细小的改变而重构整个应用,同时不同的构建步骤可以很好地并发执行以提高构建的效率。
现在将一个项目拆分不同的部件独立构建,同时设置他们之间的依赖关系。基于上面的例子,重写BUILD构建文件:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
尽管源文件一样,但是现在Bazel将采用不同的方式来构建:首先构建 greeter 库,然后构建 my-other-runner。在构建成功后运行 //:my-other-runner:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
现在如果修改ProjectRunner.java代码并重新构建my-other-runner,Greeting.java文件将不会重重新被编译。
*使用多个包*
对于大的项目
For larger projects, you will often be dealing with several directories. You can refer to targets defined in other BUILD files using the syntax //path/to/directory:target-name. For example, suppose src/main/java/com/example/ has a cmdline/ subdirectory with the following file:
对于更大的项目,通常需要将它们拆分到多个目录中。可以用类似//path/to/directory:target-name的名字引用在其他BUILD文件定义的目标。例如,假设src/main/java/com/example/有一个cmdline/子目录,包含下面的文件:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
Runner.java依赖com.example.Greeting,因此需要在src/main/java/com/example/cmdline/BUILD构建文件中添加相应的依赖规则:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
然而,在默认情况下构建目标都是私有的。也就是说,只能在同一个BUILD文件中被引用,这可以避免将很多实现的细节暴漏给公共的接口,但是也意味着我们需要手工允许runner所依赖的//:greeter目标。类似下面这个在构建runner目标时遇到的错误:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
可用通过在BUILD文件增加visibility = level属性来改变目标的可见范围。下面是通过在~/gitroot/my-project/BUILD文件增加可见规则,来改变greeter目标的可见范围:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
这个规则使//:greeter目标对于//src/main/java/com/example/cmdline包是可见的。现在可以重新构建运行runner目标程序:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
可见性配置说明请参考文档。
*部署*
如果查看 bazel-bin/src/main/java/com/example/cmdline/runner.jar 的内容,可以看到只包含了Runner.class,并没有保护所依赖的Greeting.class:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
这只能在本机正常工作(Bazel的runner脚本已经将greeter的jar添加到了classpath),但是如果将runner.jar单独复制到另一台机器上是不能正常运行。为了构建可用于部署发布的自包含所有依赖的目标,可以构建runner_deploy.jar目标(类似_deploy.jar以_deploy为后缀的名字对应可部署目标)。
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
runner_deploy.jar中将包含全部的依赖。