Por que um sistema de compilação?

Informar um problema Ver fonte Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

Esta página discute o que são sistemas de build, o que eles fazem, por que você deve usar um sistema de build e por que compiladores e scripts de build não são a melhor escolha à medida que sua organização começa a crescer. Ele é destinado a desenvolvedores que não têm muita experiência com um sistema de build.

O que é um sistema de build?

Basicamente, todos os sistemas de build têm um propósito simples: transformar o código-fonte escrito por engenheiros em binários executáveis que podem ser lidos por máquinas. Os sistemas de build não são apenas para código criado por humanos. Eles também permitem que as máquinas criem builds automaticamente, seja para testes ou para lançamentos em produção. Em uma organização com milhares de engenheiros, é comum que a maioria dos builds seja acionada automaticamente, em vez de diretamente por eles.

Não posso simplesmente usar um compilador?

A necessidade de um sistema de build pode não ser óbvia de imediato. A maioria dos engenheiros não usa um sistema de build ao aprender a programar. Eles começam invocando ferramentas como gcc ou javac diretamente da linha de comando ou o equivalente em um ambiente de desenvolvimento integrado (IDE). Desde que todo o código-fonte esteja no mesmo diretório, um comando como este funciona bem:

javac *.java

Isso instrui o compilador Java a pegar todos os arquivos de origem Java no diretório atual e transformá-los em um arquivo de classe binário. No caso mais simples, isso é tudo o que você precisa.

No entanto, assim que o código se expande, as complicações começam. O javac é inteligente o suficiente para procurar em subdiretórios do diretório atual o código a ser importado. No entanto, não há como encontrar código armazenado em outras partes do sistema de arquivos (talvez uma biblioteca compartilhada por vários projetos). Ele também só sabe criar código Java. Sistemas grandes geralmente envolvem diferentes partes escritas em uma variedade de linguagens de programação com redes de dependências entre essas partes. Isso significa que nenhum compilador de uma única linguagem pode criar o sistema inteiro.

Quando você trabalha com código de várias linguagens ou várias unidades de compilação, a criação de código não é mais um processo de uma etapa. Agora você precisa avaliar de que seu código depende e criar essas partes na ordem correta, possivelmente usando um conjunto diferente de ferramentas para cada parte. Se alguma dependência mudar, repita esse processo para evitar depender de binários desatualizados. Para uma base de código de tamanho moderado, esse processo rapidamente se torna tedioso e propenso a erros.

O compilador também não sabe como processar dependências externas, como arquivos JAR de terceiros em Java. Sem um sistema de build, você pode gerenciar isso baixando a dependência da Internet, colocando em uma pasta lib no disco rígido e configurando o compilador para ler bibliotecas desse diretório. Com o tempo, é difícil manter as atualizações, versões e fontes dessas dependências externas.

E os scripts de shell?

Suponha que seu projeto de hobby comece simples o suficiente para que você possa criá-lo usando apenas um compilador, mas você começa a enfrentar alguns dos problemas descritos anteriormente. Talvez você ainda não ache que precisa de um sistema de build e possa automatizar as partes tediosas usando alguns scripts simples do shell que cuidam de criar coisas na ordem correta. Isso ajuda por um tempo, mas logo você começa a ter ainda mais problemas:

  • Isso se torna tedioso. À medida que seu sistema se torna mais complexo, você começa a gastar quase o mesmo tempo trabalhando nos scripts de build e no código real. A depuração de scripts shell é difícil, com cada vez mais hacks sendo colocados uns sobre os outros.

  • É lento. Para garantir que você não estava usando bibliotecas desatualizadas por acidente, faça com que o script de build crie todas as dependências em ordem sempre que você o executar. Você pensa em adicionar alguma lógica para detectar quais partes precisam ser recriadas, mas isso parece muito complexo e propenso a erros para um script. Ou você pensa em especificar quais partes precisam ser reconstruídas a cada vez, mas volta à estaca zero.

  • Boas notícias: chegou a hora de lançar! É melhor descobrir todos os argumentos que você precisa transmitir ao comando jar para fazer o build final. E lembre-se de como fazer upload e enviar para o repositório central. Além disso, crie e envie as atualizações da documentação e envie uma notificação aos usuários. Hmm, talvez isso exija outro script...

  • É um desastre! O disco rígido falha, e agora você precisa recriar todo o sistema. Você foi inteligente o suficiente para manter todos os arquivos de origem no controle de versão, mas e as bibliotecas que você baixou? Você pode encontrar todos eles de novo e verificar se são a mesma versão de quando você os baixou pela primeira vez? Seus scripts provavelmente dependiam de ferramentas específicas instaladas em locais específicos. É possível restaurar esse mesmo ambiente para que os scripts funcionem novamente? E aquelas variáveis de ambiente que você definiu há muito tempo para que o compilador funcionasse corretamente e depois esqueceu?

  • Apesar dos problemas, seu projeto está tendo sucesso suficiente para que você possa começar a contratar mais engenheiros. Agora você percebe que não é preciso um desastre para que os problemas anteriores surjam. É necessário passar pelo mesmo processo doloroso de bootstrap sempre que um novo desenvolvedor entra na equipe. E, apesar de todos os esforços, ainda há pequenas diferenças no sistema de cada pessoa. Muitas vezes, o que funciona na máquina de uma pessoa não funciona na de outra. Cada vez que isso acontece, leva algumas horas de depuração de caminhos de ferramentas ou versões de biblioteca para descobrir onde está a diferença.

  • Você decide que precisa automatizar seu sistema de build. Em teoria, isso é tão simples quanto comprar um novo computador e configurá-lo para executar o script de build todas as noites usando o cron. Você ainda precisa passar pelo processo de configuração complicado, mas agora não tem o benefício de um cérebro humano capaz de detectar e resolver problemas menores. Agora, todas as manhãs, ao chegar, você vê que o build da noite passada falhou porque ontem um desenvolvedor fez uma mudança que funcionou no sistema dele, mas não no sistema de build automatizado. Cada vez que isso acontece, a correção é simples, mas é tão frequente que você acaba gastando muito tempo todos os dias descobrindo e aplicando essas correções simples.

  • As builds ficam cada vez mais lentas à medida que o projeto cresce. Um dia, enquanto espera a conclusão de um build, você olha com tristeza para a área de trabalho ociosa do colega de trabalho, que está de férias, e gostaria que houvesse uma maneira de aproveitar todo esse poder computacional desperdiçado.

Você encontrou um problema clássico de escala. Para um único desenvolvedor trabalhando em no máximo algumas centenas de linhas de código por no máximo uma ou duas semanas (que pode ter sido toda a experiência até agora de um desenvolvedor júnior recém-formado na universidade), um compilador é tudo o que você precisa. Os scripts podem levar você um pouco mais longe. Mas assim que você precisa coordenar vários desenvolvedores e máquinas, mesmo um script de build perfeito não é suficiente, porque fica muito difícil considerar as pequenas diferenças nessas máquinas. Nesse ponto, essa abordagem simples falha, e é hora de investir em um sistema de build real.