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 origens de outros projetos. Esses outros projetos podem
conter um ou mais arquivos BUILD
com os próprios destinos. 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
. Em seguida, as segmentações 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 transferidos por download da Internet. Ele usa a mesma sintaxe dos arquivos BUILD
, mas permite um conjunto diferente de regras chamado regras do repositório (às vezes
também conhecidas como regras do espaço de trabalho). O Bazel vem com algumas
regras de repositório integradas e um conjunto de
regras de repositório Starlark integradas.
Os usuários também podem escrever regras personalizadas do repositório
para ter um comportamento mais complexo.
Tipos de dependências externas com suporte
Alguns tipos básicos de dependências externas podem ser usados:
- Dependências em outros projetos do Bazel
- Dependências em projetos que não são do Bazel
- Dependências de pacotes externos
Depender 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 vinculá-lo simbolicamente a partir 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 dos destinos do projeto de seu colega de trabalho, coworkers-project/
. Os dois
projetos usam o Bazel, então você pode adicionar o projeto do colega como uma dependência
externa e usar as metas definidas pelo colega 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 um //foo:bar
de destino, seu projeto poderá se referir a ele como
@coworkers_project//foo:bar
. Nomes de projetos externos precisam ser nomes de espaços de trabalho válidos.
Dependendo de projetos que não sejam do Bazel
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, coworkers-project/
. O projeto do seu colega
usa make
para criar, mas você quer depender de um dos arquivos .so
gerados. Para fazer isso, adicione o seguinte ao 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 o projeto atual, por
exemplo:
cc_library(
name = "some-lib",
srcs = glob(["**"]),
visibility = ["//visibility:public"],
)
Então, você pode depender de @coworkers_project//:some-lib
dos arquivos
BUILD
do seu projeto.
Dependendo de pacotes externos
Artefatos e repositórios do Maven
Use a regra rules_jvm_external
para fazer o download de artefatos dos repositórios do 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 fazer o pré-carregamento das dependências necessárias para um conjunto específico de destinos, use
bazel fetch
.
Para buscar incondicionalmente todas as dependências externas, 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 sombreamento
Sempre que possível, é recomendável ter uma política de versão única no projeto. Isso é necessário para dependências que você compila e que acabam no binário final. No entanto, para casos em que isso não for 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/ESPAÇO DE TRABALHO
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 dependem de
versões diferentes de testrunner
. Não há motivo para que esses executores de teste
não coexistam pacificamente no myproject
, mas eles vão entrar em conflito um
com o outro 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 unir diamantes. Por exemplo, se A
e B
tiverem a mesma dependência, mas com nomes diferentes, essas dependências poderão
ser agrupadas em myproject/WORKSPACE.
Como substituir repositórios na linha de comando
Para substituir um repositório declarado por um repositório local na linha de comando,
use a
flag
--override_repository
. O uso dessa flag muda o conteúdo de repositórios externos sem
mudar 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:
- Depurar problemas. Por exemplo, é possível substituir um repositório
http_archive
por um diretório local, onde é possível fazer alterações 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, modifique as regras de repositório baseadas em rede para apontar para diretórios locais.
Como usar proxies
O Bazel vai extrair 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
mudanças. Em máquinas IPv4/IPv6 de pilha dupla, no entanto, 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 (testes de integração às vezes precisam disso), use também a sinalização de ferramenta
--jvmopt=-Djava.net.preferIPv6Addresses=true
. Por exemplo, usando esta linha no arquivo.bazelrc
:build --jvmopt=-Djava.net.preferIPv6Addresses
Se você estiver usando rules_jvm_external, por exemplo, para a resolução de versão de dependência, adicione também
-Djava.net.preferIPv6Addresses=true
à variável de ambienteCOURSIER_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 de 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 que uma biblioteca inclua C
na versão 1.0 e outra inclua C
na 2.0.
Armazenamento em cache de dependências externas
Por padrão, o Bazel só faz o download de dependências externas novamente se a
definição delas mudar. As mudanças nos arquivos referenciados na definição (como patches
ou arquivos BUILD
) também são consideradas pelo Bazel.
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 será criado lá em vez de criar um novo diretório.
Para encontrar 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
.
Compilações off-line
Às vezes, é desejável ou necessário executar um build de forma off-line. Para
casos de uso simples, como viajar em um avião,
o pré-carregamento dos repositórios
necessários com bazel fetch
ou bazel sync
pode ser suficiente. Além disso, usando a opção --nofetch
, a busca de outros repositórios pode ser desativada
durante o build.
Para builds totalmente off-line, 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 solicitar que o Bazel busque um arquivo por
ctx.download
ou
ctx.download_and_extract
e forneça uma soma de hash do arquivo
necessário, o Bazel vai procurar primeiro nos diretórios especificados por essa opção um
arquivo que corresponda ao nome básico do primeiro URL fornecido e usar essa cópia local
se o hash corresponder.
O próprio Bazel usa essa técnica para fazer o bootstrap off-line do artefato
de distribuição (link em inglês).
Ele faz isso coletando todas as dependências externas necessárias em uma distdir_tar
interna.
No entanto, o Bazel permite a execução de comandos arbitrários nas regras do repositório, sem saber se eles chamam a rede. Portanto, o Bazel não tem opção para forçar que os builds sejam totalmente off-line. Portanto, testar se um build funciona corretamente off-line requer o bloqueio externo da rede, como o Bazel faz no teste de inicialização.
Práticas recomendadas
Regras do repositório
Uma regra de repositório geralmente é responsável por:
- Detectar as configurações do sistema e gravá-las em arquivos.
- Encontrar recursos em outro lugar do sistema.
- Fazendo o download de recursos de URLs.
- gerar ou fazer a vinculação simbólica 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 seja do Bazel e que tenha um build usando o Make, é preferível usar repository_ctx.download()
e, em seguida,
gravar 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 do Git dependem do
git(1)
do sistema, enquanto o downloader HTTP é integrado ao Bazel e não tem dependências do sistema. http_archive
oferece suporte a uma lista deurls
como espelhos, egit_repository
oferece suporte apenas a uma únicaremote
.- O
http_archive
funciona com o cache do repositório, mas não com ogit_repository
. Consulte o No 5116 (link em inglês) para mais informações.
Não use bind()
. Consulte Considere remover o
bind para uma discussão longa
sobre os problemas e as alternativas.