• 使用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规则
    starlark预设全局变量
    完整代码

    stage1: 一个package, 一个target

    这是最简单的bazel构建例子

    目录结构

    ├── WORKSPACE
    └── main
        ├── BUILD
        └── hello.c
    

    其中,main为包名,因为它包含了BUILD文件

    hello.c:

    #include <stdio.h>
    
    int main(void){
        printf("hello from C!
    ");
        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
    ", x);
            printf("relu(x)=%f
    ", res_relu);
            printf("sigmoid(x)=%f
    ", res_sigmoid);
            printf("
    ");
        }
    
        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

  • 相关阅读:
    (转)(mark)VM参数设置、分析
    用happen-before规则重新审视DCL(转)
    java synchronized的优化--偏向锁、轻量级锁、重量级锁
    leet_14 Longest Common Prefix
    leet_15
    leet_11
    Github-浅谈
    深究angularJS系列
    深究angularJS系列
    CSS编程框架
  • 原文地址:https://www.cnblogs.com/zjutzz/p/10305301.html
Copyright © 2020-2023  润新知