Skip to content

Commit b9c31f5

Browse files
committed
[GR-58131] Generated launcher in embedding apps points to absolute path where originally created.
PullRequest: graalpython/3522
2 parents d1e21d1 + 72a531c commit b9c31f5

File tree

2 files changed

+76
-9
lines changed
  • graalpython
    • com.oracle.graal.python.test/src/tests/standalone
    • org.graalvm.python.embedding.tools/src/org/graalvm/python/embedding/tools/vfs

2 files changed

+76
-9
lines changed

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

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
# SOFTWARE.
3939

4040
import os
41-
import subprocess
4241
import tempfile
4342
import unittest
4443
import urllib.parse
@@ -217,7 +216,7 @@ def generate_app(self, target_dir):
217216

218217
@unittest.skipUnless(is_gradle_enabled, "ENABLE_GRADLE_STANDALONE_UNITTESTS is not true")
219218
def test_gradle_generated_app(self):
220-
with tempfile.TemporaryDirectory() as tmpdir:
219+
with tempfile.TemporaryDirectory() as tmpdir:
221220
target_dir = os.path.join(str(tmpdir), "generated_app_gradle" + self.target_dir_name_sufix())
222221
self.generate_app(target_dir)
223222
build_file = os.path.join(target_dir, self.build_file_name)
@@ -231,7 +230,7 @@ def test_gradle_generated_app(self):
231230
util.check_ouput("BUILD SUCCESS", out)
232231

233232
cmd = gradlew_cmd + ["nativeCompile"]
234-
# gradle needs jdk <= 22, but it looks like the 'gradle nativeCompile' cmd does not complain if higher,
233+
# gradle needs jdk <= 22, but it looks like the 'gradle nativeCompile' cmd does not complain if higher,
235234
# which is fine, because we need to build the native binary with a graalvm build
236235
# and the one we have set in JAVA_HOME is at least jdk24
237236
# => run without gradle = True
@@ -267,6 +266,22 @@ def test_gradle_generated_app(self):
267266
#GR-51132 - NoClassDefFoundError when running polyglot app in java mode
268267
util.check_ouput("java.lang.NoClassDefFoundError", out, False)
269268

269+
# move app to another folder
270+
# this will break launcher symlinks, but should be able to recover from that
271+
target_dir2 = os.path.join(str(tmpdir), "generated_app_gradle.2" + self.target_dir_name_sufix())
272+
os.rename(target_dir, target_dir2)
273+
# adding new dep triggers launcher without venv regen
274+
self.copy_build_files(target_dir2)
275+
build_file2 = os.path.join(target_dir2, self.build_file_name)
276+
append(build_file2, self.packages_termcolor_ujson())
277+
278+
gradlew_cmd2 = util.get_gradle_wrapper(target_dir2, self.env)
279+
cmd = gradlew_cmd2 + ["build", "run"]
280+
out, return_code = util.run_cmd(cmd, self.env, cwd=target_dir2, gradle = True)
281+
util.check_ouput("BUILD SUCCESS", out)
282+
util.check_ouput("Deleting GraalPy venv due to broken launcher symlinks", out)
283+
util.check_ouput("hello java", out)
284+
270285
@unittest.skipUnless(is_gradle_enabled, "ENABLE_GRADLE_STANDALONE_UNITTESTS is not true")
271286
def test_gradle_generated_app_external_resources(self):
272287
with tempfile.TemporaryDirectory() as tmpdir:
@@ -300,7 +315,7 @@ def test_gradle_generated_app_external_resources(self):
300315
out, return_code = util.run_cmd(cmd, self.env, cwd=target_dir, gradle = True)
301316
util.check_ouput("BUILD SUCCESS", out)
302317

303-
# gradle needs jdk <= 22, but it looks like the 'gradle nativeCompile' cmd does not complain if higher,
318+
# gradle needs jdk <= 22, but it looks like the 'gradle nativeCompile' cmd does not complain if higher,
304319
# which is fine, because we need to build the native binary with a graalvm build
305320
# and the one we have set in JAVA_HOME is at least jdk24
306321
# => run without gradle = True
@@ -704,6 +719,19 @@ def test_generated_app(self):
704719
#GR-51132 - NoClassDefFoundError when running polyglot app in java mode
705720
util.check_ouput("java.lang.NoClassDefFoundError", out, False)
706721

