Como migrar do Maven para o Bazel

Informar um problema Ver código-fonte

Nesta página, descrevemos como migrar do Maven para o Bazel, incluindo os pré-requisitos e as etapas de instalação. Ele descreve as diferenças entre o Maven e o Bazel e fornece um exemplo de migração usando o projeto Guava.

Ao migrar de qualquer ferramenta de build para o Bazel, é melhor que as duas ferramentas de compilação sejam executadas em paralelo até que você migre totalmente a equipe de desenvolvimento, o sistema de CI e outros sistemas relevantes. É possível executar o Maven e o Bazel no mesmo repositório.

Antes de começar

  • Instale o Bazel se ele ainda não estiver instalado.
  • Se você não está familiarizado com o Bazel, consulte o tutorial Introdução ao Bazel: criar Java antes de começar a migração. No tutorial, explicamos conceitos, estrutura e sintaxe do identificador do Bazel.

Diferenças entre o Maven e o Bazel

  • O Maven usa arquivos de nível superior pom.xml. O Bazel oferece suporte a vários arquivos de versão e vários destinos por arquivo BUILD, permitindo versões mais incrementais que as do Maven.
  • O Maven é responsável pelas etapas do processo de implantação. O Bazel não automatiza a implantação.
  • O Bazel permite que você expresse dependências entre linguagens.
  • Ao adicionar novas seções ao projeto, com o Bazel, talvez seja necessário adicionar novos arquivos BUILD. A prática recomendada é adicionar um arquivo BUILD a cada novo pacote Java.

Migrar do Maven para o Bazel

As etapas abaixo descrevem como migrar seu projeto para o Bazel:

  1. Criar o arquivo do WORKSPACE
  2. Criar um arquivo BUILD
  3. Criar mais arquivos BUILD
  4. Criar usando o Bazel

Os exemplos abaixo vêm de uma migração do projeto Guava do Maven para o Bazel. O projeto Guava usado é a versão v31.1. Os exemplos que usam Guava não percorrem cada etapa da migração, mas mostram os arquivos e o conteúdo que são gerados ou adicionados manualmente.

$ git clone https://github.com/google/guava.git && cd guava
$ git checkout v31.1

1. Criar o arquivo WORKSPACE

Crie um arquivo chamado WORKSPACE na raiz do seu projeto. Se o projeto não tiver dependências externas, o arquivo do espaço de trabalho poderá ficar vazio.

Se o projeto depender de arquivos ou pacotes que não estiverem em um dos diretórios do projeto, especifique essas dependências externas no arquivo do espaço de trabalho. Para automatizar a listagem de dependências externas do arquivo do espaço de trabalho, use rules_jvm_external. Para instruções sobre como usar esse conjunto de regras, consulte o README.

Exemplo de projeto Guava: dependências externas

É possível listar as dependências externas do projeto Guava com o conjunto de regras rules_jvm_external.

Adicione o seguinte snippet ao arquivo WORKSPACE:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

RULES_JVM_EXTERNAL_TAG = "4.3"
RULES_JVM_EXTERNAL_SHA = "6274687f6fc5783b589f56a2f1ed60de3ce1f99bc4e8f9edef3de43bdf7c6e74"

http_archive(
    name = "rules_jvm_external",
    sha256 = RULES_JVM_EXTERNAL_SHA,
    strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
    url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
)

load("@rules_jvm_external//:defs.bzl", "maven_install")

maven_install(
    artifacts = [
        "com.google.code.findbugs:jsr305:3.0.2",
        "com.google.errorprone:error_prone_annotations:2.11.0",
        "com.google.j2objc:j2objc-annotations:1.3",
        "org.codehaus.mojo:animal-sniffer-annotations:1.20",
        "org.checkerframework:checker-qual:3.12.0",
    ],
    repositories = [
        "https://repo1.maven.org/maven2",
    ],
)

2. Criar um arquivo BUILD

Agora que seu espaço de trabalho foi definido e as dependências externas (se aplicáveis) estão listadas, você precisa criar arquivos BUILD para descrever como o projeto precisa ser criado. Ao contrário do Maven com o próprio arquivo pom.xml, o Bazel pode usar muitos arquivos BUILD para criar um projeto. Esses arquivos especificam vários destinos de versão, o que permite que o Bazel produza versões incrementais.

