对于多个源码文件的项目,一般在代码模块设计上有两类做法:
1)把主入口的main的文件作为可执行分类;
2)将其它部分认为可以定义为lib的源码作为lib分类;
同时在Bazel中也有两种做法(bazel对于Package内部的拆分采用多target来区分,每个target其实就是BUILD文件中的一个个编译构建函数):
1)定义在一个package中,内部区分可执行程序和lib,在bazel中定义称为拆成多个编译构建的target。
2)定义在不同的Package中,通过不同的package区分可执行程序和不同的lib,当然这种方式更适合大粒度的模块化拆分。
这里先在一个Package中看看多源文件拆分分类的情况,Bazel是如何构建的。该项目实例结构如下:
└──TestBazelHello
├──PrintfTime
│ ├── BUILD
│ ├── PrintfTime.cpp
│ ├── PrintfTime.h
│ ├── TestPrintfTime.cpp
├── ....待生成编译构建文件
└── WORKSPACE
准备源代码
源代码的内容和之前相同,只是将其拆分成多个文件存放。
源文件PrintfTime.h
#include <ctime>
#include <string>
#include <iostream>
std::string get_greet(const std::string& who);
void print_localtime();
源文件PrintfTime.cpp
#include "PrintfTime.h"
std::string get_greet(const std::string& who) {
return "Hello " + who;
}
void print_localtime() {
std::time_t result = std::time(nullptr);
std::cout << std::asctime(std::localtime(&result));
}
包含main入口的源文件TestPrintfTime.cpp
#include "PrintfTime.h"
int main(int argc, char** argv) {
std::string who = "world";
if (argc > 1) {
who = argv[1];
}
std::cout << get_greet(who) << std::endl;
print_localtime();
return 0;
}
拆成多个源文件后,为了便于拆分管理,计划将拆分出来的PrintfTime.h/PrintfTime.cpp打包成lib,将拆分的包含入口main源文件通过依赖lib方式编译构建出可执行程序。最大的区别在BUILD文件中的构建规则。
BUILD文件:
cc_library( //表明为一个lib库
name = "PrintfTime", //库的名字
srcs = ["PrintfTime.cpp"], //源文件
hdrs = ["PrintfTime.h"], //头文件
)
cc_binary( //表明为一个可执行程序
name = "TestPrintfTime", //可执行程序名
srcs = ["TestPrintfTime.cpp"], //源文件
deps = [ //表明需要依赖库
":PrintfTime", //需要依赖库的名称
],
)
上传源码后编译构建
进入项目上传目录检查该上传的代码文件列表,没有缺少的情况下,运行编译构建命令。
回到工作空间一级目录,执行编译命令“bazel build //PrintfTime:TestPrintfTime”:
此时来看看构建生成的目录:
进入生成的可执行文件目录,运行生成的可执行程序:
查看依赖关系
这种拆分一旦完成,那么我们如何来查看一个项目的依赖关系呢?这里我们扩展开来,介绍一个好用的查询依赖关系的工具“graphviz”。
这个可视化的工具可以在windows操作系统上安装,网上可以搜索下载地址,安装基本上默认安装即可。(注意要把该工具的安装的bin目录添加到path环境变量)
那么bazel编译构建后的程序如何体现依赖关系呢,bazel提供了query指令,通过该命令,能生成依赖关系的数据,我们将该数据输入到graphviz工具即可生成依赖图。
1)生成依赖数据
2)windows上打开graphviz工具,新建一个文件
3)输入生成的依赖数据:
输出该可执行程序项目的依赖关系:
该项目可执行程序为TestPrintfTime,依赖一个Timelib库和一个包含main入口的源文件。