Skip to content

Commit 4f29b84

Browse files
committed
[GR-52199] Maven plugin should not need the launcher on the project classpath.
PullRequest: graalpython/3207
2 parents a4c3b64 + ec3b698 commit 4f29b84

File tree

8 files changed

+122
-54
lines changed

8 files changed

+122
-54
lines changed

graalpython/com.oracle.graal.python.test/src/tests/standalone/check_home_pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ SOFTWARE.
4545
<modelVersion>4.0.0</modelVersion>
4646

4747
<groupId>org.apache.maven.plugin.my.unit</groupId>
48-
<artifactId>project-to-test</artifactId>
48+
<artifactId>project-check-home</artifactId>
4949
<version>1.0-SNAPSHOT</version>
5050
<packaging>jar</packaging>
5151
<name>Test MyMojo</name>

graalpython/com.oracle.graal.python.test/src/tests/standalone/fail_without_graalpy_dep_pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ SOFTWARE.
4545
<modelVersion>4.0.0</modelVersion>
4646

4747
<groupId>org.apache.maven.plugin.my.unit</groupId>
48-
<artifactId>project-to-test</artifactId>
48+
<artifactId>project-fail-without-graalpy-dep</artifactId>
4949
<version>1.0-SNAPSHOT</version>
5050
<packaging>jar</packaging>
5151
<name>Test MyMojo</name>

graalpython/com.oracle.graal.python.test/src/tests/standalone/prepare_venv_pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ SOFTWARE.
4545
<modelVersion>4.0.0</modelVersion>
4646

4747
<groupId>org.apache.maven.plugin.my.unit</groupId>
48-
<artifactId>project-to-test</artifactId>
48+
<artifactId>project-prepare-venv</artifactId>
4949
<version>1.0-SNAPSHOT</version>
5050
<packaging>jar</packaging>
5151
<name>Test MyMojo</name>

graalpython/com.oracle.graal.python.test/src/tests/standalone/test_standalone.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -256,9 +256,9 @@ def purge_local_repo(self, target_dir, resolve=True):
256256
if not skip_purge:
257257
self.env["MVN"] = " ".join(MVN_CMD + [f"-Dgraalpy.version={self.graalvmVersion}", "-Dgraalpy.edition=python-community"])
258258
if resolve:
259-
cmd = MVN_CMD + ["dependency:purge-local-repository", f"-Dgraalpy.version={self.graalvmVersion}", "-Dgraalpy.edition=python-community"]
259+
cmd = MVN_CMD + ["dependency:purge-local-repository", f"-Dinclude=org.graalvm.python:graalpy-maven-plugin", f"-Dgraalpy.version={self.graalvmVersion}", "-Dgraalpy.edition=python-community"]
260260
else:
261-
cmd = MVN_CMD + ["dependency:purge-local-repository", "-DreResolve=false", f"-Dgraalpy.version={self.graalvmVersion}", "-Dgraalpy.edition=python-community"]
261+
cmd = MVN_CMD + ["dependency:purge-local-repository", "-DreResolve=false", f"-Dinclude=org.graalvm.python:graalpy-maven-plugin", f"-Dgraalpy.version={self.graalvmVersion}", "-Dgraalpy.edition=python-community"]
262262
run_cmd(cmd, self.env, cwd=target_dir)
263263

264264
@unittest.skipUnless(is_enabled, "ENABLE_STANDALONE_UNITTESTS is not true")
@@ -316,12 +316,7 @@ def test_fail_without_graalpy_dep(self):
316316
try:
317317
cmd = MVN_CMD + ["process-resources"]
318318
out, return_code = run_cmd(cmd, self.env, cwd=target_dir)
319-
if sys.platform != 'win32':
320-
assert "Missing GraalPy dependency org.graalvm.python:python-language" in out
321-
else:
322-
# different error message on windows due to generate launcher python script executed
323-
# before the actuall process-resources goal
324-
assert "Could not find or load main class com.oracle.graal.python.shell.GraalPythonMain" in out
319+
assert "Missing GraalPy dependency. Please add to your pom either org.graalvm.polyglot:python-community or org.graalvm.polyglot:python" in out
325320

326321
finally:
327322
self.purge_local_repo(target_dir, False)

