Skip to content

Commit aca28ad

Browse files
committed
regenerate venv if launcher symlinks are broken
1 parent 66530d3 commit aca28ad

File tree

2 files changed

+68
-6
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

+68
-6
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: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import java.io.IOException;
4949
import java.nio.file.FileVisitResult;
5050
import java.nio.file.Files;
51+
import java.nio.file.LinkOption;
5152
import java.nio.file.Path;
5253
import java.nio.file.Paths;
5354
import java.nio.file.SimpleFileVisitor;
@@ -399,6 +400,8 @@ public static void createVenv(Path venvDirectory, List<String> packages, Path la
399400
trim(packages);
400401
}
401402

403+
checkLauncher(venvDirectory, launcherPath, log);
404+
402405
var tag = venvDirectory.resolve("contents");
403406
List<String> installedPackages = new ArrayList<>();
404407

@@ -417,11 +420,10 @@ public static void createVenv(Path venvDirectory, List<String> packages, Path la
417420
installedPackages.add(lines.get(i));
418421
}
419422
}
420-
} else {
421-
log.info(String.format("Creating GraalPy %s venv", graalPyVersion));
422423
}
423424

424425
if (!Files.exists(venvDirectory)) {
426+
log.info(String.format("Creating GraalPy %s venv", graalPyVersion));
425427
runLauncher(laucherPath.toString(), subprocessLog, "-m", "venv", venvDirectory.toString(), "--without-pip");
426428
runVenvBin(venvDirectory, "graalpy", subprocessLog, "-I", "-m", "ensurepip");
427429
}
@@ -439,6 +441,38 @@ public static void createVenv(Path venvDirectory, List<String> packages, Path la
439441
}
440442
}
441443

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

0 commit comments

Comments
 (0)