
Maven: prevenir compilação desnecessária
Contents
O maven-compiler-plugin do Maven é otimizado para compilar fontes apenas quando necessário, mas às vezes a recompilação ocorre de forma indesejada. Veremos como contornar esse comportamento.
📋 Esse post se aplica mais a Pipelines, mas recomendo você continuar a leitura 😃
Verificando que o plugin, por padrão, não compila de forma desnecessária
Em um projeto Maven, executar:
É possível ver que foram compilados 1062 arquivos.
Agora vamos empacotar:
E é possível ver que nada foi compilado, o maven aproveitou o que já existe na pasta target.
Como o Maven sabe o que precisa ou não compilar?
Se o .class de um .java não existir na pasta target ou se a data de modificação do .class for anterior a do .java, o plugin vai disparar a compilação desses sources.
Caso de uso em que o Maven acaba re-compilando sem necessidade
Em Pipelines, como os do GitLab por exemplo, é comum dividirmos as fases envolvidas no processo de build (compile, test, package etc) em estágios distintos. Assim existe uma fase que compila o código gerando os .class, e uma fase seguinte pega esse compilado para empacotar e gerar o .jar, por exemplo.
Então imagine um pipeline, para simplificar, com apenas dois estágios, chamados compilação e empacotamento e que executam exatamente nessa ordem.
O estágio
compilaçãotem as seguintes etapas:- obtém o código do repositório Git (git clone)
- executa um
mvn clean compile - guarda a pasta
targetpara o estágioempacotamento
O estágio
empacotamentotem as seguintes etapas:- obtém o código do repositório Git (git clone)
[1] - obtém a pasta
targetdo estágiocompilação[2] - executa um
mvn package[3] - guarda a pasta
targetpara um estágio posterior
- obtém o código do repositório Git (git clone)
O mvn packge [3] acaba compilando o código novamente, pois ao obter o código do repositório Git [1] a data de modicação dos arquivos fica sendo a do instante do git clone e essa data é posterior a data dos .class que estão no target [2], desse modo o Maven acredita que os .java sofreram modificação e executa a compilação (novamente).
📋 O comando
stat nome-do-arquivoexibe informações de data do arquivo, entre outras coisas.
Solução
Solução 1: executar tudo em um único estágio
Se o mvn clean compile e mvn package estiverem em um mesmo estágio, o source e target serão os mesmos e não haverá mudança do timestamp nos arquivos, logo o maven não terá porque re-compilar o código.
📋 Geralmente em pipelines queremos quebrar em estágios para dar mais visibilidade do que cada etapa faz, então essa solução, podemos dizer, não se aplica muito.
Solução 2: tocar os .class / touch
No estágio que executa o empacotamento obtendo o source a partir do repo Git e o target a partir de estágio anterior, basta tocar todos os .class, assim a data dos .class será posterior a data dos .java, algo como:
Solução 3: usar a propriedade -DlastModGranularityMs do plugin de compilação
Conforme a documentação do plugin maven-compiler-plugin:
Sets the granularity in milliseconds of the last modification date for testing whether a source needs recompilation
Ou seja, nela informamos o máximo de tempo tolerado de defasagem entre a data de modificação do .class e do seu respectivo source (.java). Por mais que o .java esteja com data de modificação posterior a do .class, se estiver dentro do limite espeficiado na opção lastModGranularityMs, o plugin não irá re-compilar o .java.
| |
📋
1h * 3600 * 1000 = 3600000 ms
MhagnumDw

