No trabalho tivemos um problema com a GraalVM, e devido a isso essa issue foi criada.
Eu falei com o autor e pensei "será que não seria melhor criar um projeto maven com isso bonitinho? Subir um repo, MCVE, que você controla através de profiles as dependências?"
E cá está o repo! github.com/jeffque/graalvm-regression
Pois bem, aproveitar que vou fazer isso e compartilhar um pouco o processo.
Iniciando um projeto maven
Para começar, eu gosto de iniciar o projeto maven colocar o mvnw
(maven wrapper). Eu normalmente começo copiando o mvnw
de um outro repositório meu conhecido, mas posso fazer isso de modo mais canônico. Como por exemplo, seguindo esse tutorial do Baeldung:
$ mvn -N wrapper:wrapper
Ficou assim o diretório:
.
├── .mvn
│ └── wrapper
│ └── maven-wrapper.properties
├── mvnw
└── mvnw.cmd
Qual a versão do maven que ele usa? Podemos perguntar diretamente ao mvwn
ou então consultar em .mvn/wrapper/maven-wrapper.properties
. No caso, o conteúdo do arquivo é:
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
Muito bem, maven na versão 3.9.9, uma delícia.
Criando o projeto base
Existem diversas maneiras de iniciar um projeto maven. Inclusive a de você
o pom.xml
totalmente do zero. Mas... um modo mais canônico seria pedir um
arquétipo, que nem eu fiz em "Hello, World!" em GWT usando arquétipo do TBroyer.
Então, vamos pedir o arquétipo vazio? Normalmente eu peço o maven-archetype-quickstart
. Tem uma lista não exaustiva de arquétipos aqui.
./mvnw archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.5
Após baixar as dependências necessárias para rodar o arquétipo ele me pergunta:
- Define value for property 'groupId'
- com.jeffque
- Define value for property 'artifactId'
- graalvm-24-1-2-regression
- Define value for property 'version' 1.0-SNPASHOT
- <enter> (o que é equivalente a usar o default, que no caso é
1.0-SNAPSHOT
)
- <enter> (o que é equivalente a usar o default, que no caso é
- Define value for property 'package' com.jeffque:
- <enter> (o que é equivalente a usar o default, que no caso é
com.jeffque
)
- <enter> (o que é equivalente a usar o default, que no caso é
Após responder, ele pede para confirmar:
Define value for property 'package' com.jeffque:
Confirm properties configuration:
javaCompilerVersion: 17
junitVersion: 5.11.0
groupId: com.jeffque
artifactId: graalvm-24-1-2-regression
version: 1.0-SNAPSHOT
package: com.jeffque
Y:
Confimei e... puff!
.
├── .mvn
│ └── wrapper
│ └── maven-wrapper.properties
├── graalvm-24-1-2-regression
│ ├── .mvn
│ │ ├── jvm.config
│ │ └── maven.config
│ ├── pom.xml
│ └── src
│ ├── main
│ │ └── java
│ │ └── com
│ │ └── jeffque
│ │ └── App.java
│ └── test
│ └── java
│ └── com
│ └── jeffque
│ └── AppTest.java
├── mvnw
└── mvnw.cmd
Gerei no canto errado? Ok, só mover uma pasta pra lá
←\leftarrow←
.
cd graalvm-24-1-2-regression
mv pom.xml src ../
mv .mvn/* ../.mvn
rmdir .mvn
cd ..
rmdir graalvm-24-1-2-regression
Ok, ajeitado:
.
├── .mvn
│ ├── jvm.config
│ ├── maven.config
│ └── wrapper
│ └── maven-wrapper.properties
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
│ └── java
│ └── com
│ └── jeffque
│ └── App.java
└── test
└── java
└── com
└── jeffque
└── AppTest.java
Adicionando comando de execução
Eu quero facilitar a vida de quem vai testar usando Maven. Para tal, vou configurar o plugin exec
. O plugin é esse: exec-maven-plugin
.
Na linha de comando:
> ./mvnw exec:java -Dexec.mainClass="com.jeffque.App"
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------< com.jeffque:graalvm-24-1-2-regression >----------------
[INFO] Building graalvm-24-1-2-regression 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- exec:3.5.0:java (default-cli) @ graalvm-24-1-2-regression ---
Hello World!
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.383 s
[INFO] Finished at: 2025-01-31T08:02:13-03:00
[INFO] ------------------------------------------------------------------------
Beleza, vamos por no pom? Em pluginManagement
a gente indica commo o plugin vai ser executado ao ser chamado, não necessariamente ele será chamado. Então, se eu adicionar o exec-maven-plugin
lá, o maven não tentará fazer o fetch desse plugin a priori. Já em build.plugins
... aí a gente está indicando que o plugin será de fato executado.
Aí vem a questão: quando usar algo em build.plugins
? Quando precisamos que o plugin seja executado e não tem implícito em algum lugar isso. Como por exemplo, chamar o retrolambda
para permitir usar a sintaxe de lambdas e conseguir dar um target para java 7.
Ok, vamos adicionar as configurações do plugin em pluginManagement
? Primeiramente, vamos colocar o plugin lá:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.5.0</version>
</plugin>
Ok, prendemos a versão. Agora, vamos para o como vai ser chamado. No caso, quando for chamado o mojo exec:java
: exec
identifica esse plugin e java
o goal específico:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
</plugin>
Ok, agora, vamos por a configuração da classe principal:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.5.0</version>
<executions>...</executions>
<configuration>
<mainClass>com.jeffque.App</mainClass>
</configuration>
</plugin>
Plugin configurado! Será?
> ./mvnw exec:java
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------< com.jeffque:graalvm-24-1-2-regression >----------------
[INFO] Building graalvm-24-1-2-regression 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- exec:3.5.0:java (default-cli) @ graalvm-24-1-2-regression ---
Hello World!
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.341 s
[INFO] Finished at: 2025-01-31T08:28:48-03:00
[INFO] ------------------------------------------------------------------------
Adaptando ao problema da issue
Vamos colocar as coisas da issue do GitHub. Primeiramente, para a versão problemática do GraalVM. Vamos alterar a main
para refletir como está na issue.
Adicionar as dependências e alterar a main
foi uma non-issue, bem direto ao ponto. Para testar, coloquei as dependências para a versão do GraalVM com problema e mandei executar:
> ./mvnw compile exec:java
...
org.graalvm.polyglot.PolyglotException: TypeError: k.equals is not a function
at <js>.:=> (Unnamed:6)
at com.oracle.truffle.polyglot.PolyglotFunctionProxyHandler.invoke (PolyglotFunctionProxyHandler.java:151)
...
Perfeito! Tal qual aparece na issue!
Profiles
Vou criar perfis distintos. E neles vou colocar as dependências. Você pode ler mais na documentação oficial.
Vou criar dois perfis:
- graalvm-24, apresenta o problema
- graalvm-20, não apresenta o problema
<profiles>
<profile>
<id>graalvm-24</id>
</profile>
<profile>
<id>graalvm-24</id>
</profile>
</profiles>
Ok. Agora, vou deixar o perfil graalvm-24
ativo por default. Se ninguém falar nada, ele que será invocado:
<profile>
<id>graalvm-24</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
Como invocar esse perfil? Passando a opção -P
na linha de comando!
Por exemplo:
./mvnw exec:java -Pgraalvm-24
Estou explicitando que quero o perfil graalvm-24
. De modo semelhante:
./mvnw exec:java -Pgraalvm-20
Para o perfil graalvm-20
. Eu poderia passar múltiplos perfis também:
./mvnw exec:java -Pgraalvm-20,jeff,marmota
Isso ativa os perfis graalvm-20
, jeff
e marmota
. E no caso o activateByDefault
, funciona como? Bem, se você não falar nada...
./mvnw exec:java
# note que não tem -P
Aí só ativa o que tá cadastrado pra rodar por padrão.
Muito bem, agora eu coloco as dependências:
<profiles>
<profile>
<id>graalvm-24</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<artifactId>polyglot</artifactId>
<version>24.1.2</version>
</dependency>
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<artifactId>js-community</artifactId>
<version>24.1.2</version>
<type>pom</type>
<scope>runtime</scope>
</dependency>
</dependencies>
</profile>
<profile>
<id>graalvm-20</id>
<dependencies>
<dependency>
<groupId>org.graalvm.sdk</groupId>
<artifactId>graal-sdk</artifactId>
<version>20.1.0</version>
</dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js</artifactId>
<version>20.1.0</version>
</dependency>
</dependencies>
</profile>
</profiles>
E pronto, agora tenho dois perfis distintos! Note que o graalvm
foi removido do project.dependencies
, pois essas dependências sem perfil afetam a todos.
Para testar:
> ./mvnw exec:java
...
org.graalvm.polyglot.PolyglotException: TypeError: k.equals is not a function
at <js>.:=> (Unnamed:6)
...
> ./mvnw exec:java -P graalvm-20
...
[INFO] --- exec:3.5.0:java (default-cli) @ graalvm-24-1-2-regression ---
Optional[true]
[INFO] ------------------------------------------------------------------------
...
Author Of article : Jefferson Quesado Read full article