Trabaja con dependencias externas

Informar un problema Ver código fuente Nocturno · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

Bazel puede depender de destinos de otros proyectos. Las dependencias de estos otros proyectos se denominan dependencias externas.

El archivo WORKSPACE (o WORKSPACE.bazel) en el directorio del espacio de trabajo le indica a Bazel cómo obtener las fuentes de otros proyectos. Estos otros proyectos pueden contener uno o más archivos BUILD con sus propios destinos. Los archivos BUILD dentro del proyecto principal pueden depender de estos destinos externos usando su nombre del archivo WORKSPACE.

Por ejemplo, supongamos que hay dos proyectos en un sistema:

/
  home/
    user/
      project1/
        WORKSPACE
        BUILD
        srcs/
          ...
      project2/
        WORKSPACE
        BUILD
        my-libs/

Si project1 quisiera depender de un destino, :foo, definido en /home/user/project2/BUILD, podría especificar que se puede encontrar un repositorio llamado project2 en /home/user/project2. Luego, los destinos en /home/user/project1/BUILD podrían depender de @project2//:foo.

El archivo WORKSPACE permite que los usuarios dependan de destinos de otras partes del sistema de archivos o que se descarguen de Internet. Usa la misma sintaxis que los archivos BUILD, pero permite un conjunto diferente de reglas llamadas reglas del repositorio (a veces, también conocidas como reglas del espacio de trabajo). Bazel incluye algunas reglas de repositorio integradas y un conjunto de reglas de repositorio de Starlark integradas. Los usuarios también pueden escribir reglas de repositorio personalizadas para obtener un comportamiento más complejo.

Tipos de dependencias externas admitidos

Se pueden usar algunos tipos básicos de dependencias externas:

Dependencia de otros proyectos de Bazel

Si deseas usar destinos de un segundo proyecto de Bazel, puedes usar local_repository, git_repository o http_archive para crear un vínculo simbólico desde el sistema de archivos local, hacer referencia a un repositorio de Git o descargarlo (respectivamente).

Por ejemplo, supongamos que estás trabajando en un proyecto, my-project/, y quieres depender de los destinos del proyecto de tu compañero de trabajo, coworkers-project/. Ambos proyectos usan Bazel, por lo que puedes agregar el proyecto de tu compañero de trabajo como una dependencia externa y, luego, usar cualquier destino que tu compañero haya definido desde tus propios archivos BUILD. Agregarías lo siguiente a my_project/WORKSPACE:

local_repository(
    name = "coworkers_project",
    path = "/path/to/coworkers-project",
)

Si tu compañero de trabajo tiene un objetivo //foo:bar, tu proyecto puede hacer referencia a él como //foo:bar.@coworkers_project//foo:bar Los nombres de proyectos externos deben ser nombres de espacios de trabajo válidos.

Dependencias de proyectos que no son de Bazel

Las reglas con el prefijo new_, como new_local_repository, te permiten crear destinos a partir de proyectos que no usan Bazel.

Por ejemplo, supongamos que estás trabajando en un proyecto, my-project/, y quieres depender del proyecto de tu compañero de trabajo, coworkers-project/. El proyecto de tu compañero de trabajo usa make para compilar, pero te gustaría depender de uno de los archivos .so que genera. Para ello, agrega lo siguiente a my_project/WORKSPACE:

new_local_repository(
    name = "coworkers_project",
    path = "/path/to/coworkers-project",
    build_file = "coworker.BUILD",
)

build_file especifica un archivo BUILD para superponerlo en el proyecto existente, por ejemplo:

cc_library(
    name = "some-lib",
    srcs = glob(["**"]),
    visibility = ["//visibility:public"],
)

Luego, puedes depender de @coworkers_project//:some-lib desde los archivos BUILD de tu proyecto.

Dependencia de paquetes externos

Artefactos y repositorios de Maven

Usa el conjunto de reglas rules_jvm_external para descargar artefactos de repositorios de Maven y hacerlos disponibles como dependencias de Java.

Recupera dependencias

De forma predeterminada, las dependencias externas se recuperan según sea necesario durante bazel build. Si deseas realizar una recuperación previa de las dependencias necesarias para un conjunto específico de destinos, usa bazel fetch. Para recuperar todas las dependencias externas de forma incondicional, usa bazel sync. Como los repositorios recuperados se almacenan en la base de salida, la recuperación se realiza por lugar de trabajo.

