• bazel构建C++工程


    1.bazel介绍

    Bazel是一个开源的构建和测试工具,类似于Make、Maven和Gradle。Bazel支持多种语言的项目,并为多种平台构建输出。Bazel支持跨多个存储库和大量用户的大型代码库。

    2.bazel安装

    bazel安装有两种方法,一种是通过源安装,另一种是通过下载安装包本地安装。官网安装教程。本人选取的是第一种安装方式。
    第一步:添加Bazel分发URI作为包源

    sudo apt install curl gnupg
    curl https://bazel.build/bazel-release.pub.gpg | sudo apt-key add -
    echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
    

    第二步:安装或更新bazel

    sudo apt update && sudo apt install bazel
    

    如果已经安装了bazel,需要升级到最新版本的bazel

    sudo apt update && sudo apt full-upgrade
    

    安装特定版本的bazel,例如需要安装bazel-1.0.0

    sudo apt install bazel-1.0.0
    

    3.bazel构建工程

    bazel里面有构建C++工程的例子,可以通过命令git clone https://github.com/bazelbuild/examples下载,本教程的示例项目位于examples/cpp-tutorial目录中,其结构如下:

    examples
    └── cpp-tutorial
        ├──stage1
        │  ├── main
        │  │   ├── BUILD
        │  │   └── hello-world.cc
        │  └── WORKSPACE
        ├──stage2
        │  ├── main
        │  │   ├── BUILD
        │  │   ├── hello-world.cc
        │  │   ├── hello-greet.cc
        │  │   └── hello-greet.h
        │  └── WORKSPACE
        └──stage3
           ├── main
           │   ├── BUILD
           │   ├── hello-world.cc
           │   ├── hello-greet.cc
           │   └── hello-greet.h
           ├── lib
           │   ├── BUILD
           │   ├── hello-time.cc
           │   └── hello-time.h
           └── WORKSPACE
    

    下面对这个C++工程进行解读。

    3.1 设置工作区

    在构建项目将之前,需要设置项目工作区,这个工作区就是一个包含项目源文件的目录和bazel构建输出,bazel识别WORKSPACEBUILD这两个文件。

    • WORKSPACE文件将目录及其内容标识为Bazel工作区,位于项目目录结构的根目录中
    • BUILD文件会有一个或者多个,分别构建项目的不同部分。

    3.2 理解BUILD文件

    BUILD文件包含Bazel的几种不同类型的指令。最重要的类型是构建规则,它告诉Bazel如何构建所需的输出,比如可执行的二进制文件或库。构建文件中构建规则的每个实例称为目标,并指向一组特定的源文件和依赖项。一个目标也可以指向其他目标。
    看一下cpp-tutorial/stage1/main中BUILD文件:

    cc_binary(
        name = "hello-world",
        srcs = ["hello-world.cc"],
    )
    

    在我们的示例中,hello-world目标实例化了Bazel的内置cc_binary规则。规则告诉bazel建立一个独立的可执行二进制hello world.cc源文件没有依赖性。

    3.3 使用bazel编译项目

    构建示例项目。切换到cpp-tutorial/stage1目录,运行以下命令:

    bazel build //main:hello-world
    

    注意目标标签——//main:部分是构建文件相对于工作区根的位置,hello-world是我们在构建文件中命名的目标。
    Bazel输出结果如下:

    INFO: Found 1 target...
    Target //main:hello-world up-to-date:
      bazel-bin/main/hello-world
    INFO: Elapsed time: 2.267s, Critical Path: 0.25s
    

    现在测试新构建的二进制文件:

    bazel-bin/main/hello-world
    

    3.4 查看依赖图

    成功的构建将在构建文件中显式地声明其所有依赖项。Bazel使用这些语句创建项目的依赖关系图,从而实现精确的增量构建。
    将示例项目的依赖关系可视化。首先,生成依赖关系图的文本表示(在工作区根运行此命令):

    bazel query --notool_deps --noimplicit_deps "deps(//main:hello-world)" 
      --output graph
    

    上面的命令告诉Bazel寻找target //main:hello-world的所有依赖项(不包括主机和隐式依赖项),并将输出格式化为图形。
    然后,将文本粘贴到GraphViz中。
    然后你可以通过管道直接输出到xdot来生成和查看图形:

    sudo apt update && sudo apt install graphviz xdot
    

    然后你可以通过管道直接输出到xdot来生成和查看图形:

    xdot <(bazel query --notool_deps --noimplicit_deps "deps(//main:hello-world)" 
      --output graph)
    

    如你所见,这个示例项目的第一阶段有一个单一的目标,它构建一个没有附加依赖关系的单一源文件:

    接下来,增加点难度

    3.5 多个目标文件(target)编译

    将示例项目构建分成两个目标。看一下cpp-tutorial/stage2/主目录中的构建文件:

    cc_library(
        name = "hello-greet",
        srcs = ["hello-greet.cc"],
        hdrs = ["hello-greet.h"],
    )
    
    cc_binary(
        name = "hello-world",
        srcs = ["hello-world.cc"],
        deps = [
            ":hello-greet",
        ],
    )
    

    对于这个构建文件,Bazel首先构建hello-greet库(使用Bazel内置的cc_library规则),然后构建hello-world二进制文件。hello-world目标中的deps属性告诉Bazel需要hello-greet库来构建hello-world二进制文件。
    切换到cpp-tutorial/stage2目录,运行以下命令:

    bazel build //main:hello-world
    

    Bazel的输出如下:

    INFO: Found 1 target...
    Target //main:hello-world up-to-date:
      bazel-bin/main/hello-world
    INFO: Elapsed time: 2.399s, Critical Path: 0.30s
    

    现在测试新构建的二进制文件:

    bazel-bin/main/hello-world
    

    如果修改hello-greet.cc和重建项目,Bazel将只重新编译该文件。
    看看依赖关系图,你可以看到hello-world和以前一样依赖于相同的输入,但是构建的结构是不同的:

    现在已经构建了带有两个目标的项目。hello-world目标构建一个源文件,并依赖于另一个目标(//main:hello-greet)。

    3.6 多个项目包(packages)编译

    现在将项目分离成多个包,看一下cpp-tutorial/stage3目录的内容:

    └──stage3
       ├── main
       │   ├── BUILD
       │   ├── hello-world.cc
       │   ├── hello-greet.cc
       │   └── hello-greet.h
       ├── lib
       │   ├── BUILD
       │   ├── hello-time.cc
       │   └── hello-time.h
       └── WORKSPACE
    

    注意,现在有两个子目录,每个子目录都包含一个BUILD文件。因此,对于Bazel来说,工作空间现在包含两个包,libmain
    先看lib/BUILD文件

    cc_library(
        name = "hello-time",
        srcs = ["hello-time.cc"],
        hdrs = ["hello-time.h"],
        visibility = ["//main:__pkg__"],
    )
    

    还有main/BUILD文件:

    cc_library(
        name = "hello-greet",
        srcs = ["hello-greet.cc"],
        hdrs = ["hello-greet.h"],
    )
    
    cc_binary(
        name = "hello-world",
        srcs = ["hello-world.cc"],
        deps = [
            ":hello-greet",
            "//lib:hello-time",
        ],
    )
    

    主包中的hello-world目标依赖于lib包中的hello-time目标(因此是目标标签//lib:hello-time)—Bazel通过deps属性知道这一点。看看依赖关系图:

    请注意,为了使构建成功,我们使用visibility属性使lib/ build中的//lib:hello-time目标对main/ build中的目标显式可见。这是因为在默认情况下,目标只对同一构建文件中的其他目标可见。(Bazel使用目标可见性来防止诸如库中包含的实现细节泄漏到公共api等问题。)
    切换到cpp-tutorial/stage3目录,运行以下命令:

    bazel build //main:hello-world
    

    Bazel的输出如下:

    INFO: Found 1 target...
    Target //main:hello-world up-to-date:
      bazel-bin/main/hello-world
    INFO: Elapsed time: 0.167s, Critical Path: 0.00s
    

    现在测试新构建的二进制文件:

    bazel-bin/main/hello-world
    

    到此具有三个目标的两个包的项目构建完成,并了解了它们之间的依赖关系。

    4.Reference

    1. Google开源构建工具Bazel
    2. Introduction to Bazel: Build a C++ Project
  • 相关阅读:
    List Curry
    List Polymorphic
    List Fibonacci
    搭建 docker + nginx + keepalived 实现Web应用的高可用(亲测)
    Java 大数相乘、大数相加、大数相减
    剑指offer —— 从尾到头打印链表
    剑指offer —— 替换空格
    剑指offer —— 二维数组的查找
    JDK源码 Integer.bitCount(i)
    Springboot 实现前台动态配置数据源 (修改数据源之后自动重启)
  • 原文地址:https://www.cnblogs.com/zhongzhaoxie/p/13410228.html
Copyright © 2020-2023  润新知