Casos de uso de build em C++ comuns

Informar um problema Ver código-fonte

Aqui você encontrará alguns dos casos de uso mais comuns para criar projetos em C++ com o Bazel. Para começar a criar projetos em C++ com o Bazel, conclua o tutorial Introdução ao Bazel: crie um projeto em C++.

Para ver 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 o glob. Exemplo:

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

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

Uso de inclusões transitivas

Se um arquivo inclui um cabeçalho, qualquer regra com esse arquivo como origem (ou seja, com esse arquivo no atributo srcs, hdrs ou textual_hdrs) precisará depender da regra da biblioteca do cabeçalho incluído. Por outro lado, apenas 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 fazer farinha no sanduíche?), 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.

Adicionar caminhos de inclusão

Às vezes, não é possível (ou não quer) incluir caminhos raiz na raiz do espaço de trabalho. As bibliotecas existentes talvez já tenham um diretório de inclusão que não corresponda ao caminho no seu 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 inclusão válido, o legacy/some_lib/BUILD precisará especificar que o diretório some_lib/include é um diretório de inclusão:

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, porque 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 seu 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 complicam a regra cc_library:

  • 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 link de símbolos duplicados.

  • Ele usa arquivos de cabeçalho relativos ao diretório googletest-release-1.10.0/include/ ("gtest/gtest.h"). Portanto, você precisa adicionar esse diretório aos caminhos de inclusão.

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

Portanto, a regra final é semelhante a esta:

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, 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 programar e executar testes de C++

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

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

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

Em seguida, crie um arquivo ./test/BUILD para os 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, você precisa adicionar "//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.

Adicionar dependências em bibliotecas pré-compiladas

Para usar uma biblioteca da qual você tem apenas uma versão compilada (por exemplo, cabeçalhos e um arquivo .so), una-a em uma regra cc_library:

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

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