Casos de uso comuns de build C++

Informar um problema Ver código-fonte Nightly · 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

Aqui você vai encontrar alguns dos casos de uso mais comuns para criar projetos C++ com o Bazel. Comece a criar projetos C++ com o Bazel concluindo o tutorial Introdução ao Bazel: criar um projeto C++ para começar.

Para informações sobre os 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 cria todos os arquivos .cc e .h que encontra no mesmo diretório que o arquivo BUILD que contém o destino (excluindo subdiretórios).

Usar inclusões transitivas

Se um arquivo incluir um cabeçalho, qualquer regra com esse arquivo como origem (ou seja, ter 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 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 inclusão

À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 inclusão que não corresponde 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, legacy/some_lib/BUILD precisará especificar que o diretório some_lib/include é 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 principais delas precisam ser incluídos com um prefixo /.

Como incluir bibliotecas externas

Vamos supor que você esteja usando o Google Test. Você pode usar uma das funções do 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:

  • O googletest-release-1.10.0/src/gtest-all.cc executa #include para todos os outros arquivos em googletest-release-1.10.0/src/: o exclui da compilação para evitar erros de link em símbolos duplicados.

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

  • Ele precisa ser vinculado a pthread. Adicione isso como um linkopt.

Portanto, a regra final ficará 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 subproduto da estrutura do arquivo. Você pode 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",
    ],
)

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

Agora você pode 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 a bibliotecas pré-compiladas

Se você quiser usar uma biblioteca em que você tem apenas uma versão compilada (por exemplo, cabeçalhos e um arquivo .so), envolva-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.