diff --git a/examples/gwt/war/build.gradle b/examples/gwt/war/build.gradle
new file mode 100644
index 00000000..3ff8a3b4
--- /dev/null
+++ b/examples/gwt/war/build.gradle
@@ -0,0 +1,15 @@
+import io.freefair.gradle.plugins.gwt.tasks.AbstractGwtTask
+
+plugins {
+ id "io.freefair.gwt-war"
+ id "war"
+}
+
+gwt {
+ modules.add("io.freefair.example.Example")
+}
+
+dependencies {
+ gwtClasspath "org.gwtproject:gwt-user:2.12.1"
+ gwtClasspath project(":code-generator:generator")
+}
diff --git a/examples/gwt/war/src/main/java/io/freefair/example/Example.gwt.xml b/examples/gwt/war/src/main/java/io/freefair/example/Example.gwt.xml
new file mode 100644
index 00000000..5f1a494b
--- /dev/null
+++ b/examples/gwt/war/src/main/java/io/freefair/example/Example.gwt.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/examples/settings.gradle b/examples/settings.gradle
index 09dcf303..0e8fcd46 100644
--- a/examples/settings.gradle
+++ b/examples/settings.gradle
@@ -46,3 +46,5 @@ include 'plantuml'
include ":mjml"
include ":okhttp"
+
+include ":gwt:war"
diff --git a/gwt-plugin/build.gradle b/gwt-plugin/build.gradle
new file mode 100644
index 00000000..c01c403b
--- /dev/null
+++ b/gwt-plugin/build.gradle
@@ -0,0 +1,44 @@
+plugins {
+ id "maven-publish"
+ id "java-gradle-plugin"
+ id "com.gradle.plugin-publish"
+}
+
+dependencies {
+
+ compileOnly 'org.gwtproject:gwt-dev:2.12.1'
+
+}
+
+gradlePlugin {
+ plugins {
+ gwt {
+ id = "io.freefair.gwt"
+ implementationClass = "io.freefair.gradle.plugins.gwt.GwtPlugin"
+ displayName = "GWT Plugin"
+ description = "GWT Plugin"
+ tags.set(['gwt'])
+ }
+ gwtBase {
+ id = "io.freefair.gwt-base"
+ implementationClass = "io.freefair.gradle.plugins.gwt.GwtBasePlugin"
+ displayName = "GWT Base Plugin"
+ description = "GWT Plugin"
+ tags.set(['gwt'])
+ }
+ gwtWar {
+ id = "io.freefair.gwt-war"
+ implementationClass = "io.freefair.gradle.plugins.gwt.GwtWarPlugin"
+ displayName = "GWT War Plugin"
+ description = "GWT Plugin"
+ tags.set(['gwt'])
+ }
+ gwtWebjar {
+ id = "io.freefair.gwt-webjar"
+ implementationClass = "io.freefair.gradle.plugins.gwt.GwtWebJarPlugin"
+ displayName = "GWT WebJar Plugin"
+ description = "GWT Plugin"
+ tags.set(['gwt'])
+ }
+ }
+}
diff --git a/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/CommonGwtToolOptions.java b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/CommonGwtToolOptions.java
new file mode 100644
index 00000000..76b366db
--- /dev/null
+++ b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/CommonGwtToolOptions.java
@@ -0,0 +1,80 @@
+package io.freefair.gradle.plugins.gwt;
+
+import org.gradle.api.file.DirectoryProperty;
+import org.gradle.api.provider.ListProperty;
+import org.gradle.api.provider.MapProperty;
+import org.gradle.api.provider.Property;
+import org.gradle.api.tasks.Console;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputDirectory;
+
+/**
+ * @author Lars Grefer
+ */
+public interface CommonGwtToolOptions {
+
+ @Console
+ Property getLogLevel();
+
+ @Optional
+ @Input
+ Property getFailOnError();
+
+ @Optional
+ @OutputDirectory
+ DirectoryProperty getWorkDir();
+
+ @Optional
+ @Input
+ Property getStyle();
+
+ /**
+ * Set the values of a property in the form of propertyName=value1[,value2...].
+ */
+ @Optional
+ @Input
+ MapProperty getSetProperty();
+
+ @Optional
+ @Input
+ Property getIncremental();
+
+ @Optional
+ @Input
+ Property getSourceLevel();
+
+ /**
+ * Generate exports for JsInterop purposes. If no -includeJsInteropExport/-excludeJsInteropExport provided, generates all exports. (defaults to OFF)
+ */
+ @Optional
+ @Input
+ Property getGenerateJsInteropExports();
+
+ /**
+ * Include members and classes while generating JsInterop exports. Flag could be set multiple times to expand the pattern. (The flag has only effect if exporting is enabled via -generateJsInteropExports)
+ */
+ @Optional
+ @Input
+ ListProperty getIncludeJsInteropExports();
+
+ /**
+ * Include/exclude members and classes while generating JsInterop exports. Flag could be set multiple times to expand the pattern. (The flag has only effect if exporting is enabled via -generateJsInteropExports)
+ */
+ @Optional
+ @Input
+ ListProperty getExcludeJsInteropExports();
+
+ /**
+ * @return EXPERIMENTAL: Specifies method display name mode for chrome devtools: NONE, ONLY_METHOD_NAME, ABBREVIATED or FULL (defaults to NONE)
+ */
+ @Optional
+ @Input
+ Property getXmethodNameDisplayMode();
+
+ /**
+ * The GWT modules that the code server should compile. (Example: com.example.MyApp)
+ */
+ @Input
+ ListProperty getModule();
+}
diff --git a/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtBasePlugin.java b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtBasePlugin.java
new file mode 100644
index 00000000..d444b843
--- /dev/null
+++ b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtBasePlugin.java
@@ -0,0 +1,82 @@
+package io.freefair.gradle.plugins.gwt;
+
+import io.freefair.gradle.plugins.gwt.tasks.AbstractGwtTask;
+import io.freefair.gradle.plugins.gwt.tasks.GwtCodeServerTask;
+import io.freefair.gradle.plugins.gwt.tasks.GwtCompileTask;
+import io.freefair.gradle.plugins.gwt.tasks.GwtDevModeTask;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.attributes.DocsType;
+import org.gradle.api.attributes.VerificationType;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.plugins.JavaPluginExtension;
+import org.gradle.api.tasks.SourceSet;
+import org.gradle.api.tasks.TaskProvider;
+import org.gradle.api.tasks.compile.AbstractCompile;
+import org.gradle.api.tasks.compile.JavaCompile;
+
+/**
+ * @author Lars Grefer
+ */
+public class GwtBasePlugin implements Plugin {
+
+ @Override
+ public void apply(Project project) {
+
+ project.getPlugins().apply(JavaPlugin.class);
+
+ Configuration gwtDev = project.getConfigurations().create("gwtDev");
+
+ Configuration gwtClasspath = project.getConfigurations().create("gwtClasspath");
+
+ Configuration gwtSources = project.getConfigurations().create("gwtSources");
+ gwtSources.extendsFrom(gwtClasspath);
+
+ gwtSources.getAttributes().attribute(VerificationType.VERIFICATION_TYPE_ATTRIBUTE, project.getObjects().named(VerificationType.class, VerificationType.MAIN_SOURCES));
+ gwtSources.getAttributes().attribute(DocsType.DOCS_TYPE_ATTRIBUTE, project.getObjects().named(DocsType.class, DocsType.SOURCES));
+
+ GwtExtension gwtExtension = project.getExtensions().create("gwt", GwtExtension.class);
+
+ project.afterEvaluate(p -> {
+ gwtDev.defaultDependencies(ds -> {
+ ds.add(project.getDependencies().create("org.gwtproject:gwt-dev:" + gwtExtension.getToolVersion().get()));
+ });
+ });
+
+ project.getTasks().withType(AbstractGwtTask.class, gwtTask -> {
+ gwtTask.setGroup("gwt");
+
+ gwtTask.getGwtClasspath().from(gwtDev);
+ gwtTask.getGwtClasspath().from(gwtClasspath);
+ JavaPluginExtension pluginExtension = project.getExtensions().getByType(JavaPluginExtension.class);
+ SourceSet main = pluginExtension.getSourceSets().getByName("main");
+ gwtTask.getGwtClasspath().from(main.getAllJava().getSourceDirectories());
+
+ gwtTask.getWorkDir().set(gwtTask.getTemporaryDir());
+
+ gwtTask.getModule().convention(gwtExtension.getModules());
+
+ gwtTask.getSourceLevel().convention(project.getTasks().named("compileJava", JavaCompile.class).map(AbstractCompile::getSourceCompatibility));
+ });
+
+ TaskProvider gwtCompileTaskProvider = project.getTasks().register("gwtCompile", GwtCompileTask.class, gwtCompile -> {
+ gwtCompile.getWar().convention(project.getLayout().getBuildDirectory().dir("gwt/compile/war"));
+ gwtCompile.getDeploy().convention(project.getLayout().getBuildDirectory().dir("gwt/compile/deploy"));
+ gwtCompile.getExtra().convention(project.getLayout().getBuildDirectory().dir("gwt/compile/extra"));
+
+ gwtCompile.getGwtClasspath().from(gwtSources);
+ });
+
+ TaskProvider gwtDevModeTaskProvider = project.getTasks().register("gwtDevMode", GwtDevModeTask.class, gwtDevMode -> {
+ gwtDevMode.getWar().convention(project.getLayout().getBuildDirectory().dir("gwt/dev-mode/war"));
+ gwtDevMode.getDeploy().convention(project.getLayout().getBuildDirectory().dir("gwt/dev-mode/deploy"));
+ gwtDevMode.getExtra().convention(project.getLayout().getBuildDirectory().dir("gwt/dev-mode/extra"));
+ });
+
+ project.getTasks().register("gwtCodeServer", GwtCodeServerTask.class, gwtCodeServer -> {
+ gwtCodeServer.getLauncherDir().convention(project.getLayout().getBuildDirectory().dir("gwt/code-server"));
+ });
+
+ }
+}
diff --git a/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtCodeServerOptions.java b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtCodeServerOptions.java
new file mode 100644
index 00000000..76e505e9
--- /dev/null
+++ b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtCodeServerOptions.java
@@ -0,0 +1,77 @@
+package io.freefair.gradle.plugins.gwt;
+
+import org.gradle.api.file.DirectoryProperty;
+import org.gradle.api.provider.Property;
+import org.gradle.api.tasks.*;
+
+/**
+ * @author Lars Grefer
+ */
+public interface GwtCodeServerOptions extends CommonGwtToolOptions {
+
+ /**
+ * Allows -src flags to reference missing directories. (defaults to OFF)
+ */
+ @Optional
+ @Input
+ Property getAllowMissingSrc();
+
+ /**
+ * Exits after compiling the modules. The exit code will be 0 if the compile succeeded. (defaults to OFF)
+ */
+ @Optional
+ @Input
+ Property getCompileTest();
+
+ /**
+ * The number of times to recompile (after the first one) during a compile test.
+ */
+ @Optional
+ @Input
+ Property getCompileTestRecompiles();
+
+ /**
+ * Precompile modules. (defaults to ON)
+ */
+ @Optional
+ @Input
+ Property getPrecompile();
+
+ /**
+ * The port where the code server will run.
+ */
+ @Optional
+ @Input
+ Property getPort();
+
+ /**
+ * A directory containing GWT source to be prepended to the classpath for compiling.
+ */
+ @Optional
+ @InputDirectory
+ DirectoryProperty getSrc();
+
+ /**
+ * An output directory where files for launching Super Dev Mode will be written. (Optional.)
+ */
+ @Optional
+ @OutputDirectory
+ DirectoryProperty getLauncherDir();
+
+ /**
+ * Specifies the bind address for the code server and web server (defaults to 127.0.0.1)
+ */
+ @Optional
+ @Input
+ Property getBindAddress();
+
+ /**
+ * EXPERIMENTAL: Enables Javascript output suitable for post-compilation by Closure Compiler (defaults to OFF)
+ */
+ @Optional
+ @Input
+ Property getXclosureFormattedOutput();
+
+
+
+}
diff --git a/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtCompileOptions.java b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtCompileOptions.java
new file mode 100644
index 00000000..63d970f2
--- /dev/null
+++ b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtCompileOptions.java
@@ -0,0 +1,133 @@
+package io.freefair.gradle.plugins.gwt;
+
+import org.gradle.api.file.DirectoryProperty;
+import org.gradle.api.provider.Property;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputDirectory;
+
+/**
+ * @author Lars Grefer
+ */
+public interface GwtCompileOptions extends CommonGwtToolOptions {
+
+ /**
+ * EXPERIMENTAL: Enables Javascript output suitable for post-compilation by Closure Compiler (defaults to OFF)
+ */
+ @Optional
+ @Input
+ Property getXClosureFormattedOutput();
+
+ /**
+ * Compile a report that tells the "Story of Your Compile". (defaults to OFF)
+ */
+ @Optional
+ @Input
+ Property getCompileReport();
+
+ /**
+ * EXPERIMENTAL: DEPRECATED: use jre.checks.checkLevel instead. (defaults to OFF)
+ */
+ @Optional
+ @Input
+ Property getXcheckCasts();
+
+ /**
+ * EXPERIMENTAL: Include metadata for some java.lang.Class methods (e.g. getName()). (defaults to ON)
+ */
+ @Optional
+ @Input
+ Property getXclassMetadata();
+
+ /**
+ * Compile quickly with minimal optimizations. (defaults to OFF)
+ */
+ @Optional
+ @Input
+ Property getDraftCompile();
+
+ /**
+ * Include assert statements in compiled output. (defaults to OFF)
+ */
+ @Optional
+ @Input
+ Property getCheckAssertions();
+
+ /**
+ * EXPERIMENTAL: Limits of number of fragments using a code splitter that merges split points.
+ */
+ @Optional
+ @Input
+ Property getXfragmentCount();
+
+ /**
+ * Debugging: causes normally-transient generated types to be saved in the specified directory
+ */
+ @Optional
+ @OutputDirectory
+ DirectoryProperty getGen();
+
+ /**
+ * Puts most JavaScript globals into namespaces. Default: PACKAGE for -draftCompile, otherwise NONE
+ */
+ @Optional
+ @Input
+ Property getXnamespace();
+
+ /**
+ * Sets the optimization level used by the compiler. 0=none 9=maximum.
+ */
+ @Optional
+ @Input
+ Property getOptimize();
+
+ /**
+ * Enables saving source code needed by debuggers. Also see -debugDir. (defaults to OFF)
+ */
+ @Optional
+ @Input
+ Property getSaveSource();
+
+ /**
+ * Validate all source code, but do not compile. (defaults to OFF)
+ */
+ @Optional
+ @Input
+ Property getValidateOnly();
+
+ /**
+ * The number of local workers to use when compiling permutations
+ */
+ @Optional
+ @Input
+ Property getLocalWorkers();
+
+ /**
+ * The directory into which deployable output files will be written (defaults to 'war')
+ */
+ @OutputDirectory
+ DirectoryProperty getWar();
+
+ /**
+ * The directory into which deployable but not servable output files will be written (defaults to 'WEB-INF/deploy' under the -war directory/jar, and may be the same as the -extra directory/jar)
+ */
+ @Optional
+ @OutputDirectory
+ DirectoryProperty getDeploy();
+
+ /**
+ * The directory into which extra files, not intended for deployment, will be written
+ */
+ @Optional
+ @OutputDirectory
+ DirectoryProperty getExtra();
+
+ /**
+ * Overrides where source files useful to debuggers will be written. Default: saved with extras.
+ */
+ @Optional
+ @OutputDirectory
+ DirectoryProperty getSaveSourceOutput();
+
+
+}
diff --git a/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtDevModeOptions.java b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtDevModeOptions.java
new file mode 100644
index 00000000..b1f52760
--- /dev/null
+++ b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtDevModeOptions.java
@@ -0,0 +1,104 @@
+package io.freefair.gradle.plugins.gwt;
+
+import org.gradle.api.file.DirectoryProperty;
+import org.gradle.api.provider.Property;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputDirectory;
+
+/**
+ * @author Lars Grefer
+ */
+public interface GwtDevModeOptions extends CommonGwtToolOptions {
+
+ /**
+ * Starts a servlet container serving the directory specified by the -war flag. (defaults to ON)
+ */
+ @Optional
+ @Input
+ Property getStartServer();
+
+ /**
+ * Specifies the TCP port for the embedded web server (defaults to 8888)
+ */
+ @Optional
+ @Input
+ Property getPort();
+
+ /**
+ * Logs to a file in the given directory, as well as graphically
+ */
+ @Optional
+ @OutputDirectory
+ DirectoryProperty getLogdir();
+
+ /**
+ * Debugging: causes normally-transient generated types to be saved in the specified directory
+ */
+ @Optional
+ @OutputDirectory
+ DirectoryProperty getGen();
+
+ /**
+ * Specifies the bind address for the code server and web server (defaults to 127.0.0.1)
+ */
+ @Optional
+ @Input
+ Property getBindAddress();
+
+ /**
+ * Specifies the TCP port for the code server (defaults to 9997 for classic Dev Mode or 9876 for Super Dev Mode)
+ */
+ @Optional
+ @Input
+ Property getCodeServerPort();
+
+ /**
+ * Runs Super Dev Mode instead of classic Development Mode. (defaults to ON)
+ */
+ @Optional
+ @Input
+ Property getSuperDevMode();
+
+ /**
+ * Specify a different embedded web server to run (must implement ServletContainerLauncher)
+ */
+ @Optional
+ @Input
+ Property getServer();
+
+ /**
+ * Automatically launches the specified URL
+ */
+ @Optional
+ @Input
+ Property getStartupUrl();
+
+ /**
+ * The directory into which deployable output files will be written (defaults to 'war')
+ */
+ @OutputDirectory
+ DirectoryProperty getWar();
+
+ /**
+ * The directory into which deployable but not servable output files will be written (defaults to 'WEB-INF/deploy' under the -war directory/jar, and may be the same as the -extra directory/jar)
+ */
+ @Optional
+ @OutputDirectory
+ DirectoryProperty getDeploy();
+
+ /**
+ * The directory into which extra files, not intended for deployment, will be written
+ */
+ @Optional
+ @OutputDirectory
+ DirectoryProperty getExtra();
+
+ /**
+ * The subdirectory inside the war dir where DevMode will create module directories. (defaults empty for top level)
+ */
+ @Optional
+ @Input
+ Property getModulePathPrefix();
+
+}
diff --git a/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtExtension.java b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtExtension.java
new file mode 100644
index 00000000..b0b2b396
--- /dev/null
+++ b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtExtension.java
@@ -0,0 +1,18 @@
+package io.freefair.gradle.plugins.gwt;
+
+import org.gradle.api.provider.ListProperty;
+import org.gradle.api.provider.Property;
+
+/**
+ * @author Lars Grefer
+ */
+public abstract class GwtExtension {
+
+ public abstract Property getToolVersion();
+
+ public abstract ListProperty getModules();
+
+ public GwtExtension() {
+ getToolVersion().convention("2.12.1");
+ }
+}
diff --git a/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtPlugin.java b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtPlugin.java
new file mode 100644
index 00000000..1f0c736d
--- /dev/null
+++ b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtPlugin.java
@@ -0,0 +1,21 @@
+package io.freefair.gradle.plugins.gwt;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.plugins.WarPlugin;
+
+/**
+ * @author Lars Grefer
+ */
+public class GwtPlugin implements Plugin {
+
+ @Override
+ public void apply(Project project) {
+ project.getPlugins().apply(GwtBasePlugin.class);
+
+ project.getPlugins().withType(WarPlugin.class, warPlugin -> {
+ project.getPlugins().apply(GwtWarPlugin.class);
+ });
+
+ }
+}
diff --git a/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtWarPlugin.java b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtWarPlugin.java
new file mode 100644
index 00000000..ef17a95f
--- /dev/null
+++ b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtWarPlugin.java
@@ -0,0 +1,34 @@
+package io.freefair.gradle.plugins.gwt;
+
+import io.freefair.gradle.plugins.gwt.tasks.GwtCompileTask;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.plugins.WarPlugin;
+import org.gradle.api.tasks.TaskProvider;
+import org.gradle.api.tasks.bundling.War;
+
+/**
+ * @author Lars Grefer
+ */
+public class GwtWarPlugin implements Plugin {
+
+ @Override
+ public void apply(Project project) {
+
+ project.getPlugins().apply(WarPlugin.class);
+ project.getPlugins().apply(GwtBasePlugin.class);
+
+ TaskProvider warTask = project.getTasks().named(WarPlugin.WAR_TASK_NAME, War.class);
+ TaskProvider gwtCompile = project.getTasks().named("gwtCompile", GwtCompileTask.class);
+
+ warTask.configure(war -> {
+ war.from(gwtCompile.flatMap(GwtCompileOptions::getWar));
+ });
+
+ gwtCompile.configure(task -> {
+ task.getDeploy().unsetConvention();
+ task.getDeploy().unset();
+ });
+
+ }
+}
diff --git a/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtWebJarPlugin.java b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtWebJarPlugin.java
new file mode 100644
index 00000000..c194c113
--- /dev/null
+++ b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/GwtWebJarPlugin.java
@@ -0,0 +1,35 @@
+package io.freefair.gradle.plugins.gwt;
+
+import io.freefair.gradle.plugins.gwt.tasks.GwtCompileTask;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.tasks.TaskProvider;
+import org.gradle.language.jvm.tasks.ProcessResources;
+
+/**
+ * @author Lars Grefer
+ */
+public class GwtWebJarPlugin implements Plugin {
+
+ @Override
+ public void apply(Project project) {
+
+ project.getPlugins().apply(GwtBasePlugin.class);
+
+ TaskProvider gwtCompile = project.getTasks().named("gwtCompile", GwtCompileTask.class);
+
+ project.getTasks()
+ .named(JavaPlugin.PROCESS_RESOURCES_TASK_NAME, ProcessResources.class)
+ .configure(processResources -> {
+ processResources.into("META-INF/resources", resources -> {
+ resources.from(gwtCompile.flatMap(GwtCompileOptions::getWar));
+ });
+
+ processResources.into("deploy", resources -> {
+ resources.from(gwtCompile.flatMap(GwtCompileOptions::getDeploy));
+ });
+ });
+
+ }
+}
diff --git a/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/tasks/AbstractGwtTask.java b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/tasks/AbstractGwtTask.java
new file mode 100644
index 00000000..af5ec9e0
--- /dev/null
+++ b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/tasks/AbstractGwtTask.java
@@ -0,0 +1,88 @@
+package io.freefair.gradle.plugins.gwt.tasks;
+
+import io.freefair.gradle.plugins.gwt.CommonGwtToolOptions;
+import org.gradle.api.file.ConfigurableFileCollection;
+import org.gradle.api.file.DirectoryProperty;
+import org.gradle.api.provider.ListProperty;
+import org.gradle.api.provider.MapProperty;
+import org.gradle.api.provider.Provider;
+import org.gradle.api.tasks.Classpath;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.JavaExec;
+
+import java.util.Collection;
+
+/**
+ * @author Lars Grefer
+ */
+public abstract class AbstractGwtTask extends JavaExec implements CommonGwtToolOptions {
+
+ @InputFiles
+ @Classpath
+ public abstract ConfigurableFileCollection getGwtClasspath();
+
+ public AbstractGwtTask() {
+ classpath(getGwtClasspath());
+ }
+
+ @Override
+ public void exec() {
+ args(getModule().get());
+ super.exec();
+ }
+
+
+ static void addStringArg(Collection args, String argName, DirectoryProperty property) {
+
+ if (property.isPresent()) {
+ args.add("-" + argName);
+ args.add(property.get().getAsFile().getAbsolutePath());
+ }
+ }
+
+ static void addMapArg(Collection args, String argName, MapProperty property) {
+
+ if (property.isPresent()) {
+ property.get().forEach((key, value) -> {
+ args.add("-" + argName);
+ args.add(key + "=" + value);
+ });
+ }
+ }
+
+ static void addListArg(Collection args, String argName, ListProperty property) {
+
+ if (property.isPresent()) {
+ property.get().forEach(key -> {
+ args.add("-" + argName);
+ args.add(key);
+ });
+ }
+ }
+
+ static void addStringArg(Collection args, String argName, Provider> property) {
+
+ if (property.isPresent()) {
+ args.add("-" + argName);
+ args.add(property.get().toString());
+ }
+ }
+
+ static void addBooleanArg(Collection args, String argName, Provider property) {
+
+ if (property.isPresent()) {
+ if (property.get()) {
+ args.add("-" + argName);
+ }
+ else {
+ if (argName.startsWith("X")) {
+ args.add("-Xno" + argName.substring(1));
+ }
+ else {
+ args.add("-no" + argName);
+ }
+ }
+
+ }
+ }
+}
diff --git a/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/tasks/GwtCodeServerTask.java b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/tasks/GwtCodeServerTask.java
new file mode 100644
index 00000000..91b294ed
--- /dev/null
+++ b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/tasks/GwtCodeServerTask.java
@@ -0,0 +1,53 @@
+package io.freefair.gradle.plugins.gwt.tasks;
+
+import io.freefair.gradle.plugins.gwt.GwtCodeServerOptions;
+import org.gradle.process.CommandLineArgumentProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Lars Grefer
+ */
+public abstract class GwtCodeServerTask extends AbstractGwtTask implements GwtCodeServerOptions {
+
+ public GwtCodeServerTask() {
+ this.getOutputs().upToDateWhen(task -> false);
+ getMainClass().set("com.google.gwt.dev.codeserver.CodeServer");
+
+ getArgumentProviders().add(new ArgProvider());
+ }
+
+
+ class ArgProvider implements CommandLineArgumentProvider {
+
+ @Override
+ public Iterable asArguments() {
+ List args = new ArrayList<>();
+
+ addBooleanArg(args, "allowMissingSrc", getAllowMissingSrc());
+ addBooleanArg(args, "compileTest", getCompileTest());
+ addStringArg(args, "compileTestRecompiles", getCompileTestRecompiles());
+ addBooleanArg(args, "failOnError", getFailOnError());
+ addBooleanArg(args, "precompile", getPrecompile());
+ addStringArg(args, "port", getPort());
+ addStringArg(args, "src", getSrc());
+ addStringArg(args, "workDir", getWorkDir());
+ addStringArg(args, "launcherDir", getLauncherDir());
+ addStringArg(args, "bindAddress", getBindAddress());
+ addStringArg(args, "style", getStyle());
+ addMapArg(args, "setProperty", getSetProperty());
+ addBooleanArg(args, "incremental", getIncremental());
+ addStringArg(args, "sourceLevel", getSourceLevel());
+ addStringArg(args, "logLevel", getLogLevel());
+ addBooleanArg(args, "generateJsInteropExports", getGenerateJsInteropExports());
+ addListArg(args, "includeJsInteropExports", getIncludeJsInteropExports());
+ addListArg(args, "excludeJsInteropExports", getExcludeJsInteropExports());
+ addStringArg(args, "XmethodNameDisplayMode", getXmethodNameDisplayMode());
+ addBooleanArg(args, "XclosureFormattedOutput", getXclosureFormattedOutput());
+
+ return args;
+ }
+ }
+
+}
diff --git a/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/tasks/GwtCompileTask.java b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/tasks/GwtCompileTask.java
new file mode 100644
index 00000000..2a147175
--- /dev/null
+++ b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/tasks/GwtCompileTask.java
@@ -0,0 +1,60 @@
+package io.freefair.gradle.plugins.gwt.tasks;
+
+import io.freefair.gradle.plugins.gwt.GwtCompileOptions;
+import org.gradle.api.tasks.CacheableTask;
+import org.gradle.process.CommandLineArgumentProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Lars Grefer
+ */
+@CacheableTask
+public abstract class GwtCompileTask extends AbstractGwtTask implements GwtCompileOptions {
+
+ public GwtCompileTask() {
+ getMainClass().set("com.google.gwt.dev.Compiler");
+
+ getArgumentProviders().add(new ArgumentProvider());
+ }
+
+ class ArgumentProvider implements CommandLineArgumentProvider {
+ @Override
+ public Iterable asArguments() {
+ List args = new ArrayList<>();
+
+
+ addStringArg(args, "logLevel", getLogLevel());
+ addStringArg(args, "workDir", getWorkDir());
+ addBooleanArg(args, "XclosureFormattedOutput", getXClosureFormattedOutput());
+ addBooleanArg(args, "compileReport", getCompileReport());
+ addBooleanArg(args, "XcheckCasts", getXcheckCasts());
+ addBooleanArg(args, "XclassMetadata", getXclassMetadata());
+ addBooleanArg(args, "draftCompile", getDraftCompile());
+ addBooleanArg(args, "checkAssertions", getCheckAssertions());
+ addStringArg(args, "XfragmentCount", getXfragmentCount());
+ addStringArg(args, "gen", getGen());
+ addBooleanArg(args, "generateJsInteropExports", getGenerateJsInteropExports());
+ addListArg(args, "includeJsInteropExports", getIncludeJsInteropExports());
+ addListArg(args, "excludeJsInteropExports", getExcludeJsInteropExports());
+ addStringArg(args, "XmethodNameDisplayMode", getXmethodNameDisplayMode());
+ addStringArg(args, "Xnamespace", getXnamespace());
+ addStringArg(args, "optimize", getOptimize());
+ addStringArg(args, "saveSource", getSaveSource());
+ addMapArg(args, "setProperty", getSetProperty());
+ addStringArg(args, "style", getStyle());
+ addBooleanArg(args, "failOnError", getFailOnError());
+ addBooleanArg(args, "validateOnly", getValidateOnly());
+ addStringArg(args, "sourceLevel", getSourceLevel());
+ addStringArg(args, "localWorkers", getLocalWorkers());
+ addBooleanArg(args, "incremental", getIncremental());
+ addStringArg(args, "war", getWar());
+ addStringArg(args, "deploy", getDeploy());
+ addStringArg(args, "extra", getExtra());
+ addStringArg(args, "saveSourceOutput", getSaveSourceOutput());
+
+ return args;
+ }
+ }
+}
diff --git a/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/tasks/GwtDevModeTask.java b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/tasks/GwtDevModeTask.java
new file mode 100644
index 00000000..86f29df0
--- /dev/null
+++ b/gwt-plugin/src/main/java/io/freefair/gradle/plugins/gwt/tasks/GwtDevModeTask.java
@@ -0,0 +1,54 @@
+package io.freefair.gradle.plugins.gwt.tasks;
+
+import io.freefair.gradle.plugins.gwt.GwtDevModeOptions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Lars Grefer
+ */
+public abstract class GwtDevModeTask extends AbstractGwtTask implements GwtDevModeOptions {
+
+ public GwtDevModeTask() {
+ this.getOutputs().upToDateWhen(task -> false);
+
+ getMainClass().convention("com.google.gwt.dev.DevMode");
+
+ getArgumentProviders().add(new ArgumentProvider());
+ }
+
+ class ArgumentProvider implements org.gradle.process.CommandLineArgumentProvider {
+ @Override
+ public Iterable asArguments() {
+ List args = new ArrayList<>();
+
+ addBooleanArg(args, "startServer", getStartServer());
+ addStringArg(args, "port", getPort());
+ addStringArg(args, "logdir", getLogdir());
+ addStringArg(args, "logLevel", getLogLevel());
+ addStringArg(args, "gen", getGen());
+ addStringArg(args, "bindAddress", getBindAddress());
+ addStringArg(args, "codeServerPort", getCodeServerPort());
+ addBooleanArg(args, "superDevMode", getSuperDevMode());
+ addStringArg(args, "server", getServer());
+ addStringArg(args, "startupUrl", getStartupUrl());
+ addStringArg(args, "war", getWar());
+ addStringArg(args, "deploy", getDeploy());
+ addStringArg(args, "extra", getExtra());
+ addStringArg(args, "modulePathPrefix", getModulePathPrefix());
+ addStringArg(args, "workDir", getWorkDir());
+ addStringArg(args, "XmethodNameDisplayMode", getXmethodNameDisplayMode());
+ addStringArg(args, "sourceLevel", getSourceLevel());
+ addBooleanArg(args, "generateJsInteropExports", getGenerateJsInteropExports());
+ addListArg(args, "includeJsInteropExports", getIncludeJsInteropExports());
+ addListArg(args, "excludeJsInteropExports", getExcludeJsInteropExports());
+ addBooleanArg(args, "incremental", getIncremental());
+ addStringArg(args, "style", getStyle());
+ addBooleanArg(args, "failOnError", getFailOnError());
+ addMapArg(args, "setProperty", getSetProperty());
+
+ return args;
+ }
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index 14f5f675..22c95eb6 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -61,6 +61,7 @@ include "git-plugin"
include "mkdocs-plugin"
include "quicktype-plugin"
include "plantuml-plugin"
+include "gwt-plugin"
include "mjml-plugin"