Dependencias de sombreado

Siempre que sea posible, se recomienda tener una sola política de versiones en tu proyecto. Esto es obligatorio para las dependencias con las que compilas y que terminan en tu archivo binario final. Sin embargo, en los casos en los que esto no es cierto, es posible ocultar las dependencias. Considera la siguiente situación:

myproject/WORKSPACE

workspace(name = "myproject")

local_repository(
    name = "A",
    path = "../A",
)
local_repository(
    name = "B",
    path = "../B",
)

A/WORKSPACE

workspace(name = "A")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner",
    urls = ["https://github.com/testrunner/v1.zip"],
    sha256 = "...",
)

B/WORKSPACE

workspace(name = "B")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner",
    urls = ["https://github.com/testrunner/v2.zip"],
    sha256 = "..."
)

Ambas dependencias A y B dependen de testrunner, pero de diferentes versiones de testrunner. No hay motivos para que estos ejecutores de pruebas no coexistan de forma pacífica dentro de myproject, pero chocarán entre sí, ya que tienen el mismo nombre. Para declarar ambas dependencias, actualiza myproject/WORKSPACE:

workspace(name = "myproject")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner-v1",
    urls = ["https://github.com/testrunner/v1.zip"],
    sha256 = "..."
)
http_archive(
    name = "testrunner-v2",
    urls = ["https://github.com/testrunner/v2.zip"],
    sha256 = "..."
)
local_repository(
    name = "A",
    path = "../A",
    repo_mapping = {"@testrunner" : "@testrunner-v1"}
)
local_repository(
    name = "B",
    path = "../B",
    repo_mapping = {"@testrunner" : "@testrunner-v2"}
)

Este mecanismo también se puede usar para unir diamantes. Por ejemplo, si A y B tuvieran la misma dependencia, pero la llamaran con nombres diferentes, esas dependencias se podrían unir en myproject/WORKSPACE.

Cómo anular repositorios desde la línea de comandos

Para anular un repositorio declarado con un repositorio local desde la línea de comandos, usa la marca --override_repository. Usar esta marca cambia el contenido de los repositorios externos sin cambiar tu código fuente.

Por ejemplo, para anular @foo en el directorio local /path/to/local/foo, pasa la marca --override_repository=foo=/path/to/local/foo.

A continuación, se presentan algunos de los casos prácticos:

  • Depurar problemas Por ejemplo, puedes anular un repositorio http_archive y usar un directorio local en el que puedas realizar cambios con mayor facilidad.
  • Vendoring Si te encuentras en un entorno en el que no puedes realizar llamadas de red, anula las reglas del repositorio basadas en la red para que apunten a directorios locales.

Uso de proxies

Bazel tomará las direcciones de proxy de las variables de entorno HTTPS_PROXY y HTTP_PROXY y las usará para descargar archivos HTTP/HTTPS (si se especifican).

Compatibilidad con IPv6

En las máquinas solo IPv6, Bazel podrá descargar dependencias sin cambios. Sin embargo, en las máquinas de pila doble IPv4/IPv6, Bazel sigue la misma convención que Java: si IPv4 está habilitado, se prefiere IPv4. En algunas situaciones, por ejemplo, cuando la red IPv4 no puede resolver o alcanzar direcciones externas, esto puede causar excepciones de Network unreachable y fallas en la compilación. En estos casos, puedes anular el comportamiento de Bazel para que prefiera IPv6 con la propiedad del sistema java.net.preferIPv6Addresses=true. En particular, haz lo siguiente:

  • Usa la --host_jvm_args=-Djava.net.preferIPv6Addresses=true opción de inicio, por ejemplo, agregando la siguiente línea en tu archivo .bazelrc:

    startup --host_jvm_args=-Djava.net.preferIPv6Addresses=true

  • Si ejecutas destinos de compilación de Java que también necesitan conectarse a Internet (a veces, las pruebas de integración lo requieren), usa también la --jvmopt=-Djava.net.preferIPv6Addresses=true marca de herramienta, por ejemplo, con la siguiente línea en tu archivo .bazelrc:

    build --jvmopt=-Djava.net.preferIPv6Addresses

  • Si usas rules_jvm_external, por ejemplo, para la resolución de versiones de dependencias, también agrega -Djava.net.preferIPv6Addresses=true a la variable de entorno COURSIER_OPTS para proporcionar opciones de JVM para Coursier.

