From e97a07f870e9d7002073194355338c51b11accec Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Tue, 17 Jan 2023 13:55:52 +0100 Subject: [PATCH] wip --- src/main/java/dev/jbang/cli/App.java | 2 +- src/main/java/dev/jbang/cli/Info.java | 2 +- src/main/java/dev/jbang/cli/Jdk.java | 10 +-- .../jbang/dependencies/ModularClassPath.java | 2 +- src/main/java/dev/jbang/net/JdkManager.java | 74 +++++++++++++------ src/main/java/dev/jbang/net/JdkProvider.java | 5 ++ .../net/jdkproviders/JBangJdkProvider.java | 2 +- .../java/dev/jbang/source/AppBuilder.java | 2 +- .../jbang/source/buildsteps/JarBuildStep.java | 7 +- .../source/generators/JarCmdGenerator.java | 2 +- .../jbang/source/sources/GroovySource.java | 8 +- src/main/java/dev/jbang/util/JavaUtil.java | 11 ++- src/test/java/dev/jbang/cli/TestJdk.java | 15 +++- src/test/java/dev/jbang/cli/TestRun.java | 2 +- 14 files changed, 96 insertions(+), 48 deletions(-) diff --git a/src/main/java/dev/jbang/cli/App.java b/src/main/java/dev/jbang/cli/App.java index eae0cc1f9..1e5b6a71f 100644 --- a/src/main/java/dev/jbang/cli/App.java +++ b/src/main/java/dev/jbang/cli/App.java @@ -363,7 +363,7 @@ public static boolean needsSetup() { */ public static boolean guessWithJava() { boolean withJava; - JdkProvider.Jdk defJdk = JdkManager.getJdk(null); + JdkProvider.Jdk defJdk = JdkManager.getJdk(null, "javac"); String javaHome = System.getenv("JAVA_HOME"); Path javacCmd = Util.searchPath("javac"); withJava = defJdk != null diff --git a/src/main/java/dev/jbang/cli/Info.java b/src/main/java/dev/jbang/cli/Info.java index 0a3b4ddc6..5059f4e11 100644 --- a/src/main/java/dev/jbang/cli/Info.java +++ b/src/main/java/dev/jbang/cli/Info.java @@ -104,7 +104,7 @@ public ScriptInfo(Project prj, ProjectBuilder pb) { requestedJavaVersion = prj.getJavaVersion(); try { - JdkProvider.Jdk jdk = JdkManager.getJdk(requestedJavaVersion); + JdkProvider.Jdk jdk = JdkManager.getJdk(requestedJavaVersion, null); if (jdk != null && jdk.isInstalled()) { availableJdkPath = jdk.getHome().toString(); } diff --git a/src/main/java/dev/jbang/cli/Jdk.java b/src/main/java/dev/jbang/cli/Jdk.java index 8a5139f3e..a8786f1a1 100644 --- a/src/main/java/dev/jbang/cli/Jdk.java +++ b/src/main/java/dev/jbang/cli/Jdk.java @@ -40,12 +40,12 @@ public Integer install( @CommandLine.Parameters(paramLabel = "existingJdkPath", index = "1", description = "Pre installed JDK path", arity = "0..1") String path) throws IOException { jdkProvidersMixin.initJdkProviders(); - JdkProvider.Jdk jdk = !force ? JdkManager.getInstalledJdk(versionOrId, false) : null; + JdkProvider.Jdk jdk = !force ? JdkManager.getInstalledJdk(versionOrId, "javac", false) : null; if (force || jdk == null) { if (!Util.isNullOrBlankString(path)) { JdkManager.linkToExistingJdk(path, Integer.parseInt(versionOrId)); } else { - JdkManager.getOrInstallJdk(versionOrId); + JdkManager.getOrInstallJdk(versionOrId, null); } } else { Util.infoMsg("JDK is already installed: " + jdk); @@ -154,7 +154,7 @@ public int compareTo(JdkOut o) { public Integer uninstall( @CommandLine.Parameters(paramLabel = "version", index = "0", description = "The version to install", arity = "1") String versionOrId) { jdkProvidersMixin.initJdkProviders(); - JdkProvider.Jdk jdk = JdkManager.getInstalledJdk(versionOrId, true); + JdkProvider.Jdk jdk = JdkManager.getInstalledJdk(versionOrId, null, true); if (jdk == null) { throw new ExitException(EXIT_INVALID_INPUT, "JDK " + versionOrId + " is not installed"); } @@ -219,7 +219,7 @@ public Integer javaEnv( } private Path getJdkPath(String versionOrId) { - JdkProvider.Jdk jdk = JdkManager.getOrInstallJdk(versionOrId); + JdkProvider.Jdk jdk = JdkManager.getOrInstallJdk(versionOrId, null); return jdk.getHome(); } @@ -229,7 +229,7 @@ public Integer defaultJdk( jdkProvidersMixin.initJdkProviders(); JdkProvider.Jdk defjdk = JdkManager.getDefaultJdk(); if (versionOrId != null) { - JdkProvider.Jdk jdk = JdkManager.getOrInstallJdk(versionOrId); + JdkProvider.Jdk jdk = JdkManager.getOrInstallJdk(versionOrId, "javac"); if (!jdk.equals(defjdk)) { JdkManager.setDefaultJdk(jdk); } else { diff --git a/src/main/java/dev/jbang/dependencies/ModularClassPath.java b/src/main/java/dev/jbang/dependencies/ModularClassPath.java index 32d6fecc0..8e5b77698 100644 --- a/src/main/java/dev/jbang/dependencies/ModularClassPath.java +++ b/src/main/java/dev/jbang/dependencies/ModularClassPath.java @@ -142,7 +142,7 @@ public List getAutoDectectedModuleArguments(String requestedVersion) { } protected boolean supportsModules(String requestedVersion) { - return JavaUtil.javaVersion(requestedVersion) >= 9; + return JavaUtil.javaVersion(requestedVersion, "javac") >= 9; } public List getArtifacts() { diff --git a/src/main/java/dev/jbang/net/JdkManager.java b/src/main/java/dev/jbang/net/JdkManager.java index e00510c1c..8638ccf9a 100644 --- a/src/main/java/dev/jbang/net/JdkManager.java +++ b/src/main/java/dev/jbang/net/JdkManager.java @@ -115,21 +115,23 @@ private static List updatableProviders() { * necessary. * * @param versionOrId A version pattern, id or null + * @param command The name of a command that must be available in the JDK to + * be considered as a valid result. Can be null * @return A Jdk object or null * @throws ExitException If no JDK could be found at all or if one failed to * install */ @Nonnull - public static JdkProvider.Jdk getOrInstallJdk(String versionOrId) { + public static JdkProvider.Jdk getOrInstallJdk(@Nullable String versionOrId, @Nullable String command) { if (versionOrId != null) { if (JavaUtil.isRequestedVersion(versionOrId)) { return getOrInstallJdkByVersion(JavaUtil.minRequestedVersion(versionOrId), - JavaUtil.isOpenVersion(versionOrId), false); + JavaUtil.isOpenVersion(versionOrId), command, false); } else { - return getOrInstallJdkById(versionOrId); + return getOrInstallJdkById(versionOrId, command); } } else { - return getOrInstallJdkByVersion(0, true, false); + return getOrInstallJdkByVersion(0, true, command, false); } } @@ -140,6 +142,9 @@ public static JdkProvider.Jdk getOrInstallJdk(String versionOrId) { * * @param requestedVersion The (minimal) version to return, can be 0 * @param openVersion Return newer version if exact is not available + * @param command The name of a command that must be available in the + * JDK to be considered as a valid result. Can be + * null * @param updatableOnly Only return JDKs from updatable providers or not * @return A Jdk object or null * @throws ExitException If no JDK could be found at all or if one failed to @@ -147,8 +152,8 @@ public static JdkProvider.Jdk getOrInstallJdk(String versionOrId) { */ @Nonnull private static JdkProvider.Jdk getOrInstallJdkByVersion(int requestedVersion, boolean openVersion, - boolean updatableOnly) { - JdkProvider.Jdk jdk = getJdkByVersion(requestedVersion, openVersion, updatableOnly); + @Nullable String command, boolean updatableOnly) { + JdkProvider.Jdk jdk = getJdkByVersion(requestedVersion, openVersion, command, updatableOnly); if (jdk == null) { if (requestedVersion > 0) { throw new ExitException(EXIT_UNEXPECTED_STATE, @@ -170,13 +175,15 @@ private static JdkProvider.Jdk getOrInstallJdkByVersion(int requestedVersion, bo * if necessary. * * @param requestedId The id of the JDK to return + * @param command The name of a command that must be available in the JDK to + * be considered as a valid result. Can be null * @return A Jdk object or null * @throws ExitException If no JDK could be found at all or if one failed to * install */ @Nonnull - private static JdkProvider.Jdk getOrInstallJdkById(@Nonnull String requestedId) { - JdkProvider.Jdk jdk = getJdkById(requestedId); + private static JdkProvider.Jdk getOrInstallJdkById(@Nonnull String requestedId, @Nullable String command) { + JdkProvider.Jdk jdk = getJdkById(requestedId, command); if (jdk == null) { throw new ExitException(EXIT_UNEXPECTED_STATE, "No suitable JDK was found for requested id: " + requestedId); @@ -213,20 +220,22 @@ private static JdkProvider.Jdk ensureInstalled(JdkProvider.Jdk jdk) { * performed. See getOrInstallJdk() for that. * * @param versionOrId A version pattern, id or null + * @param command The name of a command that must be available in the JDK to + * be considered as a valid result. Can be null * @return A Jdk object or null * @throws ExitException If no JDK could be found at all */ @Nullable - public static JdkProvider.Jdk getJdk(@Nullable String versionOrId) { + public static JdkProvider.Jdk getJdk(@Nullable String versionOrId, @Nullable String command) { if (versionOrId != null) { if (JavaUtil.isRequestedVersion(versionOrId)) { return getJdkByVersion(JavaUtil.minRequestedVersion(versionOrId), JavaUtil.isOpenVersion(versionOrId), - false); + command, false); } else { - return getJdkById(versionOrId); + return getJdkById(versionOrId, command); } } else { - return getJdkByVersion(0, true, false); + return getJdkByVersion(0, true, command, false); } } @@ -240,18 +249,22 @@ public static JdkProvider.Jdk getJdk(@Nullable String versionOrId) { * * @param requestedVersion The (minimal) version to return, can be 0 * @param openVersion Return newer version if exact is not available + * @param command The name of a command that must be available in the + * JDK to be considered as a valid result. Can be + * null * @param updatableOnly Only return JDKs from updatable providers or not * @return A Jdk object or null * @throws ExitException If no JDK could be found at all */ @Nullable - private static JdkProvider.Jdk getJdkByVersion(int requestedVersion, boolean openVersion, boolean updatableOnly) { - JdkProvider.Jdk jdk = getInstalledJdkByVersion(requestedVersion, openVersion, updatableOnly); + private static JdkProvider.Jdk getJdkByVersion(int requestedVersion, boolean openVersion, @Nullable String command, + boolean updatableOnly) { + JdkProvider.Jdk jdk = getInstalledJdkByVersion(requestedVersion, openVersion, command, updatableOnly); if (jdk == null) { if (requestedVersion > 0) { jdk = getAvailableJdkByVersion(requestedVersion); } else { - jdk = getJdkByVersion(Settings.getDefaultJavaVersion(), true, updatableOnly); + jdk = getJdkByVersion(Settings.getDefaultJavaVersion(), true, command, updatableOnly); } } return jdk; @@ -266,12 +279,14 @@ private static JdkProvider.Jdk getJdkByVersion(int requestedVersion, boolean ope * to be performed. See getOrInstallJdkByVersion() for that. * * @param requestedId The id of the JDK to return + * @param command The name of a command that must be available in the JDK to + * be considered as a valid result. Can be null * @return A Jdk object or null * @throws ExitException If no JDK could be found at all */ @Nullable - private static JdkProvider.Jdk getJdkById(@Nonnull String requestedId) { - JdkProvider.Jdk jdk = getInstalledJdkById(requestedId, false); + private static JdkProvider.Jdk getJdkById(@Nonnull String requestedId, @Nullable String command) { + JdkProvider.Jdk jdk = getInstalledJdkById(requestedId, command, false); if (jdk == null) { jdk = getAvailableJdkById(requestedId); } @@ -284,20 +299,23 @@ private static JdkProvider.Jdk getJdkById(@Nonnull String requestedId) { * currently installed. * * @param versionOrId A version pattern, id or null + * @param command The name of a command that must be available in the JDK + * to be considered as a valid result. Can be + * null * @param updatableOnly Only return JDKs from updatable providers or not * @return A Jdk object or null */ @Nullable - public static JdkProvider.Jdk getInstalledJdk(String versionOrId, boolean updatableOnly) { + public static JdkProvider.Jdk getInstalledJdk(String versionOrId, @Nullable String command, boolean updatableOnly) { if (versionOrId != null) { if (JavaUtil.isRequestedVersion(versionOrId)) { return getInstalledJdkByVersion(JavaUtil.minRequestedVersion(versionOrId), - JavaUtil.isOpenVersion(versionOrId), updatableOnly); + JavaUtil.isOpenVersion(versionOrId), command, updatableOnly); } else { - return getInstalledJdkById(versionOrId, updatableOnly); + return getInstalledJdkById(versionOrId, command, updatableOnly); } } else { - return getInstalledJdkByVersion(0, true, updatableOnly); + return getInstalledJdkByVersion(0, true, command, updatableOnly); } } @@ -308,15 +326,20 @@ public static JdkProvider.Jdk getInstalledJdk(String versionOrId, boolean updata * * @param version The (major) version of the JDK to return * @param openVersion Return newer version if exact is not available + * @param command The name of a command that must be available in the JDK + * to be considered as a valid result. Can be + * null * @param updatableOnly Only return JDKs from updatable providers or not * @return A Jdk object or null */ @Nullable - private static JdkProvider.Jdk getInstalledJdkByVersion(int version, boolean openVersion, boolean updatableOnly) { + private static JdkProvider.Jdk getInstalledJdkByVersion(int version, boolean openVersion, @Nullable String command, + boolean updatableOnly) { return providers() .stream() .filter(p -> !updatableOnly || p.canUpdate()) .map(p -> p.getJdkByVersion(version, openVersion)) .filter(Objects::nonNull) + .filter(jdk -> command == null || jdk.hasCommand(command)) .findFirst() .orElse(null); } @@ -326,15 +349,20 @@ private static JdkProvider.Jdk getInstalledJdkByVersion(int version, boolean ope * Will return null if no JDK with that id is currently installed. * * @param requestedId The id of the JDK to return + * @param command The name of a command that must be available in the JDK + * to be considered as a valid result. Can be + * null * @param updatableOnly Only return JDKs from updatable providers or not * @return A Jdk object or null */ @Nullable - private static JdkProvider.Jdk getInstalledJdkById(String requestedId, boolean updatableOnly) { + private static JdkProvider.Jdk getInstalledJdkById(String requestedId, @Nullable String command, + boolean updatableOnly) { return providers() .stream() .filter(p -> !updatableOnly || p.canUpdate()) .map(p -> p.getJdkById(requestedId)) .filter(Objects::nonNull) + .filter(jdk -> command == null || jdk.hasCommand(command)) .findFirst() .orElse(null); } diff --git a/src/main/java/dev/jbang/net/JdkProvider.java b/src/main/java/dev/jbang/net/JdkProvider.java index 8b559e27f..b5d7caaf7 100644 --- a/src/main/java/dev/jbang/net/JdkProvider.java +++ b/src/main/java/dev/jbang/net/JdkProvider.java @@ -12,6 +12,7 @@ import javax.annotation.Nullable; import dev.jbang.util.JavaUtil; +import dev.jbang.util.Util; /** * This interface must be implemented by providers that are able to give access @@ -89,6 +90,10 @@ public boolean isInstalled() { return home != null; } + public boolean hasCommand(String command) { + return home != null && Util.searchPath(command, home.resolve("bin").toString()) != null; + } + @Override public boolean equals(Object o) { if (this == o) diff --git a/src/main/java/dev/jbang/net/jdkproviders/JBangJdkProvider.java b/src/main/java/dev/jbang/net/jdkproviders/JBangJdkProvider.java index 665947d03..18644f362 100644 --- a/src/main/java/dev/jbang/net/jdkproviders/JBangJdkProvider.java +++ b/src/main/java/dev/jbang/net/jdkproviders/JBangJdkProvider.java @@ -117,7 +117,7 @@ public Jdk install(@Nonnull String jdk) { } } String msg = "Required Java version not possible to download or install."; - Jdk defjdk = JdkManager.getJdk(null); + Jdk defjdk = JdkManager.getJdk(null, null); if (defjdk != null) { msg += " You can run with '--java " + defjdk.getMajorVersion() + "' to force using the default installed Java."; diff --git a/src/main/java/dev/jbang/source/AppBuilder.java b/src/main/java/dev/jbang/source/AppBuilder.java index 14f07e9ca..e4c4620c5 100644 --- a/src/main/java/dev/jbang/source/AppBuilder.java +++ b/src/main/java/dev/jbang/source/AppBuilder.java @@ -58,7 +58,7 @@ public Project build() throws IOException { // We already have a Jar, check if we can still use it if (!project.isUpToDate()) { Util.verboseMsg("Building as previous build jar found but it or its dependencies not up-to-date."); - } else if (JavaUtil.javaVersion(requestedJavaVersion) < JavaUtil.minRequestedVersion( + } else if (JavaUtil.javaVersion(requestedJavaVersion, "java") < JavaUtil.minRequestedVersion( project.getJavaVersion())) { Util.verboseMsg( String.format( diff --git a/src/main/java/dev/jbang/source/buildsteps/JarBuildStep.java b/src/main/java/dev/jbang/source/buildsteps/JarBuildStep.java index 942d4735e..f5bce8567 100644 --- a/src/main/java/dev/jbang/source/buildsteps/JarBuildStep.java +++ b/src/main/java/dev/jbang/source/buildsteps/JarBuildStep.java @@ -53,12 +53,7 @@ public static void createJar(Project prj, Path compileDir, Path jarFile) throws // options set on the Source) List rtArgs = prj.getRuntimeOptions(); String runtimeOpts = CommandBuffer.of(rtArgs).asCommandLine(Util.Shell.bash); - // TODO should be removed - // if (!runtimeOpts.isEmpty()) { - // manifest.getMainAttributes() - // .putValue(ATTR_JBANG_JAVA_OPTIONS, runtimeOpts); - // } - int buildJdk = JavaUtil.javaVersion(prj.getJavaVersion()); + int buildJdk = JavaUtil.javaVersion(prj.getJavaVersion(), "javac"); if (buildJdk > 0) { String val = buildJdk >= 9 ? Integer.toString(buildJdk) : "1." + buildJdk; manifest.getMainAttributes().putValue(ATTR_BUILD_JDK, val); diff --git a/src/main/java/dev/jbang/source/generators/JarCmdGenerator.java b/src/main/java/dev/jbang/source/generators/JarCmdGenerator.java index 734a17147..0ff3c219f 100644 --- a/src/main/java/dev/jbang/source/generators/JarCmdGenerator.java +++ b/src/main/java/dev/jbang/source/generators/JarCmdGenerator.java @@ -142,7 +142,7 @@ protected String generateCommandLineString(List fullArgs) throws IOExcep if (args.length() > COMMAND_LINE_LENGTH_LIMIT && Util.getShell() != Util.Shell.bash) { // @file is only available from java 9 onwards. String requestedJavaVersion = project.getJavaVersion(); - int actualVersion = JavaUtil.javaVersion(requestedJavaVersion); + int actualVersion = JavaUtil.javaVersion(requestedJavaVersion, "java"); useArgsFile = actualVersion >= 9; } if (useArgsFile) { diff --git a/src/main/java/dev/jbang/source/sources/GroovySource.java b/src/main/java/dev/jbang/source/sources/GroovySource.java index 83402a237..faeb6c143 100644 --- a/src/main/java/dev/jbang/source/sources/GroovySource.java +++ b/src/main/java/dev/jbang/source/sources/GroovySource.java @@ -66,6 +66,8 @@ public Builder getBuilder(Project prj) { } private static class GroovyAppBuilder extends AppBuilder { + public static final String GROOVY_COMPILER = "groovyc"; + public GroovyAppBuilder(Project project) { super(project); } @@ -88,7 +90,8 @@ public GroovyCompileBuildStep() { @Override protected String getCompilerBinary(String requestedJavaVersion) { - return resolveInGroovyHome("groovyc", ((GroovySource) project.getMainSource()).getGroovyVersion()); + return resolveInGroovyHome(GROOVY_COMPILER, + ((GroovySource) project.getMainSource()).getGroovyVersion()); } @Override @@ -96,7 +99,8 @@ protected void runCompiler(ProcessBuilder processBuilder) throws IOException { if (project.getMainSource() instanceof GroovySource) { processBuilder .environment() .put("JAVA_HOME", - JdkManager.getOrInstallJdk(project.getJavaVersion()).toString()); + JdkManager .getOrInstallJdk(project.getJavaVersion(), GROOVY_COMPILER) + .toString()); processBuilder.environment().remove("GROOVY_HOME"); } super.runCompiler(processBuilder); diff --git a/src/main/java/dev/jbang/util/JavaUtil.java b/src/main/java/dev/jbang/util/JavaUtil.java index aed997675..4bfd0c606 100644 --- a/src/main/java/dev/jbang/util/JavaUtil.java +++ b/src/main/java/dev/jbang/util/JavaUtil.java @@ -12,6 +12,8 @@ import java.util.regex.Pattern; import java.util.stream.Stream; +import javax.annotation.Nullable; + import dev.jbang.net.JdkManager; import dev.jbang.net.JdkProvider; @@ -26,10 +28,13 @@ public class JavaUtil { * PATH) * * @param requestedVersion The Java version requested by the user + * @param command The name of a command that must be available in the + * JDK to be considered as a valid result. Can be + * null * @return The Java version that will be used */ - public static int javaVersion(String requestedVersion) { - JdkProvider.Jdk jdk = JdkManager.getOrInstallJdk(requestedVersion); + public static int javaVersion(@Nullable String requestedVersion, @Nullable String command) { + JdkProvider.Jdk jdk = JdkManager.getOrInstallJdk(requestedVersion, command); return jdk.getMajorVersion(); } @@ -115,7 +120,7 @@ public static int getCurrentMajorJavaVersion() { } public static String resolveInJavaHome(String cmd, String requestedVersion) { - Path jdkHome = JdkManager.getOrInstallJdk(requestedVersion).getHome(); + Path jdkHome = JdkManager.getOrInstallJdk(requestedVersion, cmd).getHome(); if (jdkHome != null) { if (Util.isWindows()) { cmd = cmd + ".exe"; diff --git a/src/test/java/dev/jbang/cli/TestJdk.java b/src/test/java/dev/jbang/cli/TestJdk.java index b3f1ee6fd..d2bfe06a3 100644 --- a/src/test/java/dev/jbang/cli/TestJdk.java +++ b/src/test/java/dev/jbang/cli/TestJdk.java @@ -365,10 +365,21 @@ private void createMockJdk(int jdkVersion) { } private void initMockJdkDir(Path jdkPath, String version) { - String rawJavaVersion = "JAVA_VERSION=\"" + version + "\""; - Path release = jdkPath.resolve("release"); try { + String rawJavaVersion = "JAVA_VERSION=\"" + version + "\""; + Path release = jdkPath.resolve("release"); Util.writeString(release, rawJavaVersion); + + Path bin = jdkPath.resolve("bin"); + Util.mkdirs(bin); + + Path java = Util.isWindows() ? bin.resolve("java.exe") : bin.resolve("java"); + Util.writeString(java, "fake java command"); + java.toFile().setExecutable(true); + + Path javac = Util.isWindows() ? bin.resolve("javac.exe") : bin.resolve("javac"); + Util.writeString(javac, "fake javac command"); + javac.toFile().setExecutable(true); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/test/java/dev/jbang/cli/TestRun.java b/src/test/java/dev/jbang/cli/TestRun.java index 2e02247a6..c4e1878cb 100644 --- a/src/test/java/dev/jbang/cli/TestRun.java +++ b/src/test/java/dev/jbang/cli/TestRun.java @@ -2129,7 +2129,7 @@ void testReposWorksWithFresh() throws IOException { @Test void testForceJavaVersion() throws IOException { - int v = JdkManager.getJdk(null).getMajorVersion(); + int v = JdkManager.getJdk(null, null).getMajorVersion(); String arg = examplesTestFolder.resolve("java4321.java").toAbsolutePath().toString(); CommandLine.ParseResult pr = JBang.getCommandLine().parseArgs("run", "--java", "" + v, arg); Run run = (Run) pr.subcommand().commandSpec().userObject();