graalpython/graalpy-archetype-polyglot-app/src/main/resources/archetype-resources/pom.xml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,7 @@
2929
<artifactId>${symbol_dollar}{graalpy.edition}</artifactId>
3030
<version>${symbol_dollar}{graalpy.version}</version>
3131
<type>pom</type>
32-
</dependency>
33-
<!-- The dependency on python-launcher is only required if the graalpy
34-
plugin is used with packages -->
35-
<dependency>
36-
<groupId>org.graalvm.python</groupId>
37-
<artifactId>python-launcher</artifactId>
38-
<version>${symbol_dollar}{graalpy.version}</version>
39-
</dependency>
40-
32+
</dependency>
4133
<dependency>
4234
<groupId>org.graalvm.python</groupId>
4335
<artifactId>python-embedding</artifactId>

graalpython/graalpy-maven-plugin/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,5 +111,11 @@ SOFTWARE.
111111
<artifactId>python-embedding</artifactId>
112112
<version>${graalpy.version}</version>
113113
</dependency>
114+
<dependency>
115+
<groupId>org.graalvm.python</groupId>
116+
<artifactId>python-launcher</artifactId>
117+
<version>${graalpy.version}</version>
118+
<scope>runtime</scope>
119+
</dependency>
114120
</dependencies>
115121
</project>

graalpython/graalpy-maven-plugin/src/main/java/org/graalvm/python/maven/plugin/ManageResourcesMojo.java

Lines changed: 105 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -40,35 +40,44 @@
4040
*/
4141
package org.graalvm.python.maven.plugin;
4242

43+
import org.apache.maven.artifact.Artifact;
44+
import org.apache.maven.artifact.DefaultArtifact;
45+
import org.apache.maven.artifact.handler.DefaultArtifactHandler;
46+
import org.apache.maven.execution.MavenSession;
47+
import org.apache.maven.plugin.AbstractMojo;
48+
import org.apache.maven.plugin.MojoExecutionException;
49+
import org.apache.maven.plugin.logging.Log;
50+
import org.apache.maven.plugins.annotations.*;
51+
import org.apache.maven.project.*;
52+
import org.eclipse.aether.graph.Dependency;
53+
import org.graalvm.python.embedding.utils.GraalPyRunner;
54+
import org.graalvm.python.embedding.utils.VFSUtils;
55+
4356
import java.io.File;
4457
import java.io.FileWriter;
4558
import java.io.IOException;
46-
import java.nio.file.*;
59+
import java.nio.file.Files;
60+
import java.nio.file.Path;
61+
import java.nio.file.Paths;
62+
import java.nio.file.StandardOpenOption;
4763
import java.nio.file.attribute.PosixFilePermission;
4864
import java.util.*;
4965
import java.util.stream.Collectors;
5066

