Trabalhar com dependências externas

Reportar um problema Ver a fonte Nightly · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

O Bazel pode depender de destinos de outros projetos. As dependências desses outros projetos são chamadas de dependências externas.

O arquivo WORKSPACE (ou WORKSPACE.bazel) no diretório do espaço de trabalho informa ao Bazel como acessar as fontes de outros projetos. Esses outros projetos podem conter um ou mais arquivos BUILD com destinos próprios. Os arquivos BUILD no projeto principal podem depender desses destinos externos usando o nome deles no arquivo WORKSPACE.

Por exemplo, suponha que haja dois projetos em um sistema:

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

Se project1 quisesse depender de um destino, :foo, definido em /home/user/project2/BUILD, ele poderia especificar que um repositório chamado project2 poderia ser encontrado em /home/user/project2. Então, os destinos em /home/user/project1/BUILD podem depender de @project2//:foo.

O arquivo WORKSPACE permite que os usuários dependam de destinos de outras partes do sistema de arquivos ou baixados da Internet. Ele usa a mesma sintaxe dos arquivos BUILD, mas permite um conjunto diferente de regras chamado regras de repositório (às vezes também conhecido como regras de espaço de trabalho). O Bazel vem com algumas regras de repositório integradas e um conjunto de regras de repositório Starlark incorporadas. Os usuários também podem escrever regras de repositório personalizadas para ter um comportamento mais complexo.

Tipos de dependências externas compatíveis

Alguns tipos básicos de dependências externas podem ser usados:

Dependendo de outros projetos do Bazel

Se você quiser usar destinos de um segundo projeto do Bazel, use local_repository, git_repository ou http_archive para criar um link simbólico do sistema de arquivos local, referenciar um repositório git ou fazer o download dele (respectivamente).

Por exemplo, suponha que você esteja trabalhando em um projeto, my-project/, e queira depender de destinos do projeto do seu colega de trabalho, coworkers-project/. Os dois projetos usam o Bazel. Por isso, é possível adicionar o projeto do colega como uma dependência externa e usar qualquer destino que ele tenha definido nos seus próprios arquivos BUILD. Adicione o seguinte a my_project/WORKSPACE:

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

Se o colega tiver uma meta //foo:bar, seu projeto poderá se referir a ela como @coworkers_project//foo:bar. Os nomes de projetos externos precisam ser nomes de espaços de trabalho válidos.

Dependência de projetos que não são do Bazel

As regras com o prefixo new_, como new_local_repository, permitem criar destinos de projetos que não usam o Bazel.

Por exemplo, suponha que você esteja trabalhando em um projeto, my-project/, e queira depender do projeto do seu colega de trabalho, coworkers-project/. O projeto do seu colega de trabalho usa make para criar, mas você quer depender de um dos arquivos .so que ele gera. Para fazer isso, adicione o seguinte a my_project/WORKSPACE:

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

build_file especifica um arquivo BUILD para sobrepor ao projeto atual, por exemplo:

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

Em seguida, você pode depender do @coworkers_project//:some-lib nos arquivos BUILD do projeto.

Dependendo de pacotes externos

Artefatos e repositórios do Maven

Use o conjunto de regras rules_jvm_external para fazer o download de artefatos de repositórios Maven e disponibilizá-los como dependências Java.

Como buscar dependências

Por padrão, as dependências externas são buscadas conforme necessário durante bazel build. Se você quiser pré-buscar as dependências necessárias para um conjunto específico de destinos, use bazel fetch. Para buscar todas as dependências externas sem condições, use bazel sync. Como os repositórios buscados são armazenados na base de saída, a busca acontece por espaço de trabalho.

Dependências de shadowing

Sempre que possível, recomendamos ter uma única política de versão no seu projeto. Isso é necessário para dependências que você compila e que acabam no seu binário final. Mas, para casos em que isso não é verdade, é possível ocultar dependências. Pense no seguinte cenário:

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 = "..."
)

As dependências A e B dependem de testrunner, mas de versões diferentes.testrunner Não há motivo para que esses executores de teste não coexistam pacificamente em myproject, mas eles vão entrar em conflito porque têm o mesmo nome. Para declarar as duas dependências, atualize 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"}
)

Esse mecanismo também pode ser usado para juntar diamantes. Por exemplo, se A e B tiverem a mesma dependência, mas a chamarem por nomes diferentes, essas dependências poderão ser unidas em myproject/WORKSPACE.

Substituir repositórios na linha de comando

Para substituir um repositório declarado por um local na linha de comando, use a flag --override_repository. O uso dessa flag muda o conteúdo de repositórios externos sem alterar o código-fonte.

Por exemplo, para substituir @foo pelo diretório local /path/to/local/foo, transmita a flag --override_repository=foo=/path/to/local/foo.

Alguns dos casos de uso incluem:

  • Depuração de problemas. Por exemplo, é possível substituir um repositório http_archive por um diretório local em que você pode fazer mudanças com mais facilidade.
  • Disponibilização de pacotes de terceiros. Se você estiver em um ambiente em que não é possível fazer chamadas de rede, substitua as regras de repositório baseadas em rede para apontar para diretórios locais em vez disso.

Como usar proxies

