Skip to content

Commit fcaf103

Browse files
committed
graalpy launcher should on classpath contain only artifacts necessary for the launcher
1 parent ecb56b5 commit fcaf103

File tree

3 files changed

+115
-34
lines changed

3 files changed

+115
-34
lines changed

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)