diff --git a/devtools/cli/src/main/java/io/quarkus/cli/CreateApp.java b/devtools/cli/src/main/java/io/quarkus/cli/CreateApp.java index 380839b63f2c9..3213bf11ed144 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/CreateApp.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/CreateApp.java @@ -36,7 +36,7 @@ public class CreateApp extends BaseCreateCommand { @CommandLine.ArgGroup(order = 3, heading = "%nBuild tool (Maven):%n") TargetBuildToolGroup targetBuildTool = new TargetBuildToolGroup(); - @CommandLine.ArgGroup(order = 4, heading = "%nTarget language (Java):%n") + @CommandLine.ArgGroup(order = 4, exclusive = false, heading = "%nTarget language:%n") TargetLanguageGroup targetLanguage = new TargetLanguageGroup(); @CommandLine.ArgGroup(order = 5, exclusive = false, heading = "%nCode Generation:%n") @@ -58,7 +58,8 @@ public Integer call() throws Exception { } BuildTool buildTool = targetBuildTool.getBuildTool(BuildTool.MAVEN); - SourceType sourceType = targetLanguage.getSourceType(buildTool, extensions, output); + SourceType sourceType = targetLanguage.getSourceType(spec, buildTool, extensions, output); + setJavaVersion(targetLanguage.getJavaVersion()); setSourceTypeExtensions(extensions, sourceType); setCodegenOptions(codeGeneration); diff --git a/devtools/cli/src/main/java/io/quarkus/cli/CreateCli.java b/devtools/cli/src/main/java/io/quarkus/cli/CreateCli.java index 1af4b310e6ebf..5321653ab236c 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/CreateCli.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/CreateCli.java @@ -36,7 +36,7 @@ public class CreateCli extends BaseCreateCommand { @CommandLine.ArgGroup(order = 3, heading = "%nBuild tool (Maven):%n") TargetBuildToolGroup targetBuildTool = new TargetBuildToolGroup(); - @CommandLine.ArgGroup(order = 4, heading = "%nTarget language (Java):%n") + @CommandLine.ArgGroup(order = 4, exclusive = false, heading = "%nTarget language:%n") TargetLanguageGroup targetLanguage = new TargetLanguageGroup(); @CommandLine.ArgGroup(order = 5, exclusive = false, heading = "%nCode Generation:%n") @@ -60,7 +60,8 @@ public Integer call() throws Exception { } BuildTool buildTool = targetBuildTool.getBuildTool(BuildTool.MAVEN); - SourceType sourceType = targetLanguage.getSourceType(buildTool, extensions, output); + SourceType sourceType = targetLanguage.getSourceType(spec, buildTool, extensions, output); + setJavaVersion(targetLanguage.getJavaVersion()); setSourceTypeExtensions(extensions, sourceType); setCodegenOptions(codeGeneration); diff --git a/devtools/cli/src/main/java/io/quarkus/cli/create/BaseCreateCommand.java b/devtools/cli/src/main/java/io/quarkus/cli/create/BaseCreateCommand.java index 19aebf70efcb5..70a233cd7d48d 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/create/BaseCreateCommand.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/create/BaseCreateCommand.java @@ -21,9 +21,7 @@ import io.quarkus.devtools.project.codegen.SourceType; import io.quarkus.registry.RegistryResolutionException; import picocli.CommandLine; -import picocli.CommandLine.Mixin; import picocli.CommandLine.Model.CommandSpec; -import picocli.CommandLine.Spec; public class BaseCreateCommand implements Callable { @CommandLine.Mixin @@ -169,6 +167,11 @@ public void setSourceTypeExtensions(Set extensions, SourceType sourceTyp setValue(ProjectGenerator.EXTENSIONS, extensions); } + /** Set Java source level */ + public void setJavaVersion(String javaVersion) { + CreateProjectHelper.setJavaVersion(values, javaVersion); + } + /** * Process code generation options (save values) * @@ -201,8 +204,6 @@ public QuarkusCommandInvocation build(BuildTool buildTool, TargetQuarkusVersionG Map properties) throws RegistryResolutionException { - // TODO: Allow the Java version to be configured? infer from active Java version? - CreateProjectHelper.setJavaVersion(values, null); CreateProjectHelper.handleSpringConfiguration(values); output.debug("Creating an app using the following settings: %s", values); diff --git a/devtools/cli/src/main/java/io/quarkus/cli/create/TargetLanguageGroup.java b/devtools/cli/src/main/java/io/quarkus/cli/create/TargetLanguageGroup.java index e180684211def..ee88757a6f00e 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/create/TargetLanguageGroup.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/create/TargetLanguageGroup.java @@ -1,5 +1,7 @@ package io.quarkus.cli.create; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import io.quarkus.cli.common.OutputOptionMixin; @@ -7,12 +9,21 @@ import io.quarkus.devtools.project.codegen.CreateProjectHelper; import io.quarkus.devtools.project.codegen.SourceType; import picocli.CommandLine; +import picocli.CommandLine.Model.CommandSpec; +import picocli.CommandLine.ParameterException; public class TargetLanguageGroup { SourceType sourceType; - @CommandLine.Option(names = { "--java" }, description = "Use Java") - boolean java = false; + static class VersionCandidates extends ArrayList { + VersionCandidates() { + super(List.copyOf(CreateProjectHelper.JAVA_VERSIONS_LTS)); + } + } + + @CommandLine.Option(names = { + "--java" }, description = "Target Java version.\n Valid values: ${COMPLETION-CANDIDATES}", completionCandidates = VersionCandidates.class, defaultValue = CreateProjectHelper.DEFAULT_JAVA_VERSION) + String javaVersion = CreateProjectHelper.DEFAULT_JAVA_VERSION; @CommandLine.Option(names = { "--kotlin" }, description = "Use Kotlin") boolean kotlin = false; @@ -20,7 +31,12 @@ public class TargetLanguageGroup { @CommandLine.Option(names = { "--scala" }, description = "Use Scala") boolean scala = false; - public SourceType getSourceType(BuildTool buildTool, Set extensions, OutputOptionMixin output) { + public SourceType getSourceType(CommandSpec spec, BuildTool buildTool, Set extensions, OutputOptionMixin output) { + if (kotlin && scala) { + throw new ParameterException(spec.commandLine(), + "Invalid source type. Projects can target either Kotlin (--kotlin) or Scala (--scala), not both."); + } + if (sourceType == null) { if (buildTool == null) { // Buildless/JBang only works with Java, atm @@ -39,9 +55,16 @@ public SourceType getSourceType(BuildTool buildTool, Set extensions, Out return sourceType; } + public String getJavaVersion() { + return javaVersion; + } + @Override public String toString() { - return "TargetLanguageGroup [java=" + java + ", kotlin=" + kotlin + ", scala=" + scala + ", sourceType=" + sourceType + return "TargetLanguageGroup [java=" + javaVersion + + ", kotlin=" + kotlin + + ", scala=" + scala + + ", sourceType=" + sourceType + "]"; } } diff --git a/devtools/cli/src/test/java/io/quarkus/cli/CliProjectGradleTest.java b/devtools/cli/src/test/java/io/quarkus/cli/CliProjectGradleTest.java index 2d928dc3e0715..ec4fe18daf7d2 100644 --- a/devtools/cli/src/test/java/io/quarkus/cli/CliProjectGradleTest.java +++ b/devtools/cli/src/test/java/io/quarkus/cli/CliProjectGradleTest.java @@ -343,6 +343,37 @@ public void testCreateArgPassthrough() throws Exception { "Should contain JAVA, found: " + result.stdout); } + @Test + public void testCreateArgJava11() throws Exception { + CliDriver.Result result = CliDriver.execute(workspaceRoot, "create", "app", "--gradle", + "-e", "-B", "--verbose", + "--java", "11"); + + // We don't need to retest this, just need to make sure all of the arguments were passed through + Assertions.assertEquals(CommandLine.ExitCode.OK, result.exitCode, "Expected OK return code." + result); + + Path buildGradle = project.resolve("build.gradle"); + String buildGradleContent = CliDriver.readFileAsString(project, buildGradle); + Assertions.assertTrue(buildGradleContent.contains("sourceCompatibility = JavaVersion.VERSION_11"), + "Java 11 should be used when specified. Found:\n" + buildGradle); + } + + @Test + public void testCreateArgJava17() throws Exception { + CliDriver.Result result = CliDriver.execute(workspaceRoot, "create", "app", "--gradle", + "-e", "-B", "--verbose", + "--java", "17"); + + // We don't need to retest this, just need to make sure all of the arguments were passed through + Assertions.assertEquals(CommandLine.ExitCode.OK, result.exitCode, "Expected OK return code." + result); + + Path buildGradle = project.resolve("build.gradle"); + String buildGradleContent = CliDriver.readFileAsString(project, buildGradle); + + Assertions.assertTrue(buildGradleContent.contains("sourceCompatibility = JavaVersion.VERSION_17"), + "Java 17 should be used when specified. Found:\n" + buildGradleContent); + } + String validateBasicIdentifiers(Path project, String group, String artifact, String version) throws Exception { Path buildGradle = project.resolve("build.gradle"); Assertions.assertTrue(buildGradle.toFile().exists(), diff --git a/devtools/cli/src/test/java/io/quarkus/cli/CliProjectMavenTest.java b/devtools/cli/src/test/java/io/quarkus/cli/CliProjectMavenTest.java index a04780824939a..c47badb782046 100644 --- a/devtools/cli/src/test/java/io/quarkus/cli/CliProjectMavenTest.java +++ b/devtools/cli/src/test/java/io/quarkus/cli/CliProjectMavenTest.java @@ -308,6 +308,38 @@ public void testCreateArgPassthrough() throws Exception { "Should contain JAVA, found: " + result.stdout); } + @Test + public void testCreateArgJava11() throws Exception { + CliDriver.Result result = CliDriver.execute(workspaceRoot, "create", "app", + "-e", "-B", "--verbose", + "--java", "11"); + + // We don't need to retest this, just need to make sure all of the arguments were passed through + Assertions.assertEquals(CommandLine.ExitCode.OK, result.exitCode, "Expected OK return code." + result); + + Path pom = project.resolve("pom.xml"); + String pomContent = CliDriver.readFileAsString(project, pom); + + Assertions.assertTrue(pomContent.contains("maven.compiler.release>11<"), + "Java 11 should be used when specified. Found:\n" + pomContent); + } + + @Test + public void testCreateArgJava17() throws Exception { + CliDriver.Result result = CliDriver.execute(workspaceRoot, "create", "app", + "-e", "-B", "--verbose", + "--java", "17"); + + // We don't need to retest this, just need to make sure all of the arguments were passed through + Assertions.assertEquals(CommandLine.ExitCode.OK, result.exitCode, "Expected OK return code." + result); + + Path pom = project.resolve("pom.xml"); + String pomContent = CliDriver.readFileAsString(project, pom); + + Assertions.assertTrue(pomContent.contains("maven.compiler.release>17<"), + "Java 17 should be used when specified. Found:\n" + pomContent); + } + String validateBasicIdentifiers(String group, String artifact, String version) throws Exception { Path pom = project.resolve("pom.xml"); diff --git a/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java index 8647608c7e379..f99f920d89c5b 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java @@ -103,6 +103,9 @@ public class CreateProjectMojo extends AbstractMojo { @Parameter(property = "platformVersion", required = false) private String bomVersion; + @Parameter(property = "javaVersion") + private String javaVersion; + /** * The {@link #path} will define the REST path of the generated code when picking only one of those extensions resteasy, * resteasy-reactive and spring-web. @@ -287,6 +290,7 @@ public void execute() throws MojoExecutionException { .artifactId(projectArtifactId) .version(projectVersion) .sourceType(sourceType) + .javaTarget(javaVersion) .className(className) .packageName(packageName) .extensions(extensions) diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/codegen/CreateProjectHelper.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/codegen/CreateProjectHelper.java index 33e670544c398..8127af5766ab3 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/codegen/CreateProjectHelper.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/codegen/CreateProjectHelper.java @@ -19,8 +19,8 @@ public class CreateProjectHelper { - private static final Set JAVA_VERSIONS_LTS = Set.of("11", "17"); - private static final String DEFAULT_JAVA_VERSION = "11"; + public static final Set JAVA_VERSIONS_LTS = Set.of("11", "17"); + public static final String DEFAULT_JAVA_VERSION = "11"; private static final Pattern JAVA_VERSION_PATTERN = Pattern.compile("(?:1\\.)?(\\d+)(?:\\..*)?"); public static final String DEFAULT_GROUP_ID = "org.acme"; diff --git a/integration-tests/maven/src/test/java/io/quarkus/maven/it/CreateProjectMojoIT.java b/integration-tests/maven/src/test/java/io/quarkus/maven/it/CreateProjectMojoIT.java index e1a3dd63cd63d..cab4d2f2fe904 100644 --- a/integration-tests/maven/src/test/java/io/quarkus/maven/it/CreateProjectMojoIT.java +++ b/integration-tests/maven/src/test/java/io/quarkus/maven/it/CreateProjectMojoIT.java @@ -389,6 +389,80 @@ public void testProjectGenerationFromScratchWithAppConfigParameter() throws Mave } + @Test + public void testProjectGenerationFromScratchWithJava11() throws MavenInvocationException, IOException { + testDir = initEmptyProject("projects/project-generation-with-java11"); + assertThat(testDir).isDirectory(); + invoker = initInvoker(testDir); + + Properties properties = new Properties(); + properties.put("javaVersion", "11"); + + InvocationResult result = setup(properties); + assertThat(result.getExitCode()).isZero(); + + testDir = new File(testDir, "code-with-quarkus"); + assertThat(new File(testDir, "pom.xml")).isFile(); + assertThat(FileUtils.readFileToString(new File(testDir, "pom.xml"), "UTF-8")) + .contains("maven.compiler.release>11<"); + } + + @Test + public void testProjectGenerationFromScratchWithJava17() throws MavenInvocationException, IOException { + testDir = initEmptyProject("projects/project-generation-with-java17"); + assertThat(testDir).isDirectory(); + invoker = initInvoker(testDir); + + Properties properties = new Properties(); + properties.put("javaVersion", "17"); + + InvocationResult result = setup(properties); + assertThat(result.getExitCode()).isZero(); + + testDir = new File(testDir, "code-with-quarkus"); + assertThat(new File(testDir, "pom.xml")).isFile(); + assertThat(FileUtils.readFileToString(new File(testDir, "pom.xml"), "UTF-8")) + .contains("maven.compiler.release>17<"); + } + + @Test + public void testProjectGenerationFromScratchWithGradleJava11() throws MavenInvocationException, IOException { + testDir = initEmptyProject("projects/project-generation-with-gradle-java11"); + assertThat(testDir).isDirectory(); + invoker = initInvoker(testDir); + + Properties properties = new Properties(); + properties.put("javaVersion", "11"); + properties.put("buildTool", "gradle"); + + InvocationResult result = setup(properties); + assertThat(result.getExitCode()).isZero(); + + testDir = new File(testDir, "code-with-quarkus"); + assertThat(new File(testDir, "build.gradle")).isFile(); + assertThat(FileUtils.readFileToString(new File(testDir, "build.gradle"), "UTF-8")) + .contains("sourceCompatibility = JavaVersion.VERSION_11"); + } + + @Test + public void testProjectGenerationFromScratchWithGradleJava17() throws MavenInvocationException, IOException { + testDir = initEmptyProject("projects/project-generation-with-gradle-java17"); + assertThat(testDir).isDirectory(); + invoker = initInvoker(testDir); + + Properties properties = new Properties(); + properties.put("javaVersion", "17"); + properties.put("buildTool", "gradle"); + + InvocationResult result = setup(properties); + assertThat(result.getExitCode()).isZero(); + + testDir = new File(testDir, "code-with-quarkus"); + assertThat(new File(testDir, "build.gradle")).isFile(); + assertThat(FileUtils.readFileToString(new File(testDir, "build.gradle"), "UTF-8")) + .contains("sourceCompatibility = JavaVersion.VERSION_17"); + } + /** * Reproducer for https://github.com/quarkusio/quarkus/issues/671 */