Como migrar do Xcode para o Bazel

Nesta página, descrevemos como criar ou testar um projeto Xcode com o Bazel. Ela descreve as diferenças entre o Xcode e o Bazel e fornece as etapas para converter um projeto do Xcode em um projeto do Bazel. Ele também fornece soluções de problemas para resolver erros comuns.

Diferenças entre o Xcode e o Bazel

  • O Bazel exige que você especifique explicitamente cada destino de build e as dependências, além das configurações correspondentes usando regras de build.

  • O Bazel exige que todos os arquivos de que o projeto depende estejam presentes no diretório do espaço de trabalho ou especificados como importações no arquivo WORKSPACE.

  • Ao criar projetos do Xcode com o Bazel, os arquivos BUILD se tornam a fonte da verdade. Se você trabalha no projeto no Xcode, é necessário gerar uma nova versão dele que corresponda aos arquivos BUILD usando Tulsi sempre que atualizar os arquivos BUILD. Se você não estiver usando o Xcode, os comandos bazel build e bazel test vão fornecer recursos de build e teste com algumas limitações descritas mais adiante neste guia.

  • Devido a diferenças nos esquemas de configuração do build, como layouts de diretório ou sinalizações de build, o Xcode pode não estar totalmente ciente do quadro geral da versão e, portanto, alguns recursos do Xcode podem não funcionar. Ou seja:

    • Dependendo das metas selecionadas para conversão no Tulsi, o Xcode pode não conseguir indexar corretamente a origem do projeto. Isso afeta o preenchimento e a navegação do código no Xcode, já que ele não poderá ver todo o código-fonte do projeto.

    • Análises estáticas, limpadores de endereços e limpadores de linhas de execução podem não funcionar porque o Bazel não produz as saídas que o Xcode espera para esses recursos.

    • Se você gerar um projeto Xcode com o Tulsi e usar esse projeto para executar testes no Xcode, o Xcode executará os testes em vez do Bazel. Para executar testes com o Bazel, execute o comando bazel test manualmente.

Antes de começar

Antes de começar, faça o seguinte:

  1. Instale o Bazel se ainda não tiver feito isso.

  2. Se você não conhece o Bazel e os conceitos dele, conclua o tutorial do app iOS. Você precisa entender o espaço de trabalho do Bazel, incluindo os arquivos WORKSPACE e BUILD, bem como os conceitos de destinos, regras de build e pacotes do Bazel.

  3. Analisar e entender as dependências do projeto.

Analisar as dependências do projeto

Ao contrário do Xcode, o Bazel exige que você declare explicitamente todas as dependências de cada destino no arquivo BUILD.

Para mais informações sobre dependências externas, consulte Como trabalhar com dependências externas.

Criar ou testar um projeto do Xcode com o Bazel

Para criar ou testar um projeto Xcode com o Bazel, faça o seguinte:

  1. Crie o arquivo WORKSPACE

  2. (Experimental) Integrar dependências do CocoaPods

  3. Crie um arquivo BUILD:

    a. Adicionar o destino do aplicativo

    b. (Opcional) Adicionar os destinos de teste

    c. Adicionar os destinos da biblioteca

  4. (Opcional) Granularizar o build

  5. Executar o build

  6. Gerar o projeto Xcode com o Tulsi

Etapa 1: criar o arquivo WORKSPACE

Crie um arquivo WORKSPACE em um novo diretório. Esse diretório se torna a raiz do espaço de trabalho do Bazel. Se o projeto não usar dependências externas, esse arquivo poderá ficar vazio. Se o projeto depender de arquivos ou pacotes que não estejam em um dos diretórios do projeto, especifique essas dependências externas no arquivo WORKSPACE.

Etapa 2: integrar as dependências do CocoaPods (experimental)

Para integrar dependências de CocoaPods ao espaço de trabalho do Bazel, é preciso convertê-las em pacotes do Bazel, conforme descrito em Como converter dependências do CocoaPods.

Etapa 3: criar um arquivo BUILD

Depois de definir o espaço de trabalho e as dependências externas, crie um arquivo BUILD que informe ao Bazel como o projeto está estruturado. Crie o arquivo BUILD na raiz do espaço de trabalho do Bazel e configure-o para fazer um build inicial do projeto da seguinte maneira:

Dica:para saber mais sobre pacotes e outros conceitos do Bazel, consulte Espaços de trabalho, pacotes e destinos.

Etapa 3a: adicionar o destino do aplicativo

Adicione um destino de regra macos_application ou ios_application. Esse destino cria um pacote de aplicativos para macOS ou iOS, respectivamente. Na segmentação, especifique pelo menos o seguinte:

  • bundle_id: o ID do pacote (caminho de DNS reverso seguido pelo nome do app) do binário.

  • provisioning_profile: perfil de provisionamento da sua conta de desenvolvedor da Apple (se estiver criando para um dispositivo iOS).

  • families (somente iOS): se o aplicativo será criado para iPhone, iPad ou ambos.

  • infoplists: lista de arquivos .plist a serem mesclados no arquivo Info.plist final.

  • minimum_os_version: a versão mínima do macOS ou iOS compatível com o aplicativo. Isso garante que o Bazel crie o aplicativo com os níveis de API corretos.

