使用Bazel构建C/C++项目
这是关于Bazel的第二篇blog,前一篇写了安装、配置相关的东西,这一篇则是4个逐步推进的例子,改编自官方demo;以及相应的概念、文档链接等。
前提
- Linux(Ubuntu, etc)或Mac OSX系统,会点儿命令行(包括brew/apt)
- 装好了zsh和oh-my-zsh(用于
bazel build
等命令的补全) - 装好了bazel;
- 学过C/C++;
- 用过make/cmake
- 最好会一点git
- bazel版本:目前我用0.21版本,最新版删过东西(https://docs.bazel.build/versions/0.21.0/be/workspace.html)
基本概念
WORKSPACE
: 空文件;标识了项目根目录;只有一个BUILD
:WORKSPACE下的子目录里,如果放了一个名为BUILD
的文件,则这个目录是一个package
;BUILD
里写一些构建规则 (rules)cc_binary
: C/C++package
的最常用的构建规则- bazel的C/C++在线文档:https://docs.bazel.build/versions/master/be/c-cpp.html
target
的概念:cmake中的target包括executable、library两种情况rule
的概念:类似于cmake中target
概念的推广,bazel构建C/C++时的规则有:- 即使是bazel build官方文档,也不明确区分target和rule字眼,可以认为是一个意思
- 大体上,
.bzl
相当于.cmake
文件,BUILD
相当于CMakeLists.txt
WORKSPACE
,BUILD
中用到了一些预定义的函数或变量,具体看这里:https://docs.bazel.build/versions/master/skylark/lib/skylark-overview.html
速查链接汇总
workspace规则
starlark预设全局变量
完整代码
stage1: 一个package, 一个target
这是最简单的bazel构建例子
目录结构
├── WORKSPACE
└── main
├── BUILD
└── hello.c
其中,main
为包名,因为它包含了BUILD
文件
hello.c
:
#include <stdio.h>
int main(void){
printf("hello from C!\n");
return 0;
}
BUILD
:
cc_binary(
name = "hello",
srcs = ["hello.c"],
)
执行构建
bazel build main:all
- 语法是
bazel build 包名:任务名
- 输入完
bazel build
后按tab
键补全提示,比较方便 - 因为目前只有一个target,也可以输入
bazel build main:hello
运行
bazel run main:all
它其实除了输出bazel相关的信息,执行的是./bazel-bin/hello
目录下的可执行文件hello
等
执行清除
bazel clean
stage2: 一个package,多个target
典型场景:写一个库,然后调用它。这里写一个神经网络激活函数库,然后写一个测试程序。
目录结构
├── WORKSPACE
└── main
├── BUILD
├── activations.c
├── activations.h
└── testbed.c
1 directory, 5 files
BUILD
:
cc_library(
name = "actv",
srcs = ["activations.c"],
hdrs = ["activations.h"],
)
cc_binary(
name = "actv-testbed",
srcs = ["testbed.c"],
deps = [
":actv",
],
)
activations.h
:
#ifndef __ACTIVATIONS_H__
#define __ACTIVATIONS_H__
float relu(float x);
float sigmoid(float x);
#endif
activations.c
:
#include "activations.h"
#include <math.h>
float relu(float x){
if (x>=0) return x;
return 0;
}
float sigmoid(float x){
return 1.0f / (1.0f + exp(-x));
}
testbed.c
:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include "activations.h"
// return a random float in (s, t)
float get_random(float s, float t){
float v = (float)(rand()) / RAND_MAX;
v = v * (t-s) + s;
return v;
}
int main(){
const int maxn = 5;
srand(time(0));
for(int i=0; i<maxn; i++) {
float x = get_random(-2.0f, 2.0f);
float res_relu = relu(x);
float res_sigmoid = sigmoid(x);
printf("x=%f\n", x);
printf("relu(x)=%f\n", res_relu);
printf("sigmoid(x)=%f\n", res_sigmoid);
printf("\n");
}
return 0;
}
构建库
bazel build main:actv
构建测试
bazel build main:actv-testbed
执行
bazel run main:actv-testbed
stage3: 多package,多target
主要是弄清楚,如何在不同package的target之间设定依赖关系,比如一个库target在其他包中是否可用(visibility),比如头文件的包含路径。
目录结构:
├── lib
│ ├── BUILD
│ ├── random.c
│ ├── random.h
│ ├── timer.c
│ └── timer.h
├── main
│ ├── activations.c
│ ├── activations.h
│ ├── BUILD
│ └── testbed.c
└── WORKSPACE
其中,lib/BUILD
:
cc_library(
name = "timer",
srcs = ["timer.c"],
hdrs = ["timer.h"],
visibility = ["//main:__pkg__"]
)
cc_library(
name = "random",
srcs = ["random.c"],
hdrs = ["random.h"],
visibility = ["//main:__pkg__"]
)
而main/BUILD
:
cc_library(
name = "actv",
srcs = ["activations.c"],
hdrs = ["activations.h"],
)
cc_binary(
name = "actv-testbed",
srcs = ["testbed.c"],
deps = [
":actv",
"//lib:random",
"//lib:timer"
],
)
各个源码文件其实都很简单,这里就不贴出来的,只需要注意在包含lib包里面的头文件时,main/testbed.c
是这样写的:
#include "activations.h"
#include "lib/timer.h"
#include "lib/random.h"
stage4: 使用外部依赖
这里说的外部依赖,包括:本地的其他目录,git仓库或http地址下载的。外部依赖可以是基于bazel构建的,也可以不是。
anyway,google家开源的abseil框架的hello-world例子可能是最典型的demo了。但是它同时用了gtest、gmock和abseil,感觉太麻烦了,干脆直接引入abseil外部库来写hello-world,如下:
目录结构
├── BUILD
├── hello.cc
└── WORKSPACE
这里看到并没有子目录,这也是可以的
WORKSPACE
# 非必须
workspace(name = "com_google_absl_hello_world")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# Abseil
http_archive(
name = "absl",
strip_prefix = "abseil-cpp-master",
urls = ["https://github.com/abseil/abseil-cpp/archive/master.zip"],
)
BUILD
cc_binary(
name = "hello",
srcs = ["hello.cc"],
deps = [
"@absl//absl/strings"
]
)
说明:因为在WORKSPACE
中载入了absl,所以BUILD
中可以使用@absl
。
hello.cc
#include <iostream>
#include <string>
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
using std::string;
using std::cout;
using std::endl;
string Greet(absl::string_view person) {
return absl::StrCat("Hello ", person);
}
int main(){
cout << Greet("world") << endl;
cout << Greet("ChrisZZ") << endl;
return 0;
}
执行构建
bazel build :hello
它根据WORKSPACE
中的设定(也就是http_archive
),从http下载。其他的方法还包括git_repository
的方式,不过最新版bazel中把这些都踢掉了,必须手动load一下bazel工具中的.bzl文件,才能用它们。。
参考:
https://stackoverflow.com/questions/45814669/c-project-with-bazel-and-gtest?noredirect=1&lq=1
https://github.com/vincent-picaud/Bazel_with_GTest
https://docs.bazel.build/versions/master/external.html
最佳实践
https://docs.bazel.build/versions/master/external.html#best-practices
使用Bazel构建C/C++项目的更多相关文章
- 为什么google bazel构建工具流行不起来
作者Jack47 转载请保留作者和原文出处 之前博主写了系列文章Google软件构建工具Bazel原理及使用方法介绍.最近使用了一段时间后,觉得这个东西不是一种通用的构建工具,很难对接到情况复杂的大的 ...
- 使用maven一步一步构建spring mvc项目
1 使用eclipse构建maven web项目 1.1新建Maven的web项目 打开菜单File –New-MavenProject. 点击Next 选择模板类型archtype——ma ...
- 使用Maven构建多模块项目
[转] 使用Maven构建多模块项目 在平时的Javaweb项目开发中为了便于后期的维护,我们一般会进行分层开发,最常见的就是分为domain(域模型层).dao(数据库访问层).service(业务 ...
- Eclipse的maven构建一个web项目,以构建SpringMVC项目为例
http://www.cnblogs.com/javaTest/archive/2012/04/28/2589574.html springmvc demo实例教程源代码下载:http://zuida ...
- 加速 Gradle 构建大型 Android 项目的方法[转]
加速 Gradle 构建大型 Android 项目的方法 时间 2016-03-14 20:38:00 Mystra 原文 http://www.wangchenlong.org/2016/03/ ...
- Eclipse Maven构建Spring MVC项目
工作中项目开发使用Maven管理项目的构建.打包.编译,框架採用的是Spring MVC框架,而且实现了多模块.多项目的管理.自己也简单的參与了架构的设计.对于刚開始学习的人来说,使用Maven构建项 ...
- 实现自动构建编译javaweb项目并发布到N台服务器
前言 当你使用nginx实现了负载均衡,当你有了超过3台以上的应用服务器时,一个特别头疼的问题就来了,发布项目好麻烦. 你每次都要在本地编译打包一遍,然后手动复制到每一台服务器上面去,如果只有一两台服 ...
- 用Gradle构建Spring Boot项目
相比起Maven的XML配置方式,Gradle提供了一套简明的DSL用于构建Java项目,使我们就像编写程序一样编写项目构建脚本.本文将从无到有创建一个用Gradle构建的Spring Boot项目, ...
- Ant + Jenkies +Tomcat 自动构建部署Web项目
前言:博主资历尚浅,很多东西都还在刚起步学习的阶段,这几天开发任务比较轻,就在自己window系统下,模拟部署远程服务器,利用Jenkies + Ant + Tomcat 搭建了一个自动发布部署的环境 ...
随机推荐
- Debian 为firefox安装flash插件 以及 音频驱动
1. flash下载地址:http://get.adobe.com/cn/flashplayer/ lv@lv:~/Downloads$ tar -zxvf flash_player_npapi_li ...
- 2018 Multi-University Training Contest 3 杭电多校第三场
躺了几天 终于记得来填坑了 1001 Ascending Rating (hdoj 6319) 链接:http://acm.hdu.edu.cn/showproblem.php?pid=6319 ...
- 测试cpu的简单工具-dhrystone【转】
转自:https://blog.csdn.net/feixiaoxing/article/details/9005587 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog ...
- make 命令【转】
转自:https://www.ibm.com/support/knowledgecenter/zh/ssw_aix_71/com.ibm.aix.cmds3/make.htm#make__row-d3 ...
- Linux下的Jenkins+Tomcat+Maven+Git+Shell环境的搭建使用(jenkins自动化部署)【转】
jenkins自动化部署 目标:jenkins上点构建(也可以自动检查代码变化自动构建)>>>项目部署完成. 一.安装jenkins 1.下载jenkins 这里我选择的是war包安 ...
- mac技巧之常用的快键键
1.修改文件名 选中文件按return键即可修改文件名. 2.文件预览 选中文件按照空格键即可实现文件的预览(并不是打开文件) 3.任务之间进行切换 command+tab 4.复制文件 推动文件按照 ...
- 华为交换机有关BGP的相关配置
作者:邓聪聪 上图是本人在某公司任职期间的一次割接任务,在原有的路由器上新配置的另一台高性能的路由器,两台设备为并行 割接要求: 1:原有的网络结构无变化,并行新设备 2:原有设备下的所有用户无变化 ...
- ADO读写DateTime方式
// 读取日期 var = m_pResultSet->GetCollect(_variant_t("Birth_Time")); DATE dt = var.date; C ...
- Linux下编写和加载 .ko 文件(驱动模块文件)
一..ko 文件介绍 .ko文件是kernel object文件(内核模块),该文件的意义就是把内核的一些功能移动到内核外边, 需要的时候插入内核,不需要时卸载. 二.优点 (1)这样可以缩小内核体积 ...
- PHP程序守护进程化
一般Server程序都是运行在系统后台,这与普通的交互式命令行程序有很大的区别.glibc里有一个函数daemon.调用此函数,就可使当前进程脱离终端变成一个守护进程,具体内容参见man daemon ...