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?
Fundamentalmente, todos os sistemas de build têm um propósito simples: eles transformam 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 versões de produção. Em uma organização com milhares de engenheiros, é comum que a maioria dos builds seja acionada automaticamente, em vez de diretamente por engenheiros.
Não posso usar apenas um compilador?
A necessidade de um sistema de build pode não ser imediatamente óbvia. A maioria dos engenheiros
não usa um sistema de build ao aprender a programar: a maioria começa invocando ferramentas
como gcc ou javac diretamente na linha de comando ou o equivalente em um
ambiente de desenvolvimento integrado (IDE). Enquanto todo o código-fonte estiver em
o mesmo diretório, um comando como este funcionará bem:
javac *.javaIsso instrui o compilador Java a usar todos os arquivos de origem Java no diretório atual e transformá-los em um arquivo de classe binária. No caso mais simples, é tudo o que você precisa.
No entanto, assim que o código se expande, as complicações começam. javac é inteligente
o suficiente para procurar em subdiretórios do diretório atual para encontrar o código a
ser importado. Mas ele não tem 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
como criar código Java. Sistemas grandes geralmente envolvem diferentes partes escritas em
várias linguagens de programação com redes de dependências entre essas partes,
o que significa que nenhum compilador para uma única linguagem pode criar todo o sistema.
Depois de trabalhar 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 do 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, você deve repetir 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 lidar com dependências externas, como arquivos JAR de terceiros em Java. Sem um sistema de build,
você pode gerenciar isso fazendo o download da dependência da Internet, colocando
a 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 origem 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 encontrar 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 de shell simples que cuidam da criação de coisas na ordem correta. Isso ajuda por um tempo, mas logo você começa a ter ainda mais problemas:
Ele se torna tedioso. À medida que seu sistema se torna mais complexo, você começa a gastar quase tanto tempo trabalhando nos scripts de build quanto no código real. A depuração de scripts de shell é dolorosa, com mais e mais hacks sendo sobrepostos.
É lento. Para garantir que você não estava dependendo acidentalmente de bibliotecas desatualizadas, o script de build cria todas as dependências em ordem sempre que você o executa. 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 recriadas a cada vez, mas então você volta à estaca zero.
Boas notícias: é hora de uma versão! É melhor descobrir todos os argumentos que você precisa transmitir ao comando jar para fazer seu build final. E lembre-se de como fazer o upload e enviar para o repositório central. E crie e envie as atualizações de documentação e envie uma notificação aos usuários. Hmm, talvez isso exija outro script...
Desastre! Seu 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ões, mas e as bibliotecas que você fez o download? Você pode encontrar todos eles novamente e garantir que eles eram a mesma versão de quando você os fez o download pela primeira vez ? Seus scripts provavelmente dependiam de ferramentas específicas sendo instaladas em locais específicos. Você pode restaurar esse mesmo ambiente para que os scripts funcionem novamente? E todas aquelas variáveis de ambiente que você definiu há muito tempo para fazer o compilador funcionar corretamente e depois esqueceu?
Apesar dos problemas, seu projeto é bem-sucedido o 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. Você precisa passar pelo mesmo processo de inicialização doloroso sempre que um novo desenvolvedor entra na sua equipe. E apesar dos seus melhores esforços, ainda há pequenas diferenças no sistema de cada pessoa. Com frequência, o que funciona na máquina de uma pessoa não funciona na de outra, e cada vez 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 doloroso, 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, quando você chega , vê que o build da noite anterior falhou porque ontem um desenvolvedor fez uma mudança que funcionou no sistema dele, mas não no sistema de build automatizado. Cada vez é uma correção simples, mas acontece com tanta frequência que você acaba gastando muito tempo todos os dias descobrindo e aplicando essas correções simples.
Os builds ficam cada vez mais lentos à medida que o projeto cresce. Um dia, enquanto espera a conclusão de um build, você olha tristemente para a área de trabalho ociosa do seu 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 que trabalha em at 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 ), um compilador é tudo o que você precisa. Os scripts podem levar você um pouco mais longe. Mas assim que você precisar coordenar vários desenvolvedores e máquinas, mesmo um script de build perfeito não será suficiente, porque se torna muito difícil contabilizar as pequenas diferenças nessas máquinas. Nesse ponto, essa abordagem simples falha e é hora de investir em um sistema de build real.