722+
# move app to another folder
723+
# this will break launcher symlinks, but should be able to recover from that
724+
target_dir2 = os.path.join(str(tmpdir), target_name + ".2")
725+
os.rename(target_dir, target_dir2)
726+
mvnw_cmd2 = util.get_mvn_wrapper(target_dir2, self.env)
727+
# adding new dep triggers launcher without venv regen
728+
util.replace_in_file(os.path.join(target_dir2, "pom.xml"), "<packages>", "<packages>\n<package>ujson</package>")
729+
cmd = mvnw_cmd2 + ["package", "exec:java", "-Dexec.mainClass=it.pkg.GraalPy"]
730+
out, return_code = util.run_cmd(cmd, self.env, cwd=target_dir2)
731+
util.check_ouput("BUILD SUCCESS", out)
732+
util.check_ouput("Deleting GraalPy venv due to changed launcher path", out)
733+
util.check_ouput("hello java", out)
734+
707735
@unittest.skipUnless(is_enabled, "ENABLE_STANDALONE_UNITTESTS is not true")
708736
def test_generated_app_external_resources(self):
709737
with tempfile.TemporaryDirectory() as tmpdir:

graalpython/org.graalvm.python.embedding.tools/src/org/graalvm/python/embedding/tools/vfs/VFSUtils.java

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -391,14 +391,22 @@ public void log(CharSequence var1) {
391391
}
392392
}
393393

394-
public static void createVenv(Path venvDirectory, List<String> packages, Path laucherPath, LauncherClassPath launcherClassPath, String graalPyVersion, SubprocessLog subprocessLog, Log log)
394+
public static void createVenv(Path venvDirectory, List<String> packages, Path launcher, LauncherClassPath launcherClassPath, String graalPyVersion, SubprocessLog subprocessLog, Log log)
395395
throws IOException {
396-
generateLaunchers(laucherPath, launcherClassPath, subprocessLog, log);
396+
Path launcherPath = launcher;
397+
String externalLauncher = System.getProperty("graalpy.vfs.venvLauncher");
398+
if (externalLauncher == null || externalLauncher.trim().isEmpty()) {
399+
generateLaunchers(launcherPath, launcherClassPath, subprocessLog, log);
400+
} else {
401+
launcherPath = Path.of(externalLauncher);
402+
}
397403

398404
if (packages != null) {
399405
trim(packages);
400406
}
401407

408+
checkLauncher(venvDirectory, launcherPath, log);
409+
402410
var tag = venvDirectory.resolve("contents");
403411
List<String> installedPackages = new ArrayList<>();
404412

@@ -417,12 +425,11 @@ public static void createVenv(Path venvDirectory, List<String> packages, Path la
417425
installedPackages.add(lines.get(i));
418426
}
419427
}
420-
} else {
421-
log.info(String.format("Creating GraalPy %s venv", graalPyVersion));
422428
}
423429

424430
if (!Files.exists(venvDirectory)) {
425-
runLauncher(laucherPath.toString(), subprocessLog, "-m", "venv", venvDirectory.toString(), "--without-pip");
431+
log.info(String.format("Creating GraalPy %s venv", graalPyVersion));
432+
runLauncher(launcherPath.toString(), subprocessLog, "-m", "venv", venvDirectory.toString(), "--without-pip");
426433
runVenvBin(venvDirectory, "graalpy", subprocessLog, "-I", "-m", "ensurepip");
427434
}
428435

@@ -439,6 +446,38 @@ public static void createVenv(Path venvDirectory, List<String> packages, Path la
439446
}
440447
}
441448

449+
private static void checkLauncher(Path venvDirectory, Path launcherPath, Log log) throws IOException {
450+
if (!Files.exists(launcherPath)) {
451+
throw new IOException(String.format("Launcher file does not exist '%s'", launcherPath));
452+
}
453+
Path cfg = venvDirectory.resolve("pyvenv.cfg");
454+
if (Files.exists(cfg)) {
455+
try {
456+
List<String> lines = Files.readAllLines(cfg);
457+
for (String line : lines) {
458+
int idx = line.indexOf("=");
459+
if (idx > -1) {
460+
String l = line.substring(0, idx).trim();
461+
String r = line.substring(idx + 1).trim();
462+
if (l.trim().equals("executable")) {
463+
Path cfgLauncherPath = Path.of(r);
464+
if (!Files.exists(cfgLauncherPath) || !Files.isSameFile(launcherPath, cfgLauncherPath)) {
465+
log.info(String.format("Deleting GraalPy venv due to changed launcher path"));
466+
delete(venvDirectory);
467+
}
468+
break;
469+
}
470+
}
471+
}
472+
} catch (IOException e) {
473+
e.printStackTrace();
474+
throw new IOException(String.format("failed to read config file %s", cfg), e);
475+
}
476+
} else {
477+
log.info(String.format("missing venv config file: '%s'", cfg));
478+
}
479+
}
480+
442481
private static void generateLaunchers(Path laucherPath, LauncherClassPath launcherClassPath, SubprocessLog subprocessLog, Log log) throws IOException {
443482
if (!Files.exists(laucherPath)) {
444483
log.info("Generating GraalPy launchers");

0 commit comments

Comments
 (0)