Adicione arquivos BUILD em etapas. Comece adicionando um arquivo BUILD na raiz do seu projeto e usando-o para fazer uma versão inicial usando o Bazel. Em seguida, refine o build adicionando mais arquivos BUILD com destinos mais granulares.

  1. No mesmo diretório do arquivo WORKSPACE, crie um arquivo de texto e o nomeie como BUILD.

  2. Nesse arquivo BUILD, use a regra apropriada para criar um destino para criar seu projeto. Veja algumas dicas:

    • Use a regra apropriada:

      • Para criar projetos com um único módulo Maven, use a regra java_library desta maneira:

        java_library(
            name = "everything",
            srcs = glob(["src/main/java/**/*.java"]),
            resources = glob(["src/main/resources/**"]),
            deps = ["//:all-external-targets"],
        )
        
      • Para criar projetos com vários módulos Maven, use a regra java_library da seguinte maneira:

        java_library(
            name = "everything",
            srcs = glob([
                "Module1/src/main/java/**/*.java",
                "Module2/src/main/java/**/*.java",
                ...
            ]),
            resources = glob([
                "Module1/src/main/resources/**",
                "Module2/src/main/resources/**",
                ...
            ]),
            deps = ["//:all-external-targets"],
        )
        
      • Para criar binários, use a regra java_binary:

        java_binary(
            name = "everything",
            srcs = glob(["src/main/java/**/*.java"]),
            resources = glob(["src/main/resources/**"]),
            deps = ["//:all-external-targets"],
            main_class = "com.example.Main"
        )
        
    • Especifique os atributos:

      • name: dê um nome significativo ao destino. Nos exemplos acima, o alvo é chamado de "tudo".
      • srcs: use glob para listar todos os arquivos .java no projeto.
      • resources: use globs para listar todos os recursos no projeto.
      • deps: você precisa determinar de quais dependências externas seu projeto precisa. Por exemplo, se você gerou uma lista de dependências externas usando a ferramenta generate_workspace, as dependências de java_library serão as bibliotecas listadas na macro generated_java_libraries.
    • Veja o exemplo abaixo deste arquivo BUILD de nível superior na migração do projeto Guava.

  3. Agora que você tem um arquivo BUILD na raiz do seu projeto, crie o projeto para garantir que ele funcione. Na linha de comando, no diretório do espaço de trabalho, use bazel build //:everything para criar seu projeto com o Bazel.

    O projeto foi criado com sucesso com o Bazel. Você precisará adicionar mais arquivos BUILD para permitir builds incrementais do projeto.

Exemplo de projeto Guava: começar com um arquivo BUILD

Ao migrar o projeto Guava para o Bazel, inicialmente um arquivo BUILD é usado para criar todo o projeto. Veja o conteúdo do arquivo BUILD inicial no diretório do espaço de trabalho:

java_library(
    name = "everything",
    srcs = glob([
        "guava/src/**/*.java",
        "futures/failureaccess/src/**/*.java",
    ]),
    deps = [
        "@maven//:com_google_code_findbugs_jsr305",
        "@maven//:com_google_errorprone_error_prone_annotations",
        "@maven//:com_google_j2objc_j2objc_annotations",
        "@maven//:org_checkerframework_checker_qual",
        "@maven//:org_codehaus_mojo_animal_sniffer_annotations",
    ],
)

3. Criar mais arquivos BUILD (opcional)

O Bazel funciona com apenas um BUILD file, como você viu após concluir seu primeiro build. Considere dividir o build em blocos menores adicionando mais arquivos BUILD com destinos granulares.

Vários arquivos BUILD com vários destinos vão aumentar a granularidade do build, permitindo:

  • aumento dos builds incrementais do projeto,
  • maior execução paralela do build;
  • melhor manutenção do build para usuários futuros; e
  • controle sobre a visibilidade de destinos entre pacotes, o que pode evitar problemas como bibliotecas que contêm detalhes de implementação que vazam para APIs públicas.

Dicas para adicionar mais arquivos BUILD:

  • Para começar, adicione um arquivo BUILD a cada pacote Java. Comece com pacotes Java que tenham menos dependências e trabalhem até pacotes com mais dependências.
  • Ao adicionar arquivos BUILD e especificar destinos, inclua esses novos destinos nas seções deps dos destinos que dependem deles. A função glob() não cruza os limites do pacote, por isso, à medida que o número de pacotes aumenta, os arquivos correspondentes por glob() são reduzidos.
  • Sempre que você adicionar um arquivo BUILD a um diretório main, inclua um arquivo BUILD ao diretório test correspondente.
  • Tome cuidado para limitar a visibilidade corretamente entre os pacotes.
  • Para simplificar os erros de solução de problemas na configuração de arquivos BUILD, verifique se o projeto continua a ser compilado com o Bazel enquanto você adiciona cada arquivo de versão. Execute bazel build //... para garantir que todos os seus destinos ainda sejam criados.

4. Criar usando o Bazel

Você está criando usando o Bazel enquanto adiciona arquivos BUILD para validar a configuração do build.

Quando você tem arquivos BUILD na granularidade desejada, pode usar o Bazel para produzir todos os seus builds.