diff --git a/core/devmode/pom.xml b/core/devmode/pom.xml
index 028c2dcd12333..13db264232759 100644
--- a/core/devmode/pom.xml
+++ b/core/devmode/pom.xml
@@ -22,6 +22,23 @@
org.jboss.logmanager
jboss-logmanager-embedded
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
diff --git a/core/devmode/src/main/java/io/quarkus/dev/ClassLoaderCompiler.java b/core/devmode/src/main/java/io/quarkus/dev/ClassLoaderCompiler.java
index 5aebb55cb2da2..9d700f168868f 100644
--- a/core/devmode/src/main/java/io/quarkus/dev/ClassLoaderCompiler.java
+++ b/core/devmode/src/main/java/io/quarkus/dev/ClassLoaderCompiler.java
@@ -137,7 +137,10 @@ public ClassLoaderCompiler(ClassLoader classLoader,
new File(i.getProjectDirectory()),
new File(sourcePath),
new File(i.getClassesPath()),
- context.getSourceEncoding()));
+ context.getSourceEncoding(),
+ context.getCompilerOptions(),
+ context.getSourceJavaVersion(),
+ context.getTargetJvmVersion()));
});
}
}
diff --git a/core/devmode/src/main/java/io/quarkus/dev/CompilationProvider.java b/core/devmode/src/main/java/io/quarkus/dev/CompilationProvider.java
index 471dd2f714ec4..864d01b938b2a 100644
--- a/core/devmode/src/main/java/io/quarkus/dev/CompilationProvider.java
+++ b/core/devmode/src/main/java/io/quarkus/dev/CompilationProvider.java
@@ -4,7 +4,9 @@
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
import java.util.Set;
public interface CompilationProvider {
@@ -27,6 +29,9 @@ class Context {
private final File sourceDirectory;
private final File outputDirectory;
private final Charset sourceEncoding;
+ private final List compilerOptions;
+ private final String sourceJavaVersion;
+ private final String targetJvmVersion;
public Context(
String name,
@@ -34,7 +39,10 @@ public Context(
File projectDirectory,
File sourceDirectory,
File outputDirectory,
- String sourceEncoding) {
+ String sourceEncoding,
+ List compilerOptions,
+ String sourceJavaVersion,
+ String targetJvmVersion) {
this.name = name;
this.classpath = classpath;
@@ -42,6 +50,9 @@ public Context(
this.sourceDirectory = sourceDirectory;
this.outputDirectory = outputDirectory;
this.sourceEncoding = sourceEncoding == null ? StandardCharsets.UTF_8 : Charset.forName(sourceEncoding);
+ this.compilerOptions = compilerOptions == null ? new ArrayList() : compilerOptions;
+ this.sourceJavaVersion = sourceJavaVersion;
+ this.targetJvmVersion = targetJvmVersion;
}
public String getName() {
@@ -67,5 +78,17 @@ public File getOutputDirectory() {
public Charset getSourceEncoding() {
return sourceEncoding;
}
+
+ public List getCompilerOptions() {
+ return compilerOptions;
+ }
+
+ public String getSourceJavaVersion() {
+ return sourceJavaVersion;
+ }
+
+ public String getTargetJvmVersion() {
+ return targetJvmVersion;
+ }
}
}
diff --git a/core/devmode/src/main/java/io/quarkus/dev/CompilerFlags.java b/core/devmode/src/main/java/io/quarkus/dev/CompilerFlags.java
new file mode 100644
index 0000000000000..58c2d77b023f4
--- /dev/null
+++ b/core/devmode/src/main/java/io/quarkus/dev/CompilerFlags.java
@@ -0,0 +1,75 @@
+package io.quarkus.dev;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import io.quarkus.runtime.util.StringUtil;
+
+/**
+ * A set of compiler flags for javac.
+ *
+ * Can combine system-provided default flags with user-supplied flags and -source
+ * and -target
settings.
+ */
+public class CompilerFlags {
+
+ private final Set defaultFlags;
+ private final List userFlags;
+ private final String sourceJavaVersion; //can be null
+ private final String targetJavaVersion; //can be null
+
+ public CompilerFlags(
+ Set defaultFlags,
+ Collection userFlags,
+ String sourceJavaVersion,
+ String targetJavaVersion) {
+
+ this.defaultFlags = defaultFlags == null ? new HashSet<>() : new HashSet<>(defaultFlags);
+ this.userFlags = userFlags == null ? new ArrayList<>() : new ArrayList<>(userFlags);
+ this.sourceJavaVersion = sourceJavaVersion;
+ this.targetJavaVersion = targetJavaVersion;
+ }
+
+ public List toList() {
+ List flagList = new ArrayList<>();
+
+ // The set of effective default flags is the set of default flags except the ones also
+ // set by the user. This ensures that we do not needlessly pass the default flags twice.
+ Set effectiveDefaultFlags = new HashSet<>(this.defaultFlags);
+ effectiveDefaultFlags.removeAll(userFlags);
+
+ flagList.addAll(effectiveDefaultFlags);
+
+ // Add -source and -target flags.
+ if (sourceJavaVersion != null) {
+ flagList.add("-source");
+ flagList.add(sourceJavaVersion);
+ }
+ if (targetJavaVersion != null) {
+ flagList.add("-target");
+ flagList.add(targetJavaVersion);
+ }
+
+ flagList.addAll(userFlags);
+
+ return flagList;
+ }
+
+ @Override
+ public int hashCode() {
+ return toList().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof CompilerFlags && toList().equals(((CompilerFlags) obj).toList());
+ }
+
+ @Override
+ public String toString() {
+ return "CompilerFlags@{" + StringUtil.join(", ", toList().iterator()) + "}";
+ }
+}
diff --git a/core/devmode/src/main/java/io/quarkus/dev/DevModeContext.java b/core/devmode/src/main/java/io/quarkus/dev/DevModeContext.java
index e2ab7d443cb7b..1ae16d2dca091 100644
--- a/core/devmode/src/main/java/io/quarkus/dev/DevModeContext.java
+++ b/core/devmode/src/main/java/io/quarkus/dev/DevModeContext.java
@@ -30,6 +30,10 @@ public class DevModeContext implements Serializable {
private boolean test;
private boolean abortOnFailedStart;
+ private List compilerOptions;
+ private String sourceJavaVersion;
+ private String targetJvmVersion;
+
public List getClassPath() {
return classPath;
}
@@ -90,6 +94,30 @@ public void setAbortOnFailedStart(boolean abortOnFailedStart) {
this.abortOnFailedStart = abortOnFailedStart;
}
+ public List getCompilerOptions() {
+ return compilerOptions;
+ }
+
+ public void setCompilerOptions(List compilerOptions) {
+ this.compilerOptions = compilerOptions;
+ }
+
+ public String getSourceJavaVersion() {
+ return sourceJavaVersion;
+ }
+
+ public void setSourceJavaVersion(String sourceJavaVersion) {
+ this.sourceJavaVersion = sourceJavaVersion;
+ }
+
+ public String getTargetJvmVersion() {
+ return targetJvmVersion;
+ }
+
+ public void setTargetJvmVersion(String targetJvmVersion) {
+ this.targetJvmVersion = targetJvmVersion;
+ }
+
public static class ModuleInfo implements Serializable {
private final String name;
diff --git a/core/devmode/src/main/java/io/quarkus/dev/JavaCompilationProvider.java b/core/devmode/src/main/java/io/quarkus/dev/JavaCompilationProvider.java
index a4accd940e267..b2442bbba93f1 100644
--- a/core/devmode/src/main/java/io/quarkus/dev/JavaCompilationProvider.java
+++ b/core/devmode/src/main/java/io/quarkus/dev/JavaCompilationProvider.java
@@ -8,7 +8,7 @@
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
-import java.util.List;
+import java.util.HashSet;
import java.util.Set;
import javax.tools.Diagnostic;
@@ -28,7 +28,7 @@ public class JavaCompilationProvider implements CompilationProvider {
// -g is used to make the java compiler generate all debugging info
// -parameters is used to generate metadata for reflection on method parameters
// this is useful when people using debuggers against their hot-reloaded app
- private static final List COMPILER_OPTIONS = Arrays.asList("-g", "-parameters");
+ private static final Set COMPILER_OPTIONS = new HashSet<>(Arrays.asList("-g", "-parameters"));
@Override
public Set handledExtensions() {
@@ -48,9 +48,12 @@ public void compile(Set filesToCompile, Context context) {
fileManager.setLocation(StandardLocation.CLASS_PATH, context.getClasspath());
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(context.getOutputDirectory()));
+ CompilerFlags compilerFlags = new CompilerFlags(COMPILER_OPTIONS, context.getCompilerOptions(),
+ context.getSourceJavaVersion(), context.getTargetJvmVersion());
+
Iterable extends JavaFileObject> sources = fileManager.getJavaFileObjectsFromFiles(filesToCompile);
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics,
- COMPILER_OPTIONS, null, sources);
+ compilerFlags.toList(), null, sources);
if (!task.call()) {
throw new RuntimeException("Compilation failed" + diagnostics.getDiagnostics());
diff --git a/core/devmode/src/test/java/io/quarkus/dev/CompilerFlagsTest.java b/core/devmode/src/test/java/io/quarkus/dev/CompilerFlagsTest.java
new file mode 100644
index 0000000000000..c0f976b9cc44a
--- /dev/null
+++ b/core/devmode/src/test/java/io/quarkus/dev/CompilerFlagsTest.java
@@ -0,0 +1,91 @@
+package io.quarkus.dev;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+public class CompilerFlagsTest {
+
+ @Test
+ void nullHandling() {
+ assertAll(
+ () -> assertEquals(
+ new CompilerFlags(null, null, null, null),
+ new CompilerFlags(setOf(), listOf(), null, null)));
+ }
+
+ @Test
+ void defaulting() {
+ assertAll(
+ () -> assertEquals(
+ new CompilerFlags(setOf("-a", "-b"), listOf(), null, null),
+ new CompilerFlags(setOf(), listOf("-a", "-b"), null, null)),
+ () -> assertEquals(
+ new CompilerFlags(setOf("-a", "-b"), listOf("-c", "-d"), null, null),
+ new CompilerFlags(setOf(), listOf("-a", "-b", "-c", "-d"), null, null)));
+ }
+
+ @Test
+ void redundancyReduction() {
+ assertAll(
+ () -> assertEquals(
+ new CompilerFlags(setOf("-a", "-b"), listOf(), null, null),
+ new CompilerFlags(setOf(), listOf("-a", "-b"), null, null)),
+ () -> assertEquals(
+ new CompilerFlags(setOf("-a", "-b", "-c"), listOf("-a", "-b"), null, null),
+ new CompilerFlags(setOf("-c"), listOf("-a", "-b"), null, null)));
+ }
+
+ @Test
+ void sourceAndTarget() {
+ assertAll(
+ () -> assertEquals(
+ new CompilerFlags(setOf(), listOf(), "1", null),
+ new CompilerFlags(setOf(), listOf("-source", "1"), null, null)),
+ () -> assertEquals(
+ new CompilerFlags(setOf(), listOf(), null, "2"),
+ new CompilerFlags(setOf(), listOf("-target", "2"), null, null)),
+ () -> assertEquals(
+ new CompilerFlags(setOf(), listOf(), "1", "2"),
+ new CompilerFlags(setOf(), listOf("-source", "1", "-target", "2"), null, null)),
+ () -> assertEquals(
+ new CompilerFlags(setOf(), listOf("-source", "3", "-target", "4"), "1", "2"),
+ new CompilerFlags(setOf(), listOf("-source", "1", "-target", "2", "-source", "3", "-target", "4"), null,
+ null)));
+ }
+
+ @Test
+ void allFeatures() {
+ assertAll(
+ () -> assertEquals(
+ new CompilerFlags(setOf("-b", "-c", "-d"), listOf("-a", "-b", "-c"), "1", "2"),
+ new CompilerFlags(setOf(), listOf("-d", "-source", "1", "-target", "2", "-a", "-b", "-c"), null,
+ null)));
+ }
+
+ @Test
+ void listConversion() {
+ assertAll(
+ () -> assertEquals(
+ new CompilerFlags(null, null, null, null).toList(),
+ listOf()),
+ () -> assertEquals(
+ new CompilerFlags(setOf(), listOf("-a", "-b", "-c", "-d"), null, null).toList(),
+ listOf("-a", "-b", "-c", "-d")));
+ }
+
+ private List listOf(String... strings) {
+ return Arrays.asList(strings);
+ }
+
+ private Set setOf(String... strings) {
+ return new HashSet<>(Arrays.asList(strings));
+ }
+
+}
diff --git a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java
index 0e2e8918954d4..10e8ff874334d 100644
--- a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java
+++ b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java
@@ -28,6 +28,7 @@
import java.util.zip.ZipOutputStream;
import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
@@ -42,6 +43,7 @@
import org.apache.maven.shared.invoker.Invoker;
import org.apache.maven.shared.invoker.MavenInvocationException;
import org.apache.maven.toolchain.ToolchainManager;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.RemoteRepository;
@@ -173,6 +175,25 @@ public class DevMojo extends AbstractMojo {
@Parameter(defaultValue = "${noDeps}")
private boolean noDeps = false;
+ /**
+ * Additional parameters to pass to javac when recompiling changed
+ * source files.
+ */
+ @Parameter
+ private List compilerArgs;
+
+ /**
+ * The -source argument to javac.
+ */
+ @Parameter(defaultValue = "${maven.compiler.source}")
+ private String source;
+
+ /**
+ * The -target argument to javac.
+ */
+ @Parameter(defaultValue = "${maven.compiler.target}")
+ private String target;
+
@Component
private ToolchainManager toolchainManager;
@@ -285,6 +306,34 @@ public void execute() throws MojoFailureException, MojoExecutionException {
}
devModeContext.getBuildSystemProperties().putAll((Map) project.getProperties());
devModeContext.setSourceEncoding(getSourceEncoding());
+ devModeContext.setSourceJavaVersion(source);
+ devModeContext.setTargetJvmVersion(target);
+
+ // Set compilation flags. Try the explicitly given configuration first. Otherwise,
+ // refer to the configuration of the Maven Compiler Plugin.
+ if (compilerArgs != null) {
+ devModeContext.setCompilerOptions(compilerArgs);
+ } else {
+ for (Plugin plugin : project.getBuildPlugins()) {
+ if (!plugin.getKey().equals("org.apache.maven.plugins:maven-compiler-plugin")) {
+ continue;
+ }
+ Xpp3Dom compilerPluginConfiguration = (Xpp3Dom) plugin.getConfiguration();
+ if (compilerPluginConfiguration == null) {
+ continue;
+ }
+ Xpp3Dom compilerPluginArgsConfiguration = compilerPluginConfiguration.getChild("compilerArgs");
+ if (compilerPluginArgsConfiguration == null) {
+ continue;
+ }
+ List compilerPluginArgs = new ArrayList<>();
+ for (Xpp3Dom argConfiguration : compilerPluginArgsConfiguration.getChildren()) {
+ compilerPluginArgs.add(argConfiguration.getValue());
+ }
+ devModeContext.setCompilerOptions(compilerPluginArgs);
+ break;
+ }
+ }
final AppModel appModel;
try {
diff --git a/docs/src/main/asciidoc/maven-tooling.adoc b/docs/src/main/asciidoc/maven-tooling.adoc
index 61be4076861c4..21f002a32e4b1 100644
--- a/docs/src/main/asciidoc/maven-tooling.adoc
+++ b/docs/src/main/asciidoc/maven-tooling.adoc
@@ -169,6 +169,39 @@ Start Quarkus in dev mode on the remote host. Now you need to connect your local
Now every time you refresh the browser you should see any changes you have made locally immediately visible in the remote
app.
+=== Configuring Development Mode
+
+By default, the Maven plugin picks up compiler flags to pass to
+`javac` from `maven-compiler-plugin`.
+
+If you need to customize the compiler flags used in development mode,
+add a `configuration` section to the `plugin` block and set the
+`compilerArgs` property just as you would when configuring
+`maven-compiler-plugin`. You can also set `source`, `target`, and
+`jvmArgs`. For example, to pass `--enable-preview` to both the JVM
+and `javac`:
+
+[source,xml]
+----
+
+ io.quarkus
+ quarkus-maven-plugin
+ ${quarkus.version}
+
+
+
+ ${maven.compiler.target}
+
+ --enable-preview
+
+ --enable-preview
+
+
+ ...
+
+----
+
+
== Debugging
In development mode, Quarkus starts by default with debug mode enabled, listening to port `5005` without suspending the JVM.