Etapa 3b: adicionar os destinos de teste (opcional)

As regras de build da Apple do Bazel são compatíveis com a execução de testes de unidade baseados em biblioteca no iOS e no macOS, bem como testes baseados em aplicativos no macOS. Para testes baseados em aplicativos no iOS ou na interface em qualquer plataforma, o Bazel vai criar as saídas de teste, mas os testes precisam ser executados no Xcode por meio de um projeto gerado com o Tulsi. Adicione destinos de teste da seguinte maneira:

  • macos_unit_test para executar testes de unidade baseados em biblioteca e em aplicativo em um macOS.

  • ios_unit_test, para executar testes de unidade baseados em biblioteca no iOS. Nos testes que exigem o simulador de iOS, o Bazel cria as saídas de teste, mas não os executa. Gere um projeto do Xcode com o Tulsi e execute os testes nele.

  • ios_ui_test para criar saídas necessárias para executar testes de interface do usuário no simulador do iOS usando o Xcode. Gere um projeto do Xcode com o Tulsi e execute os testes nele. O Bazel não pode executar testes de interface nativamente.

No mínimo, especifique um valor para o atributo minimum_os_version. Embora outros atributos de empacotamento, como bundle_identifier e infoplists, tenham como padrão os valores mais usados, verifique se esses padrões são compatíveis com o projeto e ajuste-os conforme necessário. Para testes que exigem o simulador de iOS, especifique também o nome do destino ios_application como o valor do atributo test_host.

Etapa 3c: adicionar os destinos da biblioteca

Adicione um destino objc_library para cada biblioteca Objective C e um destino swift_library para cada biblioteca Swift de que o aplicativo e/ou testes dependem.

Adicione os destinos da biblioteca desta forma:

  • Adicione os destinos da biblioteca de aplicativos como dependências aos destinos do aplicativo.

  • Adicione os destinos da biblioteca de teste como dependências aos destinos de teste.

  • Liste as origens de implementação no atributo srcs.

  • Liste os cabeçalhos no atributo hdrs.

Para mais informações sobre regras de build, consulte Regras da Apple para o Bazel.

Neste ponto, é uma boa ideia testar o build:

bazel build //:<application_target>

Etapa 4: (opcional) granularidade do build

Se o projeto for grande ou estiver crescendo, considere dividi-lo em vários pacotes do Bazel. Essa granularidade aumentada proporciona:

  • Maior incrementabilidade de builds,

  • Maior carregamento em paralelo de tarefas de build,

  • Melhor manutenção para futuros usuários

  • Melhor controle sobre a visibilidade do código-fonte em destinos e pacotes. Isso evita problemas como bibliotecas que contêm detalhes de implementação vazando em APIs públicas.

Dicas para detalhar o projeto:

  • Coloque cada biblioteca no próprio pacote do Bazel. Comece com aquelas que exigem o menor número de dependências e avance na árvore de dependências.

  • À medida que você adiciona arquivos BUILD e especifica destinos, adicione esses novos destinos aos atributos deps dos destinos que dependem deles.

  • A função glob() não ultrapassa os limites do pacote. Portanto, à medida que o número de pacotes aumenta, os arquivos correspondentes por glob() diminuem.

  • Ao adicionar um arquivo BUILD a um diretório main, adicione também um arquivo BUILD ao diretório test correspondente.

  • Aplique limites de visibilidade íntegros em todos os pacotes.

  • Crie o projeto após cada mudança importante nos arquivos BUILD e corrija os erros de build à medida que eles aparecerem.

Etapa 5: executar o build

Execute o build totalmente migrado para garantir que seja concluído sem erros ou avisos. Execute cada aplicativo e destino do teste individualmente para encontrar com mais facilidade as origens dos erros que ocorrerem.

Exemplo:

bazel build //:my-target

Etapa 6: gerar o projeto Xcode com o Tulsi

Ao criar com o Bazel, os arquivos WORKSPACE e BUILD se tornam a fonte de verdade sobre o build. Para que o Xcode saiba disso, é preciso gerar um projeto Xcode compatível com o Bazel usando o Tulsi.

Solução de problemas

Erros do Bazel podem surgir quando ele fica dessincronizado com a versão selecionada do Xcode, como quando você aplica uma atualização. Se você estiver com erros no Xcode, tente o seguinte: "A versão do Xcode precisa ser especificada para usar um Apple CROSSTOOL".

  • Execute o Xcode manualmente e aceite os Termos e Condições.

  • Use a seleção do Xcode para indicar a versão correta, aceitar a licença e limpar o estado do Bazel.

  sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
  sudo xcodebuild -license
  bazel sync --configure
  • Se isso não funcionar, tente também executar bazel clean --expunge.