51-
import org.apache.maven.artifact.Artifact;
52-
import org.apache.maven.plugin.AbstractMojo;
53-
import org.apache.maven.plugin.MojoExecutionException;
54-
import org.apache.maven.plugin.logging.Log;
55-
import org.apache.maven.plugins.annotations.LifecyclePhase;
56-
import org.apache.maven.plugins.annotations.Mojo;
57-
import org.apache.maven.plugins.annotations.Parameter;
58-
import org.apache.maven.plugins.annotations.ResolutionScope;
59-
import org.apache.maven.project.MavenProject;
60-
import org.graalvm.python.embedding.utils.VFSUtils;
61-
import org.graalvm.python.embedding.utils.GraalPyRunner;
62-
6367
@Mojo(name = "process-graalpy-resources", defaultPhase = LifecyclePhase.PROCESS_RESOURCES,
6468
requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME,
6569
requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
6670
public class ManageResourcesMojo extends AbstractMojo {
6771

68-
private static final String PYTHON_LANGUAGE = "python-language";
72+
private static final String PYTHON_LANGUAGE_ARTIFACT_ID = "python-language";
6973
private static final String PYTHON_RESOURCES = "python-resources";
70-
private static final String PYTHON_LAUNCHER = "python-launcher";
71-
private static final String GRAALPY_GROUP = "org.graalvm.python";
74+
private static final String PYTHON_LAUNCHER_ARTIFACT_ID = "python-launcher";
75+
private static final String GRAALPY_GROUP_ID = "org.graalvm.python";
76+
77+
private static final String POLYGLOT_GROUP_ID = "org.graalvm.polyglot";
78+
private static final String PYTHON_COMMUNITY_ARTIFACT_ID = "python-community";
79+
private static final String PYTHON_ARTIFACT_ID = "python";
80+
private static final String GRAALPY_MAVEN_PLUGIN_ARTIFACT_ID = "graalpy-maven-plugin";
7281

7382
private static final String GRAALPY_MAIN_CLASS = "com.oracle.graal.python.shell.GraalPythonMain";
7483

@@ -88,6 +97,14 @@ public class ManageResourcesMojo extends AbstractMojo {
8897
@Parameter
8998
PythonHome pythonHome;
9099

100+
@Parameter(defaultValue = "${session}", readonly = true, required = true)
101+
private MavenSession session;
102+
103+
@Component
104+
private ProjectBuilder projectBuilder;
105+
106+
private Set<String> launcherClassPath;
107+
91108
static Path getHomeDirectory(MavenProject project) {
92109
return Path.of(project.getBuild().getOutputDirectory(), "vfs", "home");
93110
}
@@ -136,7 +153,7 @@ private void manageHome() throws MojoExecutionException {
136153
try {
137154
if (!Files.exists(homeDirectory)) {
138155
Files.createDirectories(homeDirectory.getParent());
139-
VFSUtils.copyGraalPyHome(calculateClasspath(project), homeDirectory, pythonHomeIncludes, pythonHomeExcludes, new MavenDelegateLog(getLog()));
156+
VFSUtils.copyGraalPyHome(calculateLauncherClasspath(project), homeDirectory, pythonHomeIncludes, pythonHomeExcludes, new MavenDelegateLog(getLog()));
140157
}
141158
Files.write(tag, List.of(graalPyVersion), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
142159
write(tag, pythonHomeIncludes, INCLUDE_PREFIX);
@@ -279,7 +296,7 @@ private void generateLaunchers() throws MojoExecutionException {
279296
var launcher = getLauncherPath();
280297
if (!Files.exists(launcher)) {
281298
var java = Paths.get(System.getProperty("java.home"), "bin", "java");
282-
var classpath = calculateClasspath(project);
299+
var classpath = calculateLauncherClasspath(project);
283300
if (!IS_WINDOWS) {
284301
var script = String.format("""
285302
#!/usr/bin/env bash
@@ -357,8 +374,8 @@ private void runVenvBin(Path venvDirectory, String bin, String... args) throws M
357374
}
358375
}
359376

360-
private static void runGraalPy(MavenProject project, Log log, String... args) throws MojoExecutionException {
361-
var classpath = calculateClasspath(project);
377+
private void runGraalPy(MavenProject project, Log log, String... args) throws MojoExecutionException {
378+
var classpath = calculateLauncherClasspath(project);
362379
try {
363380
GraalPyRunner.run(classpath, new MavenDelegateLog(log), args);
364381
} catch (IOException | InterruptedException e) {
@@ -367,17 +384,21 @@ private static void runGraalPy(MavenProject project, Log log, String... args) th
367384
}
368385

369386
private static String getGraalPyVersion(MavenProject project) throws MojoExecutionException {
370-
return getGraalPyArtifact(project, PYTHON_LANGUAGE).getVersion();
387+
return getGraalPyArtifact(project).getVersion();
371388
}
372389

373-
private static Artifact getGraalPyArtifact(MavenProject project, String aid) throws MojoExecutionException {
390+
private static Artifact getGraalPyArtifact(MavenProject project) throws MojoExecutionException {
374391
var projectArtifacts = resolveProjectDependencies(project);
375-
for (var a : projectArtifacts) {
376-
if (a.getGroupId().equals(GRAALPY_GROUP) && a.getArtifactId().equals(aid)) {
377-
return a;
378-
}
379-
}
380-
throw new MojoExecutionException(String.format("Missing GraalPy dependency %s:%s. Please add it to your pom", GRAALPY_GROUP, aid));
392+
Artifact graalPyArtifact = projectArtifacts.stream().
393+
filter(a -> isPythonArtifact(a))
394+
.findFirst()
395+
.orElse(null);
396+
return Optional.ofNullable(graalPyArtifact).orElseThrow(() -> new MojoExecutionException("Missing GraalPy dependency. Please add to your pom either %s:%s or %s:%s".formatted(POLYGLOT_GROUP_ID, PYTHON_COMMUNITY_ARTIFACT_ID, POLYGLOT_GROUP_ID, PYTHON_ARTIFACT_ID)));
397+
}
398+
399+
private static boolean isPythonArtifact(Artifact a) {
400+
return POLYGLOT_GROUP_ID.equals(a.getGroupId()) &&
401+
(PYTHON_COMMUNITY_ARTIFACT_ID.equals(a.getArtifactId()) || PYTHON_ARTIFACT_ID.equals(a.getArtifactId()));
381402
}
382403

383404
private static Collection<Artifact> resolveProjectDependencies(MavenProject project) {
@@ -387,11 +408,62 @@ private static Collection<Artifact> resolveProjectDependencies(MavenProject proj
387408
.collect(Collectors.toList());
388409
}
389410

390-
private static HashSet<String> calculateClasspath(MavenProject project) throws MojoExecutionException {
391-
var classpath = new HashSet<String>();
392-
for (var r : resolveProjectDependencies(project)) {
393-
classpath.add(r.getFile().getAbsolutePath());
411+
private Set<String> calculateLauncherClasspath(MavenProject project) throws MojoExecutionException {
412+
if(launcherClassPath == null) {
413+
String version = getGraalPyVersion(project);
414+
launcherClassPath = new HashSet<String>();
415+
416+
// 1.) python-launcher and transitive dependencies
417+
// get the artifact from its direct dependency in graalpy-maven-plugin
418+
DefaultArtifact mvnPlugin = new DefaultArtifact(GRAALPY_GROUP_ID, GRAALPY_MAVEN_PLUGIN_ARTIFACT_ID, version, "compile", "jar", null, new DefaultArtifactHandler("pom"));
419+
ProjectBuildingResult result = buildProjectFromArtifact(mvnPlugin);
420+
Artifact graalPyLauncherArtifact = result.getProject().getArtifacts().stream().filter(a ->GRAALPY_GROUP_ID.equals(a.getGroupId()) && PYTHON_LAUNCHER_ARTIFACT_ID.equals(a.getArtifactId()) && version.equals(a.getVersion()))
421+
.findFirst()
422+
.orElse(null);
423+
// python-launcher artifact
424+
launcherClassPath.add(graalPyLauncherArtifact.getFile().getAbsolutePath());
425+
// and transitively all its dependencies
426+
launcherClassPath.addAll(resolveDependencies(graalPyLauncherArtifact));
427+
428+
// 2.) graalpy dependencies
429+
Artifact graalPyArtifact = getGraalPyArtifact(project);
430+
assert graalPyArtifact != null;
431+
launcherClassPath.addAll(resolveDependencies(graalPyArtifact));
432+
}
433+
return launcherClassPath;
434+
}
435+
436+
private Set<String> resolveDependencies(Artifact artifact) throws MojoExecutionException {
437+
Set<String> dependencies = new HashSet<>();
438+
ProjectBuildingResult result = buildProjectFromArtifact(artifact);
439+
for(Dependency d : result.getDependencyResolutionResult().getResolvedDependencies()) {
440+
addDependency(d, dependencies);
441+
}
442+
return dependencies;
443+
}
444+
445+
private ProjectBuildingResult buildProjectFromArtifact(Artifact artifact) throws MojoExecutionException{
446+
try{
447+
ProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest(session.getProjectBuildingRequest());
448+
buildingRequest.setProject(null);
449+
buildingRequest.setResolveDependencies(true);
450+
buildingRequest.setPluginArtifactRepositories(project.getPluginArtifactRepositories());
451+
buildingRequest.setRemoteRepositories(project.getRemoteArtifactRepositories());
452+
453+
return projectBuilder.build(artifact, buildingRequest);
454+
} catch (ProjectBuildingException e) {
455+
throw new MojoExecutionException("Error while building project", e);
456+
}
457+
}
458+
459+
private void addDependency(Dependency d, Set<String> dependencies) {
460+
File f = d.getArtifact().getFile();
461+
if(f != null) {
462+
dependencies.add(f.getAbsolutePath());
463+
} else {
464+
getLog().warn("could not retrieve local file for artifact " + d.getArtifact());
394465
}
395-
return classpath;
396466
}
397467
}
468+
469+

mx.graalpython/suite.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1453,7 +1453,10 @@
14531453
"class": "MavenProject",
14541454
"subDir": "graalpython",
14551455
"noMavenJavadoc": True,
1456-
"dependencies": ["GRAALPYTHON_EMBEDDING"],
1456+
"dependencies": [
1457+
"GRAALPYTHON_EMBEDDING",
1458+
"GRAALPYTHON-LAUNCHER"
1459+
],
14571460
"maven": {
14581461
"tag": ["default", "public"],
14591462
},

0 commit comments

Comments
 (0)