Casos de uso comuns de build C++

Nesta página, você encontra alguns dos casos de uso mais comuns para criar projetos em C++ com o Bazel. Se ainda não fez isso, comece a criar projetos em C++ com o Bazel concluindo o tutorial Introdução ao Bazel: criar um projeto em C++.

Para informações sobre arquivos de cabeçalho cc_library e hdrs, consulte cc_library.

Como incluir vários arquivos em um destino

É possível incluir vários arquivos em um único destino com glob. Exemplo:

cc_library(
    name = "build-all-the-files",
    srcs = glob(["*.cc"]),
    hdrs = glob(["*.h"]),
)

Com esse destino, o Bazel vai criar todos os arquivos .cc e .h encontrados no mesmo diretório do arquivo BUILD que contém esse destino (excluindo subdiretórios).

Como usar includes transitivos

Se um arquivo incluir um cabeçalho, qualquer regra com esse arquivo como origem (ou seja, com esse arquivo no atributo srcs, hdrs ou textual_hdrs) vai depender da regra de biblioteca do cabeçalho incluído. Por outro lado, apenas as dependências diretas precisam ser especificadas como dependências. Por exemplo, suponha que sandwich.h inclua bread.h e bread.h inclua flour.h. sandwich.h não inclui flour.h (quem quer farinha no sanduíche?), então o arquivo BUILD ficaria assim:

cc_library(
    name = "sandwich",
    srcs = ["sandwich.cc"],
    hdrs = ["sandwich.h"],
    deps = [":bread"],
)

cc_library(
    name = "bread",
    srcs = ["bread.cc"],
    hdrs = ["bread.h"],
    deps = [":flour"],
)

cc_library(
    name = "flour",
    srcs = ["flour.cc"],
    hdrs = ["flour.h"],
)

Aqui, a biblioteca sandwich depende da biblioteca bread, que depende da biblioteca flour.

Como adicionar caminhos de include

Às vezes, não é possível (ou não é desejável) incluir caminhos na raiz do espaço de trabalho. As bibliotecas atuais podem já ter um diretório de include que não corresponde ao caminho no espaço de trabalho. Por exemplo, suponha que você tenha a seguinte estrutura de diretórios:

└── my-project
    ├── legacy
       └── some_lib
           ├── BUILD
           ├── include
              └── some_lib.h
           └── some_lib.cc
    └── WORKSPACE

O Bazel espera que some_lib.h seja incluído como legacy/some_lib/include/some_lib.h, mas suponha que some_lib.cc inclua "some_lib.h". Para tornar esse caminho de include válido, legacy/some_lib/BUILD precisa especificar que o diretório some_lib/include é um diretório de include:

cc_library(
    name = "some_lib",
    srcs = ["some_lib.cc"],
    hdrs = ["include/some_lib.h"],
    copts = ["-Ilegacy/some_lib/include"],
)

Isso é especialmente útil para dependências externas, já que os arquivos de cabeçalho precisam ser incluídos com um prefixo /.

Como incluir bibliotecas externas

Suponha que você esteja usando o Google Test. É possível usar uma das funções de repositório no arquivo WORKSPACE para fazer o download do Google Test e disponibilizá-lo no repositório:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "gtest",
    url = "https://github.com/google/googletest/archive/release-1.10.0.zip",
    sha256 = "94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91",
    build_file = "@//:gtest.BUILD",
)

Em seguida, crie gtest.BUILD, um arquivo BUILD usado para compilar o Google Test. O Google Test tem vários requisitos "especiais" que tornam a regra cc_library mais complicada:

  • googletest-release-1.10.0/src/gtest-all.cc #includes todos os outros arquivos em googletest-release-1.10.0/src/: exclua-o da compilação para evitar erros de vinculação de símbolos duplicados.

  • Ele usa arquivos de cabeçalho relativos ao googletest-release-1.10.0/include/ diretório ("gtest/gtest.h"), então é necessário adicionar esse diretório aos caminhos de include.

  • Ele precisa ser vinculado ao pthread, então adicione-o como um linkopt.

Portanto, a regra final fica assim:

cc_library(
    name = "main",
    srcs = glob(
        ["googletest-release-1.10.0/src/*.cc"],
        exclude = ["googletest-release-1.10.0/src/gtest-all.cc"]
    ),
    hdrs = glob([
        "googletest-release-1.10.0/include/**/*.h",
        "googletest-release-1.10.0/src/*.h"
    ]),
    copts = [
        "-Iexternal/gtest/googletest-release-1.10.0/include",
        "-Iexternal/gtest/googletest-release-1.10.0"
    ],
    linkopts = ["-pthread"],
    visibility = ["//visibility:public"],
)

Isso é um pouco confuso: tudo é prefixado com googletest-release-1.10.0 como um subproduto da estrutura do arquivo. É possível fazer com que http_archive remova esse prefixo adicionando o atributo strip_prefix:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "gtest",
    url = "https://github.com/google/googletest/archive/release-1.10.0.zip",
    sha256 = "94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91",
    build_file = "@//:gtest.BUILD",
    strip_prefix = "googletest-release-1.10.0",
)

Então gtest.BUILD ficaria assim:

cc_library(
    name = "main",
    srcs = glob(
        ["src/*.cc"],
        exclude = ["src/gtest-all.cc"]
    ),
    hdrs = glob([
        "include/**/*.h",
        "src/*.h"
    ]),
    copts = ["-Iexternal/gtest/include"],
    linkopts = ["-pthread"],
    visibility = ["//visibility:public"],
)

Agora, as regras cc_ podem depender de @gtest//:main.

Como escrever e executar testes em C++

Por exemplo, você pode criar um teste ./test/hello-test.cc, como:

#include "gtest/gtest.h"
#include "main/hello-greet.h"

TEST(HelloTest, GetGreet) {
  EXPECT_EQ(get_greet("Bazel"), "Hello Bazel");
}

Em seguida, crie o arquivo ./test/BUILD para seus testes:

cc_test(
    name = "hello-test",
    srcs = ["hello-test.cc"],
    copts = ["-Iexternal/gtest/include"],
    deps = [
        "@gtest//:main",
        "//main:hello-greet",
    ],
)

Para tornar hello-greet visível para hello-test, adicione "//test:__pkg__", ao atributo visibility em ./main/BUILD.

Agora é possível usar bazel test para executar o teste.

bazel test test:hello-test

Isso produz a seguinte saída:

INFO: Found 1 test target...
Target //test:hello-test up-to-date:
  bazel-bin/test/hello-test
INFO: Elapsed time: 4.497s, Critical Path: 2.53s
//test:hello-test PASSED in 0.3s

Executed 1 out of 1 tests: 1 test passes.

Como adicionar dependências em bibliotecas pré-compiladas

Se você quiser usar uma biblioteca da qual só tem uma versão compilada (por exemplo, cabeçalhos e um arquivo .so), faça o wrapper dela em uma regra cc_library:

cc_library(
    name = "mylib",
    srcs = ["mylib.so"],
    hdrs = ["mylib.h"],
)

Dessa forma, outros destinos em C++ no seu espaço de trabalho podem depender dessa regra.