从零学习构造系统之bazel示例详解

目录
  • 什么是bazel
  • Bazel的基本概念
    • WORKSPACE file
    • BUILD file
    • Bazel的C++事例
      • 单个目标,单个软件包
      • 单个软件包,多个目标
      • 多个软件包,多个目标
  • 最后

什么是bazel

Bazel 是一款与 Make、Maven 和 Gradle 类似的开源构建和测试工具。 它使用人类可读懂的高级 build 语言。Bazel 支持使用多种语言的项目,并针对多个平台构建输出。Bazel 支持跨多个代码库和大量用户的大量代码库。

Bazel的基本概念

WORKSPACE file

在构建项目之前,我们需要先设置工作区,工作区的作用是存储项目的源文件和Bazel的构建输出的目录,其中WORKSPACE file就是将目录及其内容标识为Bazel工作区的文件,需要位于项目目录结构的根目录下,该文件可以为空,但是通常包含从网络或者本地文件系统中提取其他依赖项的外部代码库声明。

BUILD file

一个项目中包含一个或多个BUILD file,BUILD主要用于告知Bazel如何构建项目,工作区包含一个BUILD文件的目录就是一个软件包。在之前的文章提到过,Bazel是基于工件的构建系统,而工件就可以理解为Bazel根据Build文件中的编译规则编译该目录下的文件形成的,软件包可以包含子软件包或包含BUILD文件的子目录,从而形成软件包的层次。

BUILD文件中包含了一些适用于Bazel的不同类型的指令,每个BUILD文件都需要包含至少一条规则(如cc_library)作为一组指令,以指示Bazel如何构建所需要的输出,例如可执行文件或库。BUILD文件中的每一个build规则实例都称为目标,并指向一组特定的源文件和依赖项,一个目标也可以指向其他目标。以下就是一个简单的BUILD文件的例子。

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

在本示例中,hello-world 目标会实例化 Bazel 的内置 cc_binary rule。该规则会告知 Bazel 从 hello-world.cc 源文件构建不含依赖项的独立可执行文件。

Bazel的C++事例

该例子是Bazel官方提供的,github地址为git clone github.com/bazelbuild/…

单个目标,单个软件包

我们从最简单的例子开始,例子只包含一个源文件,需要将改源文件构建成一个可执行文件,其文件目录如下。

examples
└── cpp-tutorial
    └──stage1
       ├── main
       │   ├── BUILD
       │   └── hello-world.cc
       └── WORKSPACE

BUILD文件内容如下

load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
)

然后进入该目录cpp-tutorial/stage1,运行以下命令构建

bazel build //main:hello-world

会打印以下信息表示编译成功

Starting local Bazel server and connecting to it...
INFO: Analyzed target //main:hello-world (37 packages loaded, 255 targets configured).
INFO: Found 1 target...
Target //main:hello-world up-to-date:
bazel-bin/main/hello-world
INFO: Elapsed time: 28.089s, Critical Path: 2.78s
INFO: 6 processes: 4 internal, 2 darwin-sandbox.
INFO: Build completed successfully, 6 total actions

然后可以使用以下命令测试,会打印Hello world

bazel-bin/main/hello-world

其依赖关系如下

单个软件包,多个目标

虽然单个目标足以满足小型项目的需求,在实际的开发过程中可能需要将较大的项目拆分为多个目标和软件包。这样可以实现快速增量构建(即 Bazel 仅重建更改的内容,并通过一次性构建项目的多个部分来加快构建速度。在教程的这一阶段,您将添加目标,然后添加软件包。

下面的BUILD文件会告诉Bazel先构建hello-greet库(使用Bazel内置的cc_library)然后构建hello-world二进制文件,其中的deps属性告诉Bazel构建hello-world需要hello-greet库。

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
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",
    ],
)

编译情况如下

INFO: Analyzed target //main:hello-world (36 packages loaded, 258 targets configured).
INFO: Found 1 target...
Target //main:hello-world up-to-date:
bazel-bin/main/hello-world
INFO: Elapsed time: 15.548s, Critical Path: 2.39s
INFO: 8 processes: 4 internal, 4 darwin-sandbox.
INFO: Build completed successfully, 8 total actions

如果现在修改 hello-greet.cc 并重新构建项目,Bazel 只会重新编译该文件,编译结果如下(通过-s选项可以打印具体编译过程)

