Casos de uso comunes de compilación de C++

Informa un problema Ver código fuente

Aquí encontrarás algunos de los casos prácticos más comunes para compilar proyectos de C++ con Bazel. Si aún no lo has hecho, completa el instructivo Introducción a Bazel: Compila un proyecto de C++ para comenzar a compilar proyectos de C++ con Bazel.

Para obtener información sobre los archivos de encabezado cc_library y hdrs, consulta cc_library.

Incluye varios archivos en un destino

Puedes incluir varios archivos en un solo destino con glob. Por ejemplo:

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

Con este destino, Bazel compilará todos los archivos .cc y .h que encuentre en el mismo directorio que el archivo BUILD que contiene este destino (excepto los subdirectorios).

Usar inclusiones transitivas

Si un archivo incluye un encabezado, cualquier regla que tenga ese archivo como fuente (es decir, tener ese archivo en los atributos srcs, hdrs o textual_hdrs) debe depender de la regla de biblioteca del encabezado incluido. Por el contrario, solo las dependencias directas deben especificarse como dependencias. Por ejemplo, supongamos que sandwich.h incluye bread.h y bread.h incluye flour.h. sandwich.h no incluye flour.h (¿quién quiere la harina en su sándwich?), por lo que el archivo BUILD se vería así:

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"],
)

En este caso, la biblioteca sandwich depende de la biblioteca bread, que depende de la biblioteca flour.

Agrega rutas de inclusión

A veces, no puedes (o no deseas) incluir rutas en la raíz del espacio de trabajo. Es posible que las bibliotecas existentes ya tengan un directorio de inclusión que no coincida con su ruta en tu lugar de trabajo. Por ejemplo, supongamos que tienes la siguiente estructura de directorio:

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

Bazel espera que se incluya some_lib.h como legacy/some_lib/include/some_lib.h, pero supongamos que some_lib.cc incluye "some_lib.h". Para que esa ruta de inclusión sea válida, legacy/some_lib/BUILD deberá especificar que el directorio some_lib/include es un directorio de inclusión:

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

Esto es particularmente útil para dependencias externas, ya que, de lo contrario, los archivos de encabezado deben incluirse con un prefijo /.

Incluye bibliotecas externas

Supongamos que usas la prueba de Google. Puedes usar una de las funciones del repositorio del archivo WORKSPACE para descargar Google Test y hacer que esté disponible en el repositorio:

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",
)

Luego, crea gtest.BUILD, un archivo BUILD que se usa para compilar Google Test. Google Test tiene varios requisitos “especiales” que hacen que la regla cc_library sea más complicada:

  • googletest-release-1.10.0/src/gtest-all.cc #include todos los demás archivos de googletest-release-1.10.0/src/: excluye el archivo de la compilación para evitar errores de vínculo de símbolos duplicados.

  • Usa archivos de encabezado relacionados con el directorio googletest-release-1.10.0/include/ ("gtest/gtest.h"), por lo que debes agregar ese directorio a las rutas de inclusión.

  • Debe vincularse en pthread, así que agrégalo como linkopt.

Por lo tanto, la norma final se ve de la siguiente manera:

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"],
)

Esto es un poco conflictivo: todo tiene el prefijo googletest-release-1.10.0 como subproducto de la estructura del archivo. Puedes hacer que http_archive quite este prefijo si agregas el 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",
)

Luego, gtest.BUILD se vería así:

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"],
)

Ahora, las reglas de cc_ pueden depender de @gtest//:main.

Escribe y ejecuta pruebas de C++

Por ejemplo, puedes crear una prueba ./test/hello-test.cc, como la siguiente:

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

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

Luego, crea el archivo ./test/BUILD para las pruebas:

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

Para que hello-greet sea visible para hello-test, debes agregar "//test:__pkg__", al atributo visibility en ./main/BUILD.

Ahora puedes usar bazel test para ejecutar la prueba.

bazel test test:hello-test

Esto produce el siguiente resultado:

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.

Cómo agregar dependencias en bibliotecas previamente compiladas

Si deseas usar una biblioteca de la que solo tienes una versión compilada (por ejemplo, encabezados y un archivo .so), debes unirla en una regla cc_library:

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

De esta manera, otros destinos C++ de su lugar de trabajo pueden depender de esta regla.