O Bazel vai coletar endereços de proxy das variáveis de ambiente HTTPS_PROXY e HTTP_PROXY e usá-los para fazer o download de arquivos HTTP/HTTPS (se especificado).

Suporte a IPv6

Em máquinas somente IPv6, o Bazel poderá fazer o download de dependências sem alterações. No entanto, em máquinas de pilha dupla IPv4/IPv6, o Bazel segue a mesma convenção do Java: se o IPv4 estiver ativado, ele será preferido. Em algumas situações, por exemplo, quando a rede IPv4 não consegue resolver/acessar endereços externos, isso pode causar exceções Network unreachable e falhas de build. Nesses casos, é possível substituir o comportamento do Bazel para preferir o IPv6 usando a propriedade do sistema java.net.preferIPv6Addresses=true. Especificamente:

  • Use a opção de inicialização --host_jvm_args=-Djava.net.preferIPv6Addresses=true, por exemplo, adicionando a seguinte linha ao arquivo .bazelrc:

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

  • Se você estiver executando destinos de build Java que também precisam se conectar à Internet (às vezes, os testes de integração precisam disso), use a flag de ferramenta --jvmopt=-Djava.net.preferIPv6Addresses=true. Por exemplo, inclua a seguinte linha no arquivo .bazelrc:

    build --jvmopt=-Djava.net.preferIPv6Addresses

  • Se você estiver usando rules_jvm_external, por exemplo, para resolução de versão de dependência, adicione também -Djava.net.preferIPv6Addresses=true à variável de ambiente COURSIER_OPTS para fornecer opções de JVM para o Coursier.

Dependências transitivas

O Bazel só lê as dependências listadas no arquivo WORKSPACE. Se o projeto (A) depender de outro projeto (B) que lista uma dependência em um terceiro projeto (C) no arquivo WORKSPACE, será necessário adicionar B e C ao arquivo WORKSPACE do projeto. Esse requisito pode aumentar o tamanho do arquivo WORKSPACE, mas limita as chances de uma biblioteca incluir C na versão 1.0 e outra incluir C na versão 2.0.

Armazenamento em cache de dependências externas

Por padrão, o Bazel só baixa novamente as dependências externas se a definição delas mudar. O bazel também considera as mudanças nos arquivos referenciados na definição, como patches ou arquivos BUILD.

Para forçar um novo download, use bazel sync.

Layout

Todas as dependências externas são baixadas para um diretório no subdiretório external na base de saída. No caso de um repositório local, um link simbólico é criado lá em vez de um novo diretório. Para conferir o diretório external, execute:

ls $(bazel info output_base)/external

Executar bazel clean não exclui o diretório externo. Para remover todos os artefatos externos, use bazel clean --expunge.

Builds off-line

Às vezes, é desejável ou necessário executar um build off-line. Para casos de uso simples, como viajar de avião, prefetching dos repositórios necessários com bazel fetch ou bazel sync pode ser suficiente. Além disso, ao usar a opção --nofetch, a busca de outros repositórios pode ser desativada durante o build.

Para builds off-line verdadeiros, em que o fornecimento dos arquivos necessários é feito por uma entidade diferente do Bazel, o Bazel oferece suporte à opção --distdir. Sempre que uma regra de repositório pede ao bazel para buscar um arquivo via ctx.download ou ctx.download_and_extract e fornece uma soma de hash do arquivo necessário, o bazel primeiro procura nos diretórios especificados por essa opção um arquivo que corresponda ao nome base do primeiro URL fornecido e usa essa cópia local se o hash corresponder.

O próprio Bazel usa essa técnica para inicializar off-line do artefato de distribuição. Para isso, ele coleta todas as dependências externas necessárias em um distdir_tar interno.

No entanto, o bazel permite a execução de comandos arbitrários em regras de repositório, sem saber se eles fazem chamadas para a rede. Portanto, o Bazel não tem opção para forçar builds totalmente off-line. Portanto, testar se um build funciona corretamente off-line exige o bloqueio externo da rede, como o bazel faz no teste de bootstrap.

Práticas recomendadas

Regras do repositório

Em geral, uma regra de repositório é responsável por:

  • Detectar configurações do sistema e gravar em arquivos.
  • Encontrar recursos em outros lugares do sistema.
  • Fazendo o download de recursos de URLs.
  • Gerar ou criar links simbólicos de arquivos BUILD no diretório do repositório externo.

Evite usar repository_ctx.execute sempre que possível. Por exemplo, ao usar uma biblioteca C++ que não é do Bazel e tem um build usando Make, é preferível usar repository_ctx.download() e escrever um arquivo BUILD que o crie, em vez de executar ctx.execute(["make"]).

Prefira http_archive a git_repository e new_git_repository. Os motivos são:

  • As regras do repositório Git dependem do sistema git(1), enquanto o downloader HTTP é integrado ao Bazel e não tem dependências do sistema.
  • http_archive é compatível com uma lista de urls como espelhos, e git_repository é compatível apenas com um único remote.
  • http_archive funciona com o cache do repositório, mas não com git_repository. Consulte #5116 para mais informações.

Não use bind(). Consulte "Considere remover bind" para uma longa discussão sobre os problemas e alternativas.