Introdução
Você não conhece o Bazel? Você está no lugar certo. Siga este tutorial do First Build para uma introdução simplificada ao uso do Bazel. Este tutorial define os principais termos usados no contexto do Bazel e mostra os princípios básicos do fluxo de trabalho do Bazel. Começando com as ferramentas necessárias, você vai criar e executar três projetos com complexidade crescente e aprender como e por que eles se tornam mais complexos.
Embora o Bazel seja um sistema de build que oferece suporte a builds em vários idiomas, este tutorial usa um projeto em C++ como exemplo e fornece as diretrizes gerais e o fluxo que se aplicam à maioria das linguagens.
Tempo estimado para conclusão: 30 minutos.
Pré-requisitos
Comece instalando o Bazel, se ainda não tiver feito isso. Este tutorial usa o Git para controle de origem. Para ter os melhores resultados, instale o Git também.
Em seguida, recupere o projeto de exemplo do repositório do GitHub do Bazel executando o seguinte na ferramenta de linha de comando de sua escolha:
git clone https://github.com/bazelbuild/examples
O projeto de amostra deste tutorial está no diretório examples/cpp-tutorial
.
Confira a estrutura:
examples
└── cpp-tutorial
├──stage1
│ ├── main
│ │ ├── BUILD
│ │ └── hello-world.cc
│ └── MODULE.bazel
├──stage2
│ ├── main
│ │ ├── BUILD
│ │ ├── hello-world.cc
│ │ ├── hello-greet.cc
│ │ └── hello-greet.h
│ └── MODULE.bazel
└──stage3
├── main
│ ├── BUILD
│ ├── hello-world.cc
│ ├── hello-greet.cc
│ └── hello-greet.h
├── lib
│ ├── BUILD
│ ├── hello-time.cc
│ └── hello-time.h
└── MODULE.bazel
Há três conjuntos de arquivos, cada um representando uma etapa deste tutorial. Na primeira etapa, você vai criar um único destino em um único pacote. Na segunda etapa, você vai criar um binário e uma biblioteca de um único pacote. Na terceira e última etapa, você vai criar um projeto com vários pacotes e destinos.
Resumo: introdução
Ao instalar o Bazel (e o Git) e clonar o repositório deste tutorial, você criou a base para seu primeiro build com o Bazel. Continue para a próxima seção para definir alguns termos e configurar seu espaço de trabalho.
Primeiros passos
Antes de criar um projeto, é necessário configurar o espaço de trabalho dele. Um espaço de trabalho é um diretório que contém os arquivos de origem do projeto e as saídas de build do Bazel. Ele também contém estes arquivos importantes:
- O arquivo
MODULE.bazel
, que identifica o diretório e o conteúdo dele como um espaço de trabalho do Bazel e fica na raiz da estrutura de diretórios do projeto. É também onde você especifica suas dependências externas. - Um ou mais
BUILD
arquivos, que informam ao Bazel como criar diferentes partes do projeto. Um diretório no espaço de trabalho que contém um arquivoBUILD
é um pacote. (Mais sobre pacotes adiante neste tutorial.)
Em projetos futuros, para designar um diretório como um espaço de trabalho do Bazel, crie um
arquivo vazio chamado MODULE.bazel
nesse diretório. Para este tutorial, um arquivo MODULE.bazel
já está presente em cada etapa.
Entender o arquivo BUILD
Um arquivo BUILD
contém vários tipos diferentes de instruções para o Bazel. Cada arquivo
BUILD
exige pelo menos uma
regra como um conjunto de instruções,
que informa ao Bazel como criar as saídas desejadas, como binários executáveis
ou bibliotecas. Cada instância de uma regra de build no arquivo BUILD
é chamada de
destino e aponta para um conjunto específico de arquivos de origem e
dependências. Uma meta também pode apontar para outras metas.
Confira o arquivo BUILD
no diretório cpp-tutorial/stage1/main
:
cc_binary(
name = "hello-world",
srcs = ["hello-world.cc"],
)
No nosso exemplo, o destino hello-world
cria uma instância da regra cc_binary
integrada do Bazel. A regra
diz ao Bazel para criar um executável binário independente do
arquivo de origem hello-world.cc
> sem dependências.
Resumo: como começar
Agora você conhece alguns termos importantes e o que eles significam no contexto deste projeto e do Bazel em geral. Na próxima seção, você vai criar e testar a Etapa 1 do projeto.
Etapa 1: um único destino, um único pacote
É hora de criar a primeira parte do projeto. Para uma referência visual, a estrutura da seção "Etapa 1" do projeto é:
examples
└── cpp-tutorial
└──stage1
├── main
│ ├── BUILD
│ └── hello-world.cc
└── MODULE.bazel
Execute o seguinte para acessar o diretório cpp-tutorial/stage1
:
cd cpp-tutorial/stage1
Depois execute:
bazel build //main:hello-world
No rótulo de destino, a parte //main:
é o local do arquivo BUILD
em relação à raiz do espaço de trabalho, e hello-world
é o nome de destino no
arquivo BUILD
.
O Bazel produz algo parecido com isto:
INFO: Found 1 target...
Target //main:hello-world up-to-date:
bazel-bin/main/hello-world
INFO: Elapsed time: 2.267s, Critical Path: 0.25s
Você acabou de criar seu primeiro destino do Bazel. O Bazel coloca as saídas de build no diretório
bazel-bin
na raiz do espaço de trabalho.
Agora teste o binário recém-criado, que é:
bazel-bin/main/hello-world
Isso resulta em uma mensagem "Hello world
" impressa.
Este é o gráfico de dependência da Etapa 1:
Resumo: etapa 1
Agora que você concluiu seu primeiro build, já tem uma ideia básica de como um build é estruturado. Na próxima etapa, você vai adicionar complexidade incluindo outro destino.
Etapa 2: várias metas de build
Embora um único destino seja suficiente para projetos pequenos, talvez seja melhor dividir projetos maiores em vários destinos e pacotes. Isso permite builds incrementais rápidos, ou seja, o Bazel só recria o que mudou, e acelera os builds criando várias partes de um projeto de uma só vez. Esta etapa do tutorial adiciona uma meta, e a próxima adiciona um pacote.
Este é o diretório com que você vai trabalhar na Etapa 2:
├──stage2
│ ├── main
│ │ ├── BUILD
│ │ ├── hello-world.cc
│ │ ├── hello-greet.cc
│ │ └── hello-greet.h
│ └── MODULE.bazel
Confira o arquivo BUILD
no diretório cpp-tutorial/stage2/main
:
cc_library(
name = "hello-greet",
srcs = ["hello-greet.cc"],
hdrs = ["hello-greet.h"],
)
cc_binary(
name = "hello-world",
srcs = ["hello-world.cc"],
deps = [
":hello-greet",
],
)
Com esse arquivo BUILD
, o Bazel primeiro cria a biblioteca hello-greet
(usando a regra cc_library
integrada do Bazel) e, em seguida, o binário hello-world
. O atributo deps
no destino hello-world
informa
ao Bazel que a biblioteca hello-greet
é necessária para criar o binário
hello-world
.
Antes de criar essa nova versão do projeto, mude
os diretórios para o cpp-tutorial/stage2
executando:
cd ../stage2
Agora, crie o novo binário usando o seguinte comando conhecido:
bazel build //main:hello-world
Mais uma vez, o Bazel produz algo parecido com isto:
INFO: Found 1 target...
Target //main:hello-world up-to-date:
bazel-bin/main/hello-world
INFO: Elapsed time: 2.399s, Critical Path: 0.30s
Agora é possível testar o binário recém-criado, que retorna outro "Hello
world
":
bazel-bin/main/hello-world
Se você modificar hello-greet.cc
e reconstruir o projeto, o Bazel vai recompilar apenas esse arquivo.
Ao analisar o gráfico de dependência, você pode ver que hello-world
depende de uma entrada extra chamada hello-greet
:
Resumo: etapa 2
Agora você criou o projeto com duas metas. O destino hello-world
cria
um arquivo de origem e depende de outro destino (//main:hello-greet
), que
cria dois arquivos de origem adicionais. Na próxima seção, vá além e adicione outro pacote.
Fase 3: vários pacotes
A próxima etapa adiciona outra camada de complicação e cria um projeto com
vários pacotes. Confira a estrutura e o conteúdo do diretório
cpp-tutorial/stage3
:
└──stage3
├── main
│ ├── BUILD
│ ├── hello-world.cc
│ ├── hello-greet.cc
│ └── hello-greet.h
├── lib
│ ├── BUILD
│ ├── hello-time.cc
│ └── hello-time.h
└── MODULE.bazel
Agora há dois subdiretórios, e cada um contém um arquivo BUILD
. Portanto, para o Bazel, o espaço de trabalho agora contém dois pacotes: lib
e
main
.
Confira o arquivo lib/BUILD
:
cc_library(
name = "hello-time",
srcs = ["hello-time.cc"],
hdrs = ["hello-time.h"],
visibility = ["//main:__pkg__"],
)
E no arquivo main/BUILD
:
cc_library(
name = "hello-greet",
srcs = ["hello-greet.cc"],
hdrs = ["hello-greet.h"],
)
cc_binary(
name = "hello-world",
srcs = ["hello-world.cc"],
deps = [
":hello-greet",
"//lib:hello-time",
],
)
O destino hello-world
no pacote principal depende do destino hello-time
no pacote lib
(daí o rótulo de destino //lib:hello-time
). O Bazel sabe
isso pelo atributo deps
. Isso pode ser visto no gráfico de dependência:
Para que o build seja bem-sucedido, torne o destino //lib:hello-time
em lib/BUILD
explicitamente visível para destinos em main/BUILD
usando o atributo de visibilidade.
Isso acontece porque, por padrão, as metas só ficam visíveis para outras metas no mesmo arquivo
BUILD
. O Bazel usa a visibilidade de destino para evitar problemas, como bibliotecas
que contêm detalhes de implementação vazando para APIs públicas.
Agora crie a versão final do projeto. Mude para o diretório cpp-tutorial/stage3
executando:
cd ../stage3
Mais uma vez, execute o comando a seguir:
bazel build //main:hello-world
O Bazel produz algo parecido com isto:
INFO: Found 1 target...
Target //main:hello-world up-to-date:
bazel-bin/main/hello-world
INFO: Elapsed time: 0.167s, Critical Path: 0.00s
Agora teste o último binário deste tutorial para uma mensagem Hello world
final:
bazel-bin/main/hello-world
Resumo: etapa 3
Agora você criou o projeto como dois pacotes com três destinos e entendeu as dependências entre eles. Isso permite que você crie projetos futuros com o Bazel. Na próxima seção, confira como continuar sua jornada com o Bazel.
Próximas etapas
Você concluiu seu primeiro build básico com o Bazel, mas isso é apenas o começo. Confira mais recursos para continuar aprendendo com o Bazel:
- Para continuar focando em C++, leia sobre casos de uso comuns de build em C++.
- Para começar a criar outros aplicativos com o Bazel, consulte os tutoriais para Java, aplicativo Android ou aplicativo iOS.
- Para saber mais sobre como trabalhar com repositórios locais e remotos, leia sobre dependências externas.
- Para saber mais sobre as outras regras do Bazel, consulte este guia de referência.
Boa criação!