Este tutorial aborda os conceitos básicos da criação de aplicativos Java com
o Bazel Você vai configurar seu espaço de trabalho e criar um projeto Java simples que
ilustra os principais conceitos do Bazel, como destinos e arquivos BUILD
.
Tempo estimado de conclusão: 30 minutos.
O que você vai aprender
Neste tutorial, você vai aprender a:
- Criar um destino
- Visualizar as dependências do projeto
- Dividir o projeto em vários destinos e pacotes
- Controlar a visibilidade do destino nos pacotes
- Fazer referência a destinos por meio de rótulos
- Implantar um destino
Antes de começar
Instalar o Bazel
Para se preparar para o tutorial, primeiro instale o Bazel se ainda não o tem instalado.
Instalar o JDK
Instale o Java JDK (a versão preferencial é a 11, mas as versões entre 8 e 15 são compatíveis).
Defina a variável de ambiente JAVA_HOME para apontar para o JDK.
No Linux/macOS:
export JAVA_HOME="$(dirname $(dirname $(realpath $(which javac))))"
No Windows:
- Abra o Painel de Controle.
- Acesse "Sistema e segurança". > "Sistema" > "Configurações avançadas do sistema" > "Avançado" > "Variáveis de ambiente..." .
- Em "Variáveis do usuário", (aquela na parte superior), clique em "Novo...".
- No campo "Nome da variável", digite
JAVA_HOME
. - Clique em "Procurar diretório...".
- Navegue até o diretório do JDK (por exemplo,
C:\Program Files\Java\jdk1.8.0_152
). - Clique em "OK". em todas as janelas de caixas de diálogo.
Acessar o projeto de amostra
Recupere o projeto de amostra do repositório GitHub do Bazel:
git clone https://github.com/bazelbuild/examples
O projeto de exemplo para este tutorial está no examples/java-tutorial
e é estruturado da seguinte forma:
java-tutorial
├── BUILD
├── src
│ └── main
│ └── java
│ └── com
│ └── example
│ ├── cmdline
│ │ ├── BUILD
│ │ └── Runner.java
│ ├── Greeting.java
│ └── ProjectRunner.java
└── WORKSPACE
Criação com o Bazel
Configurar o espaço de trabalho
Antes de criar um projeto, é preciso configurar o espaço de trabalho dele. Um espaço de trabalho é Um diretório que contém os arquivos de origem do seu projeto e as saídas de compilação do Bazel. Ela também contém arquivos que o Bazel reconhece como especiais:
O arquivo
WORKSPACE
, que identifica o diretório e o conteúdo dele como um Espaço de trabalho do Bazel e reside na raiz da estrutura de diretórios do projeto.Um ou mais arquivos
BUILD
, que informam ao Bazel como criar diferentes partes do o projeto. (Um diretório no espaço de trabalho que contém um arquivoBUILD
é um pacote (link em inglês). Você vai aprender sobre os pacotes mais adiante neste tutorial.
Para designar um diretório como um espaço de trabalho do Bazel, crie um arquivo vazio chamado
WORKSPACE
nesse diretório.
Quando o Bazel cria o projeto, todas as entradas e dependências precisam estar no mesmo espaço de trabalho. Os arquivos em diferentes espaços de trabalho são independentes de um outro, a menos que vinculado, o que está além do escopo deste tutorial.
Entenda o arquivo BUILD
Um arquivo BUILD
contém vários tipos diferentes de instruções para o Bazel.
O tipo mais importante é a regra de build, que informa ao Bazel como criar a
as saídas desejadas, como bibliotecas ou binários executáveis. Cada instância
de uma regra de build no arquivo BUILD
é chamado de destino e aponta para uma
um conjunto específico de arquivos de origem e dependências. Um alvo também pode apontar para outros
de destino.
Dê uma olhada no arquivo java-tutorial/BUILD
:
java_binary(
name = "ProjectRunner",
srcs = glob(["src/main/java/com/example/*.java"]),
)
No nosso exemplo, o destino ProjectRunner
instancia a biblioteca
java_binary
regra. A regra diz ao Bazel para
crie um arquivo .jar
e um script de shell do wrapper (ambos nomeados de acordo com o destino).
Os atributos no destino declaram explicitamente as dependências e opções.
Embora o atributo name
seja obrigatório, muitos são opcionais. Por exemplo, na
ProjectRunner
destino da regra, name
é o nome do destino, srcs
especifica.
os arquivos de origem que o Bazel usa para criar o destino, e main_class
especifica
a classe que contém o método principal. Você deve ter notado que nosso exemplo
usa glob para transmitir um conjunto de arquivos de origem para o Bazel.
em vez de listá-los um por um.)
Criar o projeto
Para criar seu projeto de amostra, navegue até o diretório java-tutorial
e execute:
bazel build //:ProjectRunner
No rótulo de destino, a parte //
é o local do arquivo BUILD
.
em relação à raiz do espaço de trabalho (nesse caso, a própria raiz),
e ProjectRunner
é o nome do destino no arquivo BUILD
. (Você
saiba mais sobre os rótulos de destino no final deste tutorial.
O Bazel produz uma saída semelhante a esta:
INFO: Found 1 target...
Target //:ProjectRunner up-to-date:
bazel-bin/ProjectRunner.jar
bazel-bin/ProjectRunner
INFO: Elapsed time: 1.021s, Critical Path: 0.83s
Parabéns, você acabou de criar seu primeiro destino do Bazel. Build de lugares do Bazel
gera saídas no diretório bazel-bin
na raiz do espaço de trabalho. Procurar
o conteúdo do arquivo para ter uma ideia da estrutura de saída do Bazel.
Agora teste o binário recém-criado:
bazel-bin/ProjectRunner
Analisar o gráfico de dependências
O Bazel exige que as dependências de build sejam explicitamente declaradas nos arquivos BUILD. O Bazel usa essas instruções para criar o gráfico de dependências do projeto, que permite builds incrementais precisos.
Para visualizar as dependências do projeto de exemplo, gere um arquivo de texto representação do gráfico de dependência. Basta executar este comando no raiz do espaço de trabalho:
bazel query --notool_deps --noimplicit_deps "deps(//:ProjectRunner)" --output graph
O comando acima diz ao Bazel para procurar todas as dependências do destino
//:ProjectRunner
(excluindo dependências implícitas e de host) e formate o
como um gráfico.
Depois, cole o texto no GraphViz.
Como você pode notar, o projeto tem um único destino que cria dois arquivos de origem com sem outras dependências:
Depois de configurar seu espaço de trabalho, crie seu projeto e examine as dependências, você pode adicionar alguma complexidade.
Refinar sua versão do Bazel
Embora um único destino seja suficiente para projetos pequenos, é recomendável dividir projetos maiores em vários destinos e pacotes para permitir uma rápida (ou seja, recriar somente o que foi alterado) e acelerá-los ao criar várias partes de um projeto de uma só vez.
Especificar vários destinos de build
É possível dividir o build do projeto de amostra em dois destinos. Substituir o conteúdo do
o arquivo java-tutorial/BUILD
pelo seguinte:
java_binary(
name = "ProjectRunner",
srcs = ["src/main/java/com/example/ProjectRunner.java"],
main_class = "com.example.ProjectRunner",
deps = [":greeter"],
)
java_library(
name = "greeter",
srcs = ["src/main/java/com/example/Greeting.java"],
)
Com essa configuração, o Bazel cria primeiro a biblioteca greeter
e depois a
Binário ProjectRunner
. O atributo deps
em java_binary
informa ao Bazel que
A biblioteca greeter
é necessária para criar o binário ProjectRunner
.
Para criar essa nova versão do projeto, execute o seguinte comando:
bazel build //:ProjectRunner
O Bazel produz uma saída semelhante a esta:
INFO: Found 1 target...
Target //:ProjectRunner up-to-date:
bazel-bin/ProjectRunner.jar
bazel-bin/ProjectRunner
INFO: Elapsed time: 2.454s, Critical Path: 1.58s
Agora teste o binário recém-criado:
bazel-bin/ProjectRunner
Se você agora modificar ProjectRunner.java
e recriar o projeto, somente o Bazel
recompila esse arquivo.
No gráfico de dependências, é possível notar que ProjectRunner
depende do
as mesmas entradas de antes, mas a estrutura do build é diferente:
Você criou o projeto com dois destinos. O build de destino ProjectRunner
dois arquivos de origem e depende de um outro destino (:greeter
), que cria
um arquivo de origem extra.
Usar vários pacotes
Agora vamos dividir o projeto em vários pacotes. Se você analisar
src/main/java/com/example/cmdline
, observe que ele também contém
um arquivo BUILD
, além de alguns arquivos de origem. Portanto, para Bazel, o espaço de trabalho
contém dois pacotes, //src/main/java/com/example/cmdline
e //
(já que
há um arquivo BUILD
na raiz do espaço de trabalho).
Dê uma olhada no arquivo src/main/java/com/example/cmdline/BUILD
:
java_binary(
name = "runner",
srcs = ["Runner.java"],
main_class = "com.example.cmdline.Runner",
deps = ["//:greeter"],
)
O destino runner
depende do destino greeter
no pacote //
. Portanto,
o rótulo de destino //:greeter
) - o Bazel sabe disso pelo atributo deps
.
Confira o gráfico de dependências:
No entanto, para que o build tenha êxito, você precisa informar explicitamente o destino runner
na visibilidade de //src/main/java/com/example/cmdline/BUILD
para as metas em
//BUILD
usando o atributo visibility
. Isso ocorre porque, por padrão,
ficam visíveis apenas para outros destinos no mesmo arquivo BUILD
. (O Bazel usa
visibilidade para evitar problemas como bibliotecas que contêm detalhes de implementação
vazando para APIs públicas.
Para fazer isso, adicione o atributo visibility
ao destino greeter
em
java-tutorial/BUILD
, como mostrado abaixo:
java_library(
name = "greeter",
srcs = ["src/main/java/com/example/Greeting.java"],
visibility = ["//src/main/java/com/example/cmdline:__pkg__"],
)
Agora você pode criar o novo pacote executando o seguinte comando na raiz do espaço de trabalho:
bazel build //src/main/java/com/example/cmdline:runner
O Bazel produz uma saída semelhante a esta:
INFO: Found 1 target...
Target //src/main/java/com/example/cmdline:runner up-to-date:
bazel-bin/src/main/java/com/example/cmdline/runner.jar
bazel-bin/src/main/java/com/example/cmdline/runner
INFO: Elapsed time: 1.576s, Critical Path: 0.81s
Agora teste o binário recém-criado:
./bazel-bin/src/main/java/com/example/cmdline/runner
Você modificou o projeto para compilar como dois pacotes, cada um contendo um e entender as dependências entre eles.
Usar rótulos para fazer referência a destinos
Em arquivos BUILD
e na linha de comando, o Bazel usa rótulos de destino para referenciar
de destino, por exemplo, //:ProjectRunner
ou
//src/main/java/com/example/cmdline:runner
A sintaxe é a seguinte:
//path/to/package:target-name
Se o destino for uma regra, path/to/package
será o caminho para a
diretório contendo o arquivo BUILD
, e target-name
é o nome do arquivo
destino no arquivo BUILD
(o atributo name
). Se o destino for um arquivo
destino, path/to/package
é o caminho para a raiz do pacote e
target-name
é o nome do arquivo de destino, incluindo o caminho completo.
Ao referenciar destinos na raiz do repositório, o caminho do pacote fica vazio,
basta usar //:target-name
. Ao referenciar destinos dentro do mesmo BUILD
é possível pular o identificador raiz do espaço de trabalho //
e apenas usar
:target-name
Por exemplo, para destinos no arquivo java-tutorial/BUILD
, você não precisava
especificar um caminho de pacote, já que a raiz do espaço de trabalho é um pacote (//
); e
seus dois rótulos de destino eram //:ProjectRunner
e //:greeter
.
No entanto, para destinos no arquivo //src/main/java/com/example/cmdline/BUILD
,
teve que especificar o caminho completo do pacote de //src/main/java/com/example/cmdline
e o rótulo de destino era //src/main/java/com/example/cmdline:runner
.
Empacotar um destino Java para implantação
Agora, vamos empacotar um destino Java para implantação criando o binário com todos das dependências de ambiente de execução. Com isso, você pode executar o binário ambiente de desenvolvimento de software.
Como você se lembra, a regra de build java_binary
produz um .jar
e um script de shell do wrapper. Confira o conteúdo
runner.jar
usando este comando:
jar tf bazel-bin/src/main/java/com/example/cmdline/runner.jar
O conteúdo é:
META-INF/
META-INF/MANIFEST.MF
com/
com/example/
com/example/cmdline/
com/example/cmdline/Runner.class
Como você pode notar, runner.jar
contém Runner.class
, mas não a dependência correspondente.
Greeting.class
O script runner
gerado pelo Bazel adiciona greeter.jar
.
para o caminho de classe. Se deixar assim, ele será executado localmente, mas
e não vai funcionar de forma independente
em outra máquina. Felizmente, a regra java_binary
permite criar um binário independente e implantável. Para criá-lo, anexe
_deploy.jar
, que é o nome do destino:
bazel build //src/main/java/com/example/cmdline:runner_deploy.jar
O Bazel produz uma saída semelhante a esta:
INFO: Found 1 target...
Target //src/main/java/com/example/cmdline:runner_deploy.jar up-to-date:
bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jar
INFO: Elapsed time: 1.700s, Critical Path: 0.23s
Você acabou de criar o runner_deploy.jar
, que pode ser executado de forma independente
seu ambiente de desenvolvimento, já que ele contém o ambiente de execução necessário
dependências. Observe o conteúdo desse JAR independente usando o
o mesmo comando de antes:
jar tf bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jar
O conteúdo inclui todas as classes necessárias para a execução:
META-INF/
META-INF/MANIFEST.MF
build-data.properties
com/
com/example/
com/example/cmdline/
com/example/cmdline/Runner.class
com/example/Greeting.class
Leitura adicional
Confira mais detalhes em:
rules_jvm_external para para gerenciar dependências do Maven transitivas.
Dependências externas para saber mais sobre como trabalhar com repositórios locais e remotos.
Confira as outras regras (link em inglês) para saber mais sobre o Bazel.
O tutorial de criação do C++ para começar a criar Projetos C++ com o Bazel.
O tutorial do aplicativo Android e Tutorial do aplicativo iOS) para começar a usar criação de apps para dispositivos móveis Android e iOS com o Bazel.
Boa construção!