使用Bazel構建C/C++項目

這是關於Bazel的第二篇blog,前一篇寫了安裝、配置相關的東西,這一篇則是4個逐步推動的例子,改編自官方demo;以及相應的概念、文檔連接等。html

[TOC]python

前提

  • 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預設全局變量 完整代碼ios

stage1: 一個package, 一個target

這是最簡單的bazel構建例子git

目錄結構github

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

其中,main爲包名,由於它包含了BUILD文件bash

hello.c:網絡

#include <stdio.h>

int main(void){
    printf("hello from C!\n");
    return 0;
}

BUILD:框架

cc_binary(
    name = "hello",
    srcs = ["hello.c"],
)

執行構建dom

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

相關文章
相關標籤/搜索