Dependencias transitivas

Bazel solo lee las dependencias que se indican en tu archivo WORKSPACE. Si tu proyecto (A) depende de otro proyecto (B) que incluye una dependencia en un tercer proyecto (C) en su archivo WORKSPACE, deberás agregar B y C al archivo WORKSPACE de tu proyecto. Este requisito puede aumentar el tamaño del archivo WORKSPACE, pero limita las posibilidades de que una biblioteca incluya C en la versión 1.0 y otra incluya C en la versión 2.0.

Almacenamiento en caché de dependencias externas

De forma predeterminada, Bazel solo volverá a descargar las dependencias externas si cambia su definición. Bazel también tiene en cuenta los cambios en los archivos a los que se hace referencia en la definición (como parches o archivos BUILD).

Para forzar una nueva descarga, usa bazel sync.

Diseño

Todas las dependencias externas se descargan en un directorio dentro del subdirectorio external en la base de salida. En el caso de un repositorio local, se crea un symlink allí en lugar de crear un directorio nuevo. Para ver el directorio external, ejecuta lo siguiente:

ls $(bazel info output_base)/external

Ten en cuenta que ejecutar bazel clean no borrará el directorio externo. Para quitar todos los artefactos externos, usa bazel clean --expunge.

Compilaciones sin conexión

A veces, es conveniente o necesario ejecutar una compilación sin conexión. Para casos de uso simples, como viajar en avión, puede ser suficiente con prefetching los repositorios necesarios con bazel fetch o bazel sync. Además, con la opción --nofetch, se puede inhabilitar la recuperación de más repositorios durante la compilación.

Para las compilaciones sin conexión reales, en las que una entidad diferente de Bazel debe proporcionar los archivos necesarios, Bazel admite la opción --distdir. Cada vez que una regla de repositorio le pide a Bazel que recupere un archivo a través de ctx.download o ctx.download_and_extract y proporciona una suma de hash del archivo necesario, Bazel primero buscará en los directorios especificados por esa opción un archivo que coincida con el nombre base de la primera URL proporcionada y usará esa copia local si coincide el hash.

Bazel usa esta técnica para el bootstrapping sin conexión desde el artefacto de distribución. Para ello, recopila todas las dependencias externas necesarias en un distdir_tar interno.

Sin embargo, Bazel permite la ejecución de comandos arbitrarios en reglas de repositorio, sin saber si llaman a la red. Por lo tanto, Bazel no tiene la opción de aplicar que las compilaciones sean completamente sin conexión. Por lo tanto, probar si una compilación funciona correctamente sin conexión requiere el bloqueo externo de la red, como lo hace Bazel en su prueba de bootstrap.

Prácticas recomendadas

Reglas del repositorio

Por lo general, una regla de repositorio debe encargarse de lo siguiente:

  • Detectar la configuración del sistema y escribirla en archivos
  • Encontrar recursos en otras partes del sistema
  • Descarga recursos desde URLs.
  • Generar o crear vínculos simbólicos de archivos BUILD en el directorio del repositorio externo

Evita usar repository_ctx.execute cuando sea posible. Por ejemplo, cuando se usa una biblioteca de C++ que no es de Bazel y que tiene una compilación con Make, es preferible usar repository_ctx.download() y, luego, escribir un archivo BUILD que la compile, en lugar de ejecutar ctx.execute(["make"]).

Prefiere http_archive a git_repository y new_git_repository. Estos son los motivos:

  • Las reglas del repositorio de Git dependen del sistema git(1), mientras que el descargador de HTTP está integrado en Bazel y no tiene dependencias del sistema.
  • http_archive admite una lista de urls como duplicados, y git_repository solo admite un solo remote.
  • http_archive funciona con la caché del repositorio, pero no con git_repository. Consulta #5116 para obtener más información.

No uses bind(). Consulta "Considera quitar bind" para ver un debate extenso sobre sus problemas y alternativas.