INFO: Analyzed target //main:hello-world (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
SUBCOMMAND: # //main:hello-greet [action 'Compiling main/hello-greet.cc', configuration: a42135a42aad3da7e3af209ce54745fb0d0306dc29e1f3dc84d7d58372421fc9, execution platform: @local_config_platform//:host]
(cd /private/var/tmp/_bazel_qiming/e4d33fbb5ee1f924b3cb079f19abf4eb/execroot/__main__ && \
  exec env - \
    APPLE_SDK_PLATFORM=MacOSX \
    APPLE_SDK_VERSION_OVERRIDE=13.1 \
    PATH=/Users/qiming/Library/Caches/bazelisk/downloads/bazelbuild/bazel-6.0.0-darwin-x86_64/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin \
    XCODE_VERSION_OVERRIDE=14.2.0.14C18 \
    ZERO_AR_DATE=1 \
  external/local_config_cc/wrapped_clang_pp '-D_FORTIFY_SOURCE=1' -fstack-protector -fcolor-diagnostics -Wall -Wthread-safety -Wself-assign -fno-omit-frame-pointer -O0 -DDEBUG '-std=c++11' 'DEBUG_PREFIX_MAP_PWD=.' -iquote . -iquote bazel-out/darwin-fastbuild/bin -MD -MF bazel-out/darwin-fastbuild/bin/main/_objs/hello-greet/hello-greet.d '-DBAZEL_CURRENT_REPOSITORY=""' '-frandom-seed=bazel-out/darwin-fastbuild/bin/main/_objs/hello-greet/hello-greet.o' -isysroot __BAZEL_XCODE_SDKROOT__ -F__BAZEL_XCODE_SDKROOT__/System/Library/Frameworks -F__BAZEL_XCODE_DEVELOPER_DIR__/Platforms/MacOSX.platform/Developer/Library/Frameworks -no-canonical-prefixes -pthread -no-canonical-prefixes -Wno-builtin-macro-redefined '-D__DATE__="redacted"' '-D__TIMESTAMP__="redacted"' '-D__TIME__="redacted"' -target x86_64-apple-macosx13.1 -c main/hello-greet.cc -o bazel-out/darwin-fastbuild/bin/main/_objs/hello-greet/hello-greet.o)
# Configuration: a42135a42aad3da7e3af209ce54745fb0d0306dc29e1f3dc84d7d58372421fc9
# Execution platform: @local_config_platform//:host
Target //main:hello-world up-to-date:
  bazel-bin/main/hello-world
INFO: Elapsed time: 0.750s, Critical Path: 0.58s
INFO: 2 processes: 1 internal, 1 darwin-sandbox.
INFO: Build completed successfully, 2 total actions

其中依赖如下图

多个软件包,多个目标

在包含多个软件包的情况下又该如何编译呢?其结构如下:

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

lib目录下的BUILD文件如下

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

main目录下的BUILD文件如下

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
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 属性知道这一点。依赖项图中反映了这一点:

为了顺利构建,请使用可见性属性使 lib/BUILD 中的 //lib:hello-time 目标明确对 main/BUILD 中的目标可见。这是因为默认情况下,目标仅对同一 BUILD 文件中的其他目标可见。Bazel 使用目标可见性来防止出现包含实现细节的库泄露到公共 API 等问题。

最后

这篇文章主要介绍了构造系统bazel的简单使用,更多关于bazel构造系统的资料请关注我们其它相关文章!

(0)

相关推荐

  • 详解如何使用Bazel构建Golang程序

    目录 使用Bazel构建Golang程序 从头开始一个 Golang 项目 将现有项目转换为 Bazel 构建 密封测试(Hermetic tests) 使用Bazel构建Golang程序 在这篇简短的文章中,我们将介绍如何将 Golang 与 Bazel 构建系统结合使用. 具体来说,我们将讨论三个场景: 从头开始一个 Golang 项目: 将一个现有的 Golang 项目转换为 Bazel 构建: 以及将一个第三方 Golang 项目引入到您的 Bazel 构建系统. 从头开始一个 Gola

  • 使用Bazel编译TensorBoard教程

    1.TensorBoard Tensorboard是一套用于查看和理解TensorFlow运行情况的工具,有时可能现有的功能并不能满足我们当前的需求,那么则需要我们对Tensorboard进行定制化开发,定制化的第一步就是编译源码. TensorBoard已在github上开源,我们可以通过https://github.com/tensorflow/tensorboard获取到完整的代码.包括TensorBoard在内,Google的很多项目都是使用Bazel进行编译的,接下来我们进行相关环境的

  • Go语言学习教程之反射的示例详解

    目录 介绍 反射的规律 1. 从接口值到反射对象的反射 2. 从反射对象到接口值的反射 3. 要修改反射对象,该值一定是可设置的 介绍 reflect包实现运行时反射,允许一个程序操作任何类型的对象.典型的使用是:取静态类型interface{}的值,通过调用TypeOf获取它的动态类型信息,调用ValueOf会返回一个表示运行时数据的一个值.本文通过记录对reflect包的简单使用,来对反射有一定的了解.本文使用的Go版本: $ go version go version go1.18 dar

  • TensorFlow人工智能学习数据合并分割统计示例详解

    目录 一.数据合并与分割 1.tf.concat() 2.tf.split() 3.tf.stack() 二.数据统计 1.tf.norm() 2.reduce_min/max/mean() 3.tf.argmax/argmin() 4.tf.equal() 5.tf.unique() 一.数据合并与分割 1.tf.concat() 填入两个tensor, 指定某维度,在指定的维度合并.除了合并的维度之外,其他的维度必须相等. 2.tf.split() 填入tensor,指定维度,指定分割的数量

  • 人工智能学习Pytorch教程Tensor基本操作示例详解

    目录 一.tensor的创建 1.使用tensor 2.使用Tensor 3.随机初始化 4.其他数据生成 ①torch.full ②torch.arange ③linspace和logspace ④ones, zeros, eye ⑤torch.randperm 二.tensor的索引与切片 1.索引与切片使用方法 ①index_select ②... ③mask 三.tensor维度的变换 1.维度变换 ①torch.view ②squeeze/unsqueeze ③expand,repea

  • 人工智能学习Pytorch梯度下降优化示例详解

    目录 一.激活函数 1.Sigmoid函数 2.Tanh函数 3.ReLU函数 二.损失函数及求导 1.autograd.grad 2.loss.backward() 3.softmax及其求导 三.链式法则 1.单层感知机梯度 2. 多输出感知机梯度 3. 中间有隐藏层的求导 4.多层感知机的反向传播 四.优化举例 一.激活函数 1.Sigmoid函数 函数图像以及表达式如下: 通过该函数,可以将输入的负无穷到正无穷的输入压缩到0-1之间.在x=0的时候,输出0.5 通过PyTorch实现方式

  • python神经网络学习数据增强及预处理示例详解

    目录 学习前言 处理长宽不同的图片 数据增强 1.在数据集内进行数据增强 2.在读取图片的时候数据增强 3.目标检测中的数据增强 学习前言 进行训练的话,如果直接用原图进行训练,也是可以的(就如我们最喜欢Mnist手写体),但是大部分图片长和宽不一样,直接resize的话容易出问题. 除去resize的问题外,有些时候数据不足该怎么办呢,当然要用到数据增强啦. 这篇文章就是记录我最近收集的一些数据预处理的方式 处理长宽不同的图片 对于很多分类.目标检测算法,输入的图片长宽是一样的,如224,22

  • Git基础学习之分支操作的示例详解

    目录 1.新建一个分支并且使分支指向指定的提交对象 2.思考 3.项目分叉历史的形成 4.分支的总结 1.新建一个分支并且使分支指向指定的提交对象 使用命令:git branch branchname commitHash. 我们现在本地库中只有一个 master 分支,并且在 master 分支有三个提交历史. 需求:创建一个 testing 分支,并且testing 分支指向 master 分支第二个版本. # 1.查看提交历史记录 L@DESKTOP-T2AI2SU MINGW64 /j/

  • react后台系统最佳实践示例详解

    目录 一.中后台系统的技术栈选型 1. 要做什么 2. 要求 3. 技术栈怎么选 二.hooks时代状态管理库的选型 context redux recoil zustand MobX 三.hooks的使用问题与解决方案 总结 一.中后台系统的技术栈选型 本文主要讲三块内容:中后台系统的技术栈选型.hooks时代状态管理库的选型以及hooks的使用问题与解决方案. 1. 要做什么 我们的目标是搭建一个适用于公司内部中后台系统的前端项目最佳实践. 2. 要求 由于业务需求比较多,一名开发人员需要负

  • React学习笔记之列表渲染示例详解

    前言 本文主要给大家介绍了关于React列表渲染的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 示例详解: 列表渲染也很简单,利用map方法返回一个新的渲染列表即可,例如: const numbers = [1, 2, 3, 4, 5]; const listItems = numbers.map((number) => <li>{number}</li> ); ReactDOM.render( <ul>{listItems}<

  • Struts2学习教程之输入校验示例详解

    前言 数据校验几乎是每个应用都要做的工作.用户输入的数据,发送到服务器端,天知道用户输入的数据是否是合法的,是否为恶意输入.所以一个健壮的应用系统必须对用户的输入进行校验,将非法的输入阻止在应用之外,防止这些非法的输入进入系统,从而保证系统的稳定性.安全性. 我们都知道,为了更好的用户体验,以及更高的效率,现在的Web应用都存在以下两重数据校验: 客户端数据校验 服务器端数据校验 对于客户端数据校验主要是通过JavaScript代码来完成:而对于服务器端数据校验是整个应用阻止非法数据的最后防线,

  • Python3学习笔记之列表方法示例详解

    前言 本文主要给大家介绍了关于Python3列表方法的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 1 使用[]或者list()创建列表 user = [] user = list() 2 使用list() 可以将其他类型转换成列表 # 将字符串转成列表 >>> list('abcde') ['a', 'b', 'c', 'd', 'e'] # 将元祖转成列表 >>> list(('a','b','c')) ['a', 'b', 'c']

随机推荐