mirror of
https://github.com/ethauvin/rife2-hello.git
synced 2025-04-29 08:58:12 -07:00
Compare commits
20 commits
f8b3078ba1
...
938372addc
Author | SHA1 | Date | |
---|---|---|---|
938372addc | |||
a3a5c7c380 | |||
b51e36ee73 | |||
83e52c0e06 | |||
|
d06124c26d | ||
|
a79e616d79 | ||
de5c974f15 | |||
0a2b061679 | |||
|
21c85ea93b | ||
cf4870745c | |||
5ca1fa305b | |||
d8c41b45d9 | |||
|
c9f286132c | ||
|
65e579966c | ||
86b7ec21d9 | |||
aeaadfb1cc | |||
ed8046e83d | |||
c8f47a935e | |||
d3dedfe189 | |||
a54ab9b23e |
26 changed files with 701 additions and 155 deletions
15
README.md
15
README.md
|
@ -92,18 +92,23 @@ GraalVM supports creating a single Ahead-Of-Time
|
||||||
[native executable](https://www.graalvm.org/native-image/) from your java
|
[native executable](https://www.graalvm.org/native-image/) from your java
|
||||||
bytecode.
|
bytecode.
|
||||||
|
|
||||||
Once you have at least GraalVM 22.3.1 Java 19 installed, you can generate the
|
Once you have at least GraalVM 22.3.1 Java 17 installed, you can generate the native binary with:
|
||||||
UberJar as above, then create your native binary as such:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
native-image --no-fallback --enable-preview -jar app/build/libs/hello-uber-1.0.jar
|
./gradlew nativeCompile
|
||||||
```
|
```
|
||||||
|
|
||||||
You'll end up with a `hello-uber-1.0` file that can be executed directly without
|
You'll end up with a `hello-1.0` file that can be executed directly without
|
||||||
the need of a JVM:
|
the need of a JVM:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./hello-uber-1.0
|
./app/build/native/nativeCompile/hello-1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can run the native executable directly with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew nativeRun
|
||||||
```
|
```
|
||||||
|
|
||||||
> **NOTE:** RIFE2 support for GraalVM native-image is still in preliminary
|
> **NOTE:** RIFE2 support for GraalVM native-image is still in preliminary
|
||||||
|
|
|
@ -1,16 +1,22 @@
|
||||||
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
|
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
|
||||||
import org.gradle.api.tasks.testing.logging.TestLogEvent
|
import org.gradle.api.tasks.testing.logging.TestLogEvent
|
||||||
|
import com.uwyn.rife2.gradle.TemplateType.*
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
|
application
|
||||||
id("com.uwyn.rife2")
|
id("com.uwyn.rife2")
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
|
id("org.graalvm.buildtools.native") version "0.9.20"
|
||||||
}
|
}
|
||||||
|
|
||||||
version = 1.0
|
|
||||||
group = "com.example"
|
|
||||||
|
|
||||||
base {
|
base {
|
||||||
archivesName.set("hello")
|
archivesName.set("hello")
|
||||||
|
version = 1.0
|
||||||
|
group = "com.example"
|
||||||
|
}
|
||||||
|
|
||||||
|
application {
|
||||||
|
mainClass.set("hello.App")
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
|
@ -26,9 +32,9 @@ repositories {
|
||||||
}
|
}
|
||||||
|
|
||||||
rife2 {
|
rife2 {
|
||||||
mainClass.set("hello.App")
|
version.set("1.4.0")
|
||||||
version.set("1.3.0")
|
|
||||||
useAgent.set(true)
|
useAgent.set(true)
|
||||||
|
precompiledTemplateTypes.add(HTML)
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -63,3 +69,8 @@ publishing {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
graalvmNative.binaries.all {
|
||||||
|
buildArgs.add("--enable-preview") // support for Jetty virtual threads with JDK 19
|
||||||
|
imageName.set("hello-$version")
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name":"rife.template.html.hello",
|
"name":"rife.template.html.hello",
|
||||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
plugins {
|
plugins {
|
||||||
`java-gradle-plugin`
|
`java-gradle-plugin`
|
||||||
|
groovy
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -8,13 +9,8 @@ repositories {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
gradleApi()
|
gradleApi()
|
||||||
}
|
testImplementation(libs.spock.core)
|
||||||
|
testImplementation(gradleTestKit())
|
||||||
tasks {
|
|
||||||
withType<JavaCompile> {
|
|
||||||
options.isDeprecation = true
|
|
||||||
options.compilerArgs.add("-Xlint:unchecked")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gradlePlugin {
|
gradlePlugin {
|
||||||
|
@ -25,3 +21,11 @@ gradlePlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.withType<Test>().configureEach {
|
||||||
|
useJUnitPlatform()
|
||||||
|
testLogging {
|
||||||
|
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||||
|
events = setOf(org.gradle.api.tasks.testing.logging.TestLogEvent.PASSED, org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED, org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,9 @@
|
||||||
rootProject.name = "build-logic"
|
rootProject.name = "build-logic"
|
||||||
|
|
||||||
|
dependencyResolutionManagement {
|
||||||
|
versionCatalogs {
|
||||||
|
create("libs") {
|
||||||
|
from(files("../gradle/libs.versions.toml"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,11 +18,12 @@ package com.uwyn.rife2.gradle;
|
||||||
import org.gradle.api.DefaultTask;
|
import org.gradle.api.DefaultTask;
|
||||||
import org.gradle.api.file.ConfigurableFileCollection;
|
import org.gradle.api.file.ConfigurableFileCollection;
|
||||||
import org.gradle.api.file.DirectoryProperty;
|
import org.gradle.api.file.DirectoryProperty;
|
||||||
|
import org.gradle.api.provider.ListProperty;
|
||||||
import org.gradle.api.provider.Property;
|
import org.gradle.api.provider.Property;
|
||||||
import org.gradle.api.tasks.CacheableTask;
|
import org.gradle.api.tasks.CacheableTask;
|
||||||
import org.gradle.api.tasks.Classpath;
|
import org.gradle.api.tasks.Classpath;
|
||||||
import org.gradle.api.tasks.Input;
|
import org.gradle.api.tasks.Input;
|
||||||
import org.gradle.api.tasks.InputDirectory;
|
import org.gradle.api.tasks.InputFiles;
|
||||||
import org.gradle.api.tasks.Optional;
|
import org.gradle.api.tasks.Optional;
|
||||||
import org.gradle.api.tasks.OutputDirectory;
|
import org.gradle.api.tasks.OutputDirectory;
|
||||||
import org.gradle.api.tasks.PathSensitive;
|
import org.gradle.api.tasks.PathSensitive;
|
||||||
|
@ -31,6 +32,7 @@ import org.gradle.api.tasks.TaskAction;
|
||||||
import org.gradle.process.ExecOperations;
|
import org.gradle.process.ExecOperations;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -40,12 +42,12 @@ public abstract class PrecompileTemplates extends DefaultTask {
|
||||||
@Classpath
|
@Classpath
|
||||||
public abstract ConfigurableFileCollection getClasspath();
|
public abstract ConfigurableFileCollection getClasspath();
|
||||||
|
|
||||||
@InputDirectory
|
@InputFiles
|
||||||
@PathSensitive(PathSensitivity.RELATIVE)
|
@PathSensitive(PathSensitivity.RELATIVE)
|
||||||
public abstract DirectoryProperty getTemplatesDirectory();
|
public abstract ConfigurableFileCollection getTemplatesDirectories();
|
||||||
|
|
||||||
@Input
|
@Input
|
||||||
public abstract Property<String> getType();
|
public abstract ListProperty<TemplateType> getTypes();
|
||||||
|
|
||||||
@Input
|
@Input
|
||||||
@Optional
|
@Optional
|
||||||
|
@ -63,6 +65,9 @@ public abstract class PrecompileTemplates extends DefaultTask {
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
public void precompileTemplates() {
|
public void precompileTemplates() {
|
||||||
|
for (var type : getTypes().get()) {
|
||||||
|
getTemplatesDirectories().getFiles().forEach(dir -> {
|
||||||
|
if (Files.exists(dir.toPath())) {
|
||||||
getExecOperations().javaexec(javaexec -> {
|
getExecOperations().javaexec(javaexec -> {
|
||||||
javaexec.setClasspath(getClasspath());
|
javaexec.setClasspath(getClasspath());
|
||||||
javaexec.getMainClass().set("rife.template.TemplateDeployer");
|
javaexec.getMainClass().set("rife.template.TemplateDeployer");
|
||||||
|
@ -71,13 +76,17 @@ public abstract class PrecompileTemplates extends DefaultTask {
|
||||||
args.add("-verbose");
|
args.add("-verbose");
|
||||||
}
|
}
|
||||||
args.add("-t");
|
args.add("-t");
|
||||||
args.add(getType().get());
|
args.add(type.identifier());
|
||||||
args.add("-d");
|
args.add("-d");
|
||||||
args.add(getOutputDirectory().get().getAsFile().getPath());
|
args.add(getOutputDirectory().get().getAsFile().getPath());
|
||||||
args.add("-encoding");
|
args.add("-encoding");
|
||||||
args.add(getEncoding().orElse("UTF-8").get());
|
args.add(getEncoding().orElse("UTF-8").get());
|
||||||
args.add(getTemplatesDirectory().get().getAsFile().getPath());
|
args.add(dir.getPath());
|
||||||
javaexec.args(args);
|
javaexec.args(args);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,15 +15,18 @@
|
||||||
*/
|
*/
|
||||||
package com.uwyn.rife2.gradle;
|
package com.uwyn.rife2.gradle;
|
||||||
|
|
||||||
|
import org.gradle.api.file.ConfigurableFileCollection;
|
||||||
|
import org.gradle.api.provider.ListProperty;
|
||||||
import org.gradle.api.provider.Property;
|
import org.gradle.api.provider.Property;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public abstract class Rife2Extension {
|
public abstract class Rife2Extension {
|
||||||
public abstract Property<String> getMainClass();
|
|
||||||
|
|
||||||
public abstract Property<String> getVersion();
|
public abstract Property<String> getVersion();
|
||||||
|
|
||||||
public abstract Property<Boolean> getUseAgent();
|
public abstract Property<Boolean> getUseAgent();
|
||||||
|
|
||||||
public abstract Property<String> getUberMainClass();
|
public abstract Property<String> getUberMainClass();
|
||||||
|
|
||||||
|
public abstract ListProperty<TemplateType> getPrecompiledTemplateTypes();
|
||||||
|
|
||||||
|
public abstract ConfigurableFileCollection getTemplateDirectories();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,29 +21,35 @@ import org.gradle.api.artifacts.Configuration;
|
||||||
import org.gradle.api.artifacts.ConfigurationContainer;
|
import org.gradle.api.artifacts.ConfigurationContainer;
|
||||||
import org.gradle.api.artifacts.dsl.DependencyHandler;
|
import org.gradle.api.artifacts.dsl.DependencyHandler;
|
||||||
import org.gradle.api.attributes.Attribute;
|
import org.gradle.api.attributes.Attribute;
|
||||||
import org.gradle.api.attributes.AttributeContainer;
|
|
||||||
import org.gradle.api.attributes.Bundling;
|
import org.gradle.api.attributes.Bundling;
|
||||||
import org.gradle.api.component.AdhocComponentWithVariants;
|
import org.gradle.api.component.AdhocComponentWithVariants;
|
||||||
import org.gradle.api.component.ConfigurationVariantDetails;
|
import org.gradle.api.component.ConfigurationVariantDetails;
|
||||||
|
import org.gradle.api.file.ConfigurableFileCollection;
|
||||||
import org.gradle.api.file.DuplicatesStrategy;
|
import org.gradle.api.file.DuplicatesStrategy;
|
||||||
import org.gradle.api.plugins.BasePluginExtension;
|
import org.gradle.api.plugins.BasePluginExtension;
|
||||||
|
import org.gradle.api.plugins.JavaApplication;
|
||||||
import org.gradle.api.plugins.JavaPlugin;
|
import org.gradle.api.plugins.JavaPlugin;
|
||||||
import org.gradle.api.plugins.JavaPluginExtension;
|
import org.gradle.api.plugins.JavaPluginExtension;
|
||||||
import org.gradle.api.plugins.PluginContainer;
|
import org.gradle.api.plugins.PluginContainer;
|
||||||
import org.gradle.api.tasks.*;
|
import org.gradle.api.tasks.JavaExec;
|
||||||
|
import org.gradle.api.tasks.SourceSet;
|
||||||
|
import org.gradle.api.tasks.TaskContainer;
|
||||||
|
import org.gradle.api.tasks.TaskProvider;
|
||||||
import org.gradle.api.tasks.bundling.Jar;
|
import org.gradle.api.tasks.bundling.Jar;
|
||||||
import org.gradle.api.tasks.testing.Test;
|
import org.gradle.api.tasks.testing.Test;
|
||||||
import org.gradle.process.CommandLineArgumentProvider;
|
import org.gradle.process.CommandLineArgumentProvider;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@SuppressWarnings({"ALL", "unused"})
|
|
||||||
public class Rife2Plugin implements Plugin<Project> {
|
public class Rife2Plugin implements Plugin<Project> {
|
||||||
public static final String DEFAULT_TEMPLATES_DIR = "src/main/templates";
|
public static final List<String> DEFAULT_TEMPLATES_DIRS = List.of("src/main/resources/templates");
|
||||||
public static final String DEFAULT_GENERATED_RIFE2_CLASSES_DIR = "generated/classes/rife2";
|
public static final String DEFAULT_GENERATED_RIFE2_CLASSES_DIR = "generated/classes/rife2";
|
||||||
public static final String RIFE2_GROUP = "rife2";
|
public static final String RIFE2_GROUP = "rife2";
|
||||||
public static final String WEBAPP_SRCDIR = "src/main/webapp";
|
public static final String WEBAPP_SRCDIR = "src/main/webapp";
|
||||||
|
public static final String PRECOMPILE_TEMPLATES_TASK_NAME = "precompileTemplates";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Project project) {
|
public void apply(Project project) {
|
||||||
|
@ -54,67 +60,88 @@ public class Rife2Plugin implements Plugin<Project> {
|
||||||
var configurations = project.getConfigurations();
|
var configurations = project.getConfigurations();
|
||||||
var dependencyHandler = project.getDependencies();
|
var dependencyHandler = project.getDependencies();
|
||||||
var tasks = project.getTasks();
|
var tasks = project.getTasks();
|
||||||
|
|
||||||
var rife2Configuration = createRife2Configuration(configurations, dependencyHandler, rife2Extension);
|
var rife2Configuration = createRife2Configuration(configurations, dependencyHandler, rife2Extension);
|
||||||
var rife2CompilerClasspath = createRife2CompilerClasspathConfiguration(configurations, rife2Configuration);
|
var rife2CompilerClasspath = createRife2CompilerClasspathConfiguration(configurations, rife2Configuration);
|
||||||
var rife2AgentClasspath = createRife2AgentConfiguration(configurations, dependencyHandler, rife2Extension);
|
var rife2AgentClasspath = createRife2AgentConfiguration(configurations, dependencyHandler, rife2Extension);
|
||||||
configurations.getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME).extendsFrom(rife2Configuration);
|
configurations.getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME).extendsFrom(rife2Configuration);
|
||||||
var precompileTemplates = registerPrecompileTemplateTask(project, rife2CompilerClasspath);
|
|
||||||
createRife2DevelopmentOnlyConfiguration(project, configurations, dependencyHandler, precompileTemplates);
|
var precompileTemplates = registerPrecompileTemplateTask(project, rife2CompilerClasspath, rife2Extension);
|
||||||
|
createRife2DevelopmentOnlyConfiguration(project, configurations, dependencyHandler, rife2Extension.getTemplateDirectories());
|
||||||
exposePrecompiledTemplatesToTestTask(project, configurations, dependencyHandler, precompileTemplates);
|
exposePrecompiledTemplatesToTestTask(project, configurations, dependencyHandler, precompileTemplates);
|
||||||
configureAgent(project, plugins, rife2Extension, rife2AgentClasspath);
|
configureAgent(project, plugins, rife2Extension, rife2AgentClasspath);
|
||||||
registerRunTask(project, rife2Extension, rife2AgentClasspath);
|
|
||||||
TaskProvider<Jar> uberJarTask = registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileTemplates);
|
TaskProvider<Jar> uberJarTask = registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileTemplates);
|
||||||
bundlePrecompiledTemplatesIntoJarFile(tasks, precompileTemplates);
|
bundlePrecompiledTemplatesIntoJarFile(tasks, precompileTemplates, rife2Extension);
|
||||||
|
|
||||||
configureMavenPublishing(project, plugins, configurations, uberJarTask);
|
configureMavenPublishing(project, plugins, configurations, uberJarTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void configureMavenPublishing(Project project, PluginContainer plugins, ConfigurationContainer configurations, TaskProvider<Jar> uberJarTask) {
|
@SuppressWarnings("unchecked")
|
||||||
|
private static void configureMavenPublishing(Project project,
|
||||||
|
PluginContainer plugins,
|
||||||
|
ConfigurationContainer configurations,
|
||||||
|
TaskProvider<Jar> uberJarTask) {
|
||||||
plugins.withId("maven-publish", unused -> {
|
plugins.withId("maven-publish", unused -> {
|
||||||
Configuration rife2UberJarElements = configurations.create("rife2UberJarElements", conf -> {
|
var rife2UberJarElements = configurations.create("rife2UberJarElements", conf -> {
|
||||||
conf.setDescription("Exposes the uber jar archive of the RIFE2 web application.");
|
conf.setDescription("Exposes the uber jar archive of the RIFE2 web application.");
|
||||||
conf.setCanBeResolved(false);
|
conf.setCanBeResolved(false);
|
||||||
conf.setCanBeConsumed(true);
|
conf.setCanBeConsumed(true);
|
||||||
conf.getOutgoing().artifact(uberJarTask, artifact -> artifact.setClassifier("uber"));
|
conf.getOutgoing().artifact(uberJarTask, artifact -> artifact.setClassifier("uber"));
|
||||||
AttributeContainer runtimeAttributes = configurations.getByName(JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME).getAttributes();
|
|
||||||
|
var runtimeAttributes = configurations.getByName(JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME).getAttributes();
|
||||||
conf.attributes(attrs -> {
|
conf.attributes(attrs -> {
|
||||||
for (Attribute<?> attribute : runtimeAttributes.keySet()) {
|
for (Attribute<?> attribute : runtimeAttributes.keySet()) {
|
||||||
Object value = runtimeAttributes.getAttribute(attribute);
|
Object value = runtimeAttributes.getAttribute(attribute);
|
||||||
if (value != null) {
|
//noinspection unchecked
|
||||||
if (Bundling.class.equals(attribute.getType())) {
|
if (Bundling.class.equals(attribute.getType())) {
|
||||||
attrs.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.SHADOWED));
|
attrs.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.SHADOWED));
|
||||||
} else {
|
} else {
|
||||||
//noinspection unchecked
|
|
||||||
attrs.attribute((Attribute<Object>) attribute, value);
|
attrs.attribute((Attribute<Object>) attribute, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
AdhocComponentWithVariants component = (AdhocComponentWithVariants) project.getComponents().getByName("java");
|
|
||||||
|
var component = (AdhocComponentWithVariants) project.getComponents().getByName("java");
|
||||||
component.addVariantsFromConfiguration(rife2UberJarElements, ConfigurationVariantDetails::mapToOptional);
|
component.addVariantsFromConfiguration(rife2UberJarElements, ConfigurationVariantDetails::mapToOptional);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void exposePrecompiledTemplatesToTestTask(Project project, ConfigurationContainer configurations, DependencyHandler dependencyHandler, TaskProvider<PrecompileTemplates> precompileTemplates) {
|
private static void exposePrecompiledTemplatesToTestTask(Project project,
|
||||||
|
ConfigurationContainer configurations,
|
||||||
|
DependencyHandler dependencyHandler,
|
||||||
|
TaskProvider<PrecompileTemplates> precompileTemplatesTask) {
|
||||||
configurations.getByName(JavaPlugin.TEST_RUNTIME_ONLY_CONFIGURATION_NAME)
|
configurations.getByName(JavaPlugin.TEST_RUNTIME_ONLY_CONFIGURATION_NAME)
|
||||||
.getDependencies()
|
.getDependencies()
|
||||||
.add(dependencyHandler.create(project.files(precompileTemplates)));
|
.add(dependencyHandler.create(project.files(precompileTemplatesTask)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void bundlePrecompiledTemplatesIntoJarFile(TaskContainer tasks, TaskProvider<PrecompileTemplates> precompileTemplates) {
|
private static void bundlePrecompiledTemplatesIntoJarFile(TaskContainer tasks,
|
||||||
tasks.named("jar", Jar.class, jar -> jar.from(precompileTemplates));
|
TaskProvider<PrecompileTemplates> precompileTemplatesTask,
|
||||||
|
Rife2Extension rife2Extension) {
|
||||||
|
tasks.named("jar", Jar.class, jar -> {
|
||||||
|
jar.from(precompileTemplatesTask);
|
||||||
|
// This isn't great because it needs to be hardcoded, in order to avoid the templates
|
||||||
|
// declared in `src/main/resources/templates` to be included in the jar file.
|
||||||
|
// which means that if for whatever reason the user also uses the same directory for
|
||||||
|
// something else, it will be excluded from the jar file.
|
||||||
|
rife2Extension.getPrecompiledTemplateTypes().get().forEach(templateType -> jar.exclude("/templates/**." + templateType.identifier().toLowerCase())
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createRife2DevelopmentOnlyConfiguration(Project project,
|
private void createRife2DevelopmentOnlyConfiguration(Project project,
|
||||||
ConfigurationContainer configurations,
|
ConfigurationContainer configurations,
|
||||||
DependencyHandler dependencies,
|
DependencyHandler dependencies,
|
||||||
TaskProvider<PrecompileTemplates> precompileTemplatesTask) {
|
ConfigurableFileCollection templateDirectories) {
|
||||||
Configuration rife2DevelopmentOnly = configurations.create("rife2DevelopmentOnly", conf -> {
|
var rife2DevelopmentOnly = configurations.create("rife2DevelopmentOnly", conf -> {
|
||||||
conf.setDescription("Dependencies which should only be visible when running the application in development mode (and not in tests).");
|
conf.setDescription("Dependencies which should only be visible when running the application in development mode (and not in tests).");
|
||||||
conf.setCanBeConsumed(false);
|
conf.setCanBeConsumed(false);
|
||||||
conf.setCanBeResolved(false);
|
conf.setCanBeResolved(false);
|
||||||
});
|
});
|
||||||
rife2DevelopmentOnly.getDependencies().add(dependencies.create(project.files(precompileTemplatesTask)));
|
rife2DevelopmentOnly.getDependencies().addAllLater(templateDirectories.getElements().map(locations ->
|
||||||
|
locations.stream().map(fs -> dependencies.create(project.files(fs))).collect(Collectors.toList()))
|
||||||
|
);
|
||||||
configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(rife2DevelopmentOnly);
|
configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(rife2DevelopmentOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,21 +157,29 @@ public class Rife2Plugin implements Plugin<Project> {
|
||||||
var base = project.getExtensions().getByType(BasePluginExtension.class);
|
var base = project.getExtensions().getByType(BasePluginExtension.class);
|
||||||
jar.getArchiveBaseName().convention(project.provider(() -> base.getArchivesName().get() + "-uber"));
|
jar.getArchiveBaseName().convention(project.provider(() -> base.getArchivesName().get() + "-uber"));
|
||||||
jar.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE);
|
jar.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE);
|
||||||
var runtimeClasspath = project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
|
||||||
jar.from(javaPluginExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput());
|
jar.from(javaPluginExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput());
|
||||||
jar.from(precompileTemplatesTask);
|
jar.from(precompileTemplatesTask);
|
||||||
jar.into("webapp", spec -> spec.from(WEBAPP_SRCDIR));
|
jar.into("webapp", spec -> spec.from(WEBAPP_SRCDIR));
|
||||||
|
var runtimeClasspath = project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||||
jar.from(runtimeClasspath.getElements().map(e -> e.stream()
|
jar.from(runtimeClasspath.getElements().map(e -> e.stream()
|
||||||
.filter(f -> f.getAsFile().getName().toLowerCase(Locale.ENGLISH).endsWith(".jar"))
|
.filter(f -> f.getAsFile().getName().toLowerCase(Locale.ENGLISH).endsWith(".jar"))
|
||||||
.map(project::zipTree)
|
.map(project::zipTree)
|
||||||
.toList()));
|
.toList()));
|
||||||
|
// This isn't great because it needs to be hardcoded, in order to avoid the templates
|
||||||
|
// declared in `src/main/resources/templates` to be included in the jar file.
|
||||||
|
// which means that if for whatever reason the user also uses the same directory for
|
||||||
|
// something else, it will be excluded from the jar file.
|
||||||
|
jar.exclude("templates");
|
||||||
plugins.withId("application", unused -> jar.manifest(manifest ->
|
plugins.withId("application", unused -> jar.manifest(manifest ->
|
||||||
manifest.getAttributes().put("Main-Class", rife2Extension.getUberMainClass().get()))
|
manifest.getAttributes().put("Main-Class", rife2Extension.getUberMainClass().get()))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void configureAgent(Project project, PluginContainer plugins, Rife2Extension rife2Extension, Configuration rife2AgentClasspath) {
|
private static void configureAgent(Project project,
|
||||||
|
PluginContainer plugins,
|
||||||
|
Rife2Extension rife2Extension,
|
||||||
|
Configuration rife2AgentClasspath) {
|
||||||
CommandLineArgumentProvider agentProvider = () -> {
|
CommandLineArgumentProvider agentProvider = () -> {
|
||||||
if (Boolean.TRUE.equals(rife2Extension.getUseAgent().get())) {
|
if (Boolean.TRUE.equals(rife2Extension.getUseAgent().get())) {
|
||||||
return Collections.singleton("-javaagent:" + rife2AgentClasspath.getAsPath());
|
return Collections.singleton("-javaagent:" + rife2AgentClasspath.getAsPath());
|
||||||
|
@ -158,11 +193,14 @@ public class Rife2Plugin implements Plugin<Project> {
|
||||||
private static Rife2Extension createRife2Extension(Project project) {
|
private static Rife2Extension createRife2Extension(Project project) {
|
||||||
var rife2 = project.getExtensions().create("rife2", Rife2Extension.class);
|
var rife2 = project.getExtensions().create("rife2", Rife2Extension.class);
|
||||||
rife2.getUseAgent().convention(false);
|
rife2.getUseAgent().convention(false);
|
||||||
rife2.getUberMainClass().set(rife2.getMainClass() + "Uber");
|
rife2.getUberMainClass().convention(project.getExtensions().getByType(JavaApplication.class).getMainClass()
|
||||||
|
.map(mainClass -> mainClass + "Uber"));
|
||||||
|
DEFAULT_TEMPLATES_DIRS.stream().forEachOrdered(dir -> rife2.getTemplateDirectories().from(project.files(dir)));
|
||||||
return rife2;
|
return rife2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Configuration createRife2CompilerClasspathConfiguration(ConfigurationContainer configurations, Configuration rife2Configuration) {
|
private static Configuration createRife2CompilerClasspathConfiguration(ConfigurationContainer configurations,
|
||||||
|
Configuration rife2Configuration) {
|
||||||
return configurations.create("rife2CompilerClasspath", conf -> {
|
return configurations.create("rife2CompilerClasspath", conf -> {
|
||||||
conf.setDescription("The RIFE2 compiler classpath");
|
conf.setDescription("The RIFE2 compiler classpath");
|
||||||
conf.setCanBeConsumed(false);
|
conf.setCanBeConsumed(false);
|
||||||
|
@ -198,28 +236,16 @@ public class Rife2Plugin implements Plugin<Project> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TaskProvider<PrecompileTemplates> registerPrecompileTemplateTask(Project project,
|
private static TaskProvider<PrecompileTemplates> registerPrecompileTemplateTask(Project project,
|
||||||
Configuration rife2CompilerClasspath) {
|
Configuration rife2CompilerClasspath,
|
||||||
return project.getTasks().register("precompileTemplates", PrecompileTemplates.class, task -> {
|
Rife2Extension rife2Extension) {
|
||||||
|
return project.getTasks().register(PRECOMPILE_TEMPLATES_TASK_NAME, PrecompileTemplates.class, task -> {
|
||||||
task.setGroup(RIFE2_GROUP);
|
task.setGroup(RIFE2_GROUP);
|
||||||
task.setDescription("Pre-compiles the templates.");
|
task.setDescription("Pre-compiles the templates.");
|
||||||
task.getVerbose().convention(true);
|
task.getVerbose().convention(true);
|
||||||
task.getClasspath().from(rife2CompilerClasspath);
|
task.getClasspath().from(rife2CompilerClasspath);
|
||||||
task.getType().convention("html");
|
task.getTypes().convention(rife2Extension.getPrecompiledTemplateTypes());
|
||||||
task.getTemplatesDirectory().set(project.getLayout().getProjectDirectory().dir(DEFAULT_TEMPLATES_DIR));
|
task.getTemplatesDirectories().from(rife2Extension.getTemplateDirectories());
|
||||||
task.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir(DEFAULT_GENERATED_RIFE2_CLASSES_DIR));
|
task.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir(DEFAULT_GENERATED_RIFE2_CLASSES_DIR));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerRunTask(Project project, Rife2Extension rife2Extension,
|
|
||||||
Configuration rife2CompilerClasspath) {
|
|
||||||
project.getTasks().register("run", RunTask.class, task -> {
|
|
||||||
task.setGroup(RIFE2_GROUP);
|
|
||||||
task.setDescription("Runs this project as a web application.");
|
|
||||||
task.getAgentClassPath().set(rife2CompilerClasspath.getAsPath());
|
|
||||||
task.getClasspath().from(project.getExtensions().getByType(SourceSetContainer.class)
|
|
||||||
.getByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath());
|
|
||||||
task.getMainClass().set(rife2Extension.getMainClass());
|
|
||||||
task.getTemplatesDirectory().set(project.getLayout().getProjectDirectory().dir(DEFAULT_TEMPLATES_DIR));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2003-2021 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.uwyn.rife2.gradle;
|
|
||||||
|
|
||||||
import org.gradle.api.DefaultTask;
|
|
||||||
import org.gradle.api.file.ConfigurableFileCollection;
|
|
||||||
import org.gradle.api.file.DirectoryProperty;
|
|
||||||
import org.gradle.api.provider.Property;
|
|
||||||
import org.gradle.api.tasks.*;
|
|
||||||
import org.gradle.process.ExecOperations;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@CacheableTask
|
|
||||||
public abstract class RunTask extends DefaultTask {
|
|
||||||
@Input
|
|
||||||
public abstract Property<String> getAgentClassPath();
|
|
||||||
|
|
||||||
@Classpath
|
|
||||||
public abstract ConfigurableFileCollection getClasspath();
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
protected abstract ExecOperations getExecOperations();
|
|
||||||
|
|
||||||
@Input
|
|
||||||
public abstract Property<String> getMainClass();
|
|
||||||
|
|
||||||
@InputDirectory
|
|
||||||
@PathSensitive(PathSensitivity.RELATIVE)
|
|
||||||
public abstract DirectoryProperty getTemplatesDirectory();
|
|
||||||
|
|
||||||
@TaskAction
|
|
||||||
public void run() {
|
|
||||||
getExecOperations().javaexec(run -> {
|
|
||||||
run.setClasspath(getProject().getObjects().fileCollection().from(getTemplatesDirectory()).plus(getClasspath()));
|
|
||||||
run.getMainClass().set(getMainClass());
|
|
||||||
run.args(List.of("-javaagent:" + getAgentClassPath().get()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.uwyn.rife2.gradle;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class TemplateType implements Serializable {
|
||||||
|
@Serial private static final long serialVersionUID = -2736320275307140837L;
|
||||||
|
|
||||||
|
public static TemplateType HTML = new TemplateType("html");
|
||||||
|
public static TemplateType JSON = new TemplateType("json");
|
||||||
|
public static TemplateType SVG = new TemplateType("svg");
|
||||||
|
public static TemplateType XML = new TemplateType("xml");
|
||||||
|
public static TemplateType TXT = new TemplateType("txt");
|
||||||
|
public static TemplateType SQL = new TemplateType("sql");
|
||||||
|
|
||||||
|
private final String identifier_;
|
||||||
|
|
||||||
|
public TemplateType(String identifier) {
|
||||||
|
identifier_ = identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String identifier() {
|
||||||
|
return identifier_;
|
||||||
|
}
|
||||||
|
}
|
42
build-logic/src/test-projects/minimal/build.gradle
Normal file
42
build-logic/src/test-projects/minimal/build.gradle
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import com.uwyn.rife2.gradle.TemplateType
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("application")
|
||||||
|
id("com.uwyn.rife2")
|
||||||
|
}
|
||||||
|
|
||||||
|
base {
|
||||||
|
archivesName = "hello"
|
||||||
|
version = 1.0
|
||||||
|
group = "com.example"
|
||||||
|
}
|
||||||
|
|
||||||
|
application {
|
||||||
|
mainClass = "hello.App"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(17)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
rife2 {
|
||||||
|
version = "1.4.0"
|
||||||
|
useAgent = true
|
||||||
|
precompiledTemplateTypes.add(TemplateType.HTML)
|
||||||
|
templateDirectories.from(file("src/main/templates"))
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
runtimeOnly("org.eclipse.jetty:jetty-server:11.0.13")
|
||||||
|
runtimeOnly("org.eclipse.jetty:jetty-servlet:11.0.13")
|
||||||
|
runtimeOnly("org.slf4j:slf4j-simple:2.0.5")
|
||||||
|
|
||||||
|
testImplementation("org.jsoup:jsoup:1.15.3")
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
||||||
|
}
|
1
build-logic/src/test-projects/minimal/settings.gradle
Normal file
1
build-logic/src/test-projects/minimal/settings.gradle
Normal file
|
@ -0,0 +1 @@
|
||||||
|
rootProject.name = 'minimal'
|
|
@ -0,0 +1,16 @@
|
||||||
|
package hello;
|
||||||
|
|
||||||
|
import rife.engine.*;
|
||||||
|
|
||||||
|
public class App extends Site {
|
||||||
|
public void setup() {
|
||||||
|
var hello = get("/hello", c -> c.print(c.template("hello")));
|
||||||
|
get("/", c -> c.redirect(hello));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new Server()
|
||||||
|
.staticResourceBase("src/main/webapp")
|
||||||
|
.start(new App());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title><!--v title-->Hello<!--/v--></title>
|
||||||
|
<link rel="stylesheet" href="{{v webapp:rootUrl/}}css/style.css?{{v context:paramRandom/}}">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Hello World</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title><!--v title-->Hello<!--/v--></title>
|
||||||
|
<link rel="stylesheet" href="{{v webapp:rootUrl/}}css/style.css?{{v context:paramRandom/}}">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Hello World</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,21 @@
|
||||||
|
:root {
|
||||||
|
/* fonts */
|
||||||
|
--main-font: sans-serif;
|
||||||
|
|
||||||
|
/* font sizes */
|
||||||
|
--main-font-size: 18px;
|
||||||
|
|
||||||
|
/* colors */
|
||||||
|
--main-background-color: #0d0d0d;
|
||||||
|
--main-text-color: #d0d0d0;
|
||||||
|
|
||||||
|
/* margins and padding */
|
||||||
|
--content-padding: 2em;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
background: var(--main-background-color);
|
||||||
|
font-family: var(--main-font);
|
||||||
|
font-style: var(--main-font-size);
|
||||||
|
color: var(--main-text-color);
|
||||||
|
padding: var(--content-padding);
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* This Java source file was generated by the Gradle 'init' task.
|
||||||
|
*/
|
||||||
|
package hello;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import rife.test.MockConversation;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class AppTest {
|
||||||
|
@Test
|
||||||
|
void verifyRoot() {
|
||||||
|
var m = new MockConversation(new App());
|
||||||
|
assertEquals(m.doRequest("/").getStatus(), 302);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void verifyHello() {
|
||||||
|
var m = new MockConversation(new App());
|
||||||
|
assertEquals("Hello", m.doRequest("/hello")
|
||||||
|
.getTemplate().getValue("title"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,184 @@
|
||||||
|
package com.uwyn.rife2.gradle
|
||||||
|
|
||||||
|
import org.gradle.testkit.runner.BuildResult
|
||||||
|
import org.gradle.testkit.runner.GradleRunner
|
||||||
|
import org.gradle.testkit.runner.TaskOutcome
|
||||||
|
import org.gradle.util.GFileUtils
|
||||||
|
import org.gradle.util.GradleVersion
|
||||||
|
import spock.lang.Specification
|
||||||
|
import spock.lang.TempDir
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
abstract class AbstractFunctionalTest extends Specification {
|
||||||
|
|
||||||
|
private final String gradleVersion = System.getProperty("gradleVersion", GradleVersion.current().version)
|
||||||
|
|
||||||
|
@TempDir
|
||||||
|
Path testDirectory
|
||||||
|
|
||||||
|
boolean debug
|
||||||
|
|
||||||
|
private StringWriter outputWriter
|
||||||
|
private StringWriter errorOutputWriter
|
||||||
|
private String output
|
||||||
|
private String errorOutput
|
||||||
|
|
||||||
|
BuildResult result
|
||||||
|
|
||||||
|
Path path(String... pathElements) {
|
||||||
|
Path cur = testDirectory
|
||||||
|
pathElements.each {
|
||||||
|
cur = cur.resolve(it)
|
||||||
|
}
|
||||||
|
cur
|
||||||
|
}
|
||||||
|
|
||||||
|
File file(String... pathElements) {
|
||||||
|
path(pathElements).toFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
File getGroovyBuildFile() {
|
||||||
|
file("build.gradle")
|
||||||
|
}
|
||||||
|
|
||||||
|
File getBuildFile() {
|
||||||
|
groovyBuildFile
|
||||||
|
}
|
||||||
|
|
||||||
|
File getKotlinBuildFile() {
|
||||||
|
file("build.gradle.kts")
|
||||||
|
}
|
||||||
|
|
||||||
|
File getGroovySettingsFile() {
|
||||||
|
file("settings.gradle")
|
||||||
|
}
|
||||||
|
|
||||||
|
File getKotlinSettingsFile() {
|
||||||
|
file("settings.gradle.kts")
|
||||||
|
}
|
||||||
|
|
||||||
|
File getSettingsFile() {
|
||||||
|
groovySettingsFile
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(String... args) {
|
||||||
|
try {
|
||||||
|
result = newRunner(args)
|
||||||
|
.build()
|
||||||
|
} finally {
|
||||||
|
recordOutputs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void outputContains(String text) {
|
||||||
|
assert output.normalize().contains(text.normalize())
|
||||||
|
}
|
||||||
|
|
||||||
|
void outputDoesNotContain(String text) {
|
||||||
|
assert !output.normalize().contains(text.normalize())
|
||||||
|
}
|
||||||
|
|
||||||
|
void errorOutputContains(String text) {
|
||||||
|
assert errorOutput.normalize().contains(text.normalize())
|
||||||
|
}
|
||||||
|
|
||||||
|
void tasks(@DelegatesTo(value = TaskExecutionGraph, strategy = Closure.DELEGATE_FIRST) Closure spec) {
|
||||||
|
def graph = new TaskExecutionGraph()
|
||||||
|
spec.delegate = graph
|
||||||
|
spec.resolveStrategy = Closure.DELEGATE_FIRST
|
||||||
|
spec()
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recordOutputs() {
|
||||||
|
output = outputWriter.toString()
|
||||||
|
errorOutput = errorOutputWriter.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private GradleRunner newRunner(String... args) {
|
||||||
|
outputWriter = new StringWriter()
|
||||||
|
errorOutputWriter = new StringWriter()
|
||||||
|
ArrayList<String> autoArgs = computeAutoArgs()
|
||||||
|
def runner = GradleRunner.create()
|
||||||
|
.forwardStdOutput(tee(new OutputStreamWriter(System.out), outputWriter))
|
||||||
|
.forwardStdError(tee(new OutputStreamWriter(System.err), errorOutputWriter))
|
||||||
|
.withPluginClasspath()
|
||||||
|
.withProjectDir(testDirectory.toFile())
|
||||||
|
.withArguments([*autoArgs, *args])
|
||||||
|
if (gradleVersion) {
|
||||||
|
runner.withGradleVersion(gradleVersion)
|
||||||
|
}
|
||||||
|
if (debug) {
|
||||||
|
runner.withDebug(true)
|
||||||
|
}
|
||||||
|
runner
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<String> computeAutoArgs() {
|
||||||
|
List<String> autoArgs = [
|
||||||
|
"-s",
|
||||||
|
"--console=verbose"
|
||||||
|
]
|
||||||
|
if (Boolean.getBoolean("config.cache")) {
|
||||||
|
autoArgs << '--configuration-cache'
|
||||||
|
}
|
||||||
|
autoArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Writer tee(Writer one, Writer two) {
|
||||||
|
return TeeWriter.of(one, two)
|
||||||
|
}
|
||||||
|
|
||||||
|
void fails(String... args) {
|
||||||
|
try {
|
||||||
|
result = newRunner(args)
|
||||||
|
.buildAndFail()
|
||||||
|
} finally {
|
||||||
|
recordOutputs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TaskExecutionGraph {
|
||||||
|
void succeeded(String... tasks) {
|
||||||
|
tasks.each { task ->
|
||||||
|
contains(task)
|
||||||
|
assert result.task(task).outcome == TaskOutcome.SUCCESS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void failed(String... tasks) {
|
||||||
|
tasks.each { task ->
|
||||||
|
contains(task)
|
||||||
|
assert result.task(task).outcome == TaskOutcome.FAILED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void skipped(String... tasks) {
|
||||||
|
tasks.each { task ->
|
||||||
|
contains(task)
|
||||||
|
assert result.task(task).outcome == TaskOutcome.SKIPPED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void contains(String... tasks) {
|
||||||
|
tasks.each { task ->
|
||||||
|
assert result.task(task) != null: "Expected to find task $task in the graph but it was missing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void doesNotContain(String... tasks) {
|
||||||
|
tasks.each { task ->
|
||||||
|
assert result.task(task) == null: "Task $task should be missing from the task graph but it was found with an outcome of ${result.task(task).outcome}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void usesProject(String name) {
|
||||||
|
File sampleDir = new File("src/test-projects/$name")
|
||||||
|
GFileUtils.copyDirectory(sampleDir, testDirectory.toFile())
|
||||||
|
}
|
||||||
|
|
||||||
|
File file(String path) {
|
||||||
|
new File(testDirectory.toFile(), path)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.uwyn.rife2.gradle
|
||||||
|
|
||||||
|
import java.nio.file.FileSystems
|
||||||
|
import java.nio.file.Files
|
||||||
|
|
||||||
|
class PackagingTest extends AbstractFunctionalTest {
|
||||||
|
def setup() {
|
||||||
|
usesProject("minimal")
|
||||||
|
}
|
||||||
|
|
||||||
|
def "#archive contains compiled resources"() {
|
||||||
|
def jarFile = file(archive).toPath()
|
||||||
|
when:
|
||||||
|
run task
|
||||||
|
|
||||||
|
then: "compiles templates are found in the archive"
|
||||||
|
tasks {
|
||||||
|
succeeded ":${Rife2Plugin.PRECOMPILE_TEMPLATES_TASK_NAME}"
|
||||||
|
}
|
||||||
|
Files.exists(jarFile)
|
||||||
|
try (def fs = FileSystems.newFileSystem(jarFile, [:])) {
|
||||||
|
fs.getRootDirectories().each {
|
||||||
|
Files.walk(it).forEach { path ->
|
||||||
|
println path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert Files.exists(fs.getPath("/rife/template/html/hello.class"))
|
||||||
|
assert Files.exists(fs.getPath("/rife/template/html/world.class"))
|
||||||
|
assert !Files.exists(fs.getPath("/templates/hello.html"))
|
||||||
|
assert !Files.exists(fs.getPath("/templates/world.html"))
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
task | archive
|
||||||
|
'jar' | 'build/libs/hello-1.0.jar'
|
||||||
|
'uberJar' | 'build/libs/hello-uber-1.0.jar'
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
package com.uwyn.rife2.gradle
|
||||||
|
|
||||||
|
import groovy.transform.CompileStatic
|
||||||
|
|
||||||
|
@CompileStatic
|
||||||
|
class TeeWriter extends Writer {
|
||||||
|
private final Writer one
|
||||||
|
private final Writer two
|
||||||
|
|
||||||
|
static TeeWriter of(Writer one, Writer two) {
|
||||||
|
new TeeWriter(one, two)
|
||||||
|
}
|
||||||
|
|
||||||
|
private TeeWriter(Writer one, Writer two) {
|
||||||
|
this.one = one
|
||||||
|
this.two = two
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void write(int c) throws IOException {
|
||||||
|
try {
|
||||||
|
one.write(c)
|
||||||
|
} finally {
|
||||||
|
two.write(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void write(char[] cbuf) throws IOException {
|
||||||
|
try {
|
||||||
|
one.write(cbuf)
|
||||||
|
} finally {
|
||||||
|
two.write(cbuf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void write(char[] cbuf, int off, int len) throws IOException {
|
||||||
|
try {
|
||||||
|
one.write(cbuf, off, len)
|
||||||
|
} finally {
|
||||||
|
two.write(cbuf, off, len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void write(String str) throws IOException {
|
||||||
|
try {
|
||||||
|
one.write(str)
|
||||||
|
} finally {
|
||||||
|
two.write(str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void write(String str, int off, int len) throws IOException {
|
||||||
|
try {
|
||||||
|
one.write(str, off, len)
|
||||||
|
} finally {
|
||||||
|
two.write(str, off, len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void flush() throws IOException {
|
||||||
|
try {
|
||||||
|
one.flush()
|
||||||
|
} finally {
|
||||||
|
two.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void close() throws IOException {
|
||||||
|
try {
|
||||||
|
one.close()
|
||||||
|
} finally {
|
||||||
|
two.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package com.uwyn.rife2.gradle
|
||||||
|
|
||||||
|
class TemplateCompilationTest extends AbstractFunctionalTest {
|
||||||
|
def setup() {
|
||||||
|
usesProject("minimal")
|
||||||
|
}
|
||||||
|
|
||||||
|
def "doesn't precompile templates when calling `run`"() {
|
||||||
|
given:
|
||||||
|
buildFile << """
|
||||||
|
tasks.named("run") {
|
||||||
|
doFirst {
|
||||||
|
throw new RuntimeException("force stop")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
when:
|
||||||
|
fails 'run'
|
||||||
|
|
||||||
|
then: "precompile templates task must not be present in task graph"
|
||||||
|
errorOutputContains("force stop")
|
||||||
|
tasks {
|
||||||
|
doesNotContain ":${Rife2Plugin.PRECOMPILE_TEMPLATES_TASK_NAME}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "`run` task classpath includes template sources"() {
|
||||||
|
given:
|
||||||
|
buildFile << """
|
||||||
|
tasks.register("dumpRunClasspath") {
|
||||||
|
doLast {
|
||||||
|
def rootPath = rootProject.projectDir.toPath()
|
||||||
|
tasks.named("run").get().classpath.files.each {
|
||||||
|
println "Classpath entry: \${rootPath.relativize(it.toPath())}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
when:
|
||||||
|
run("dumpRunClasspath")
|
||||||
|
|
||||||
|
then: "template sources must be present in the classpath"
|
||||||
|
outputContains("Classpath entry: src/main/templates")
|
||||||
|
outputContains("Classpath entry: src/main/resources/templates")
|
||||||
|
}
|
||||||
|
|
||||||
|
def "compiles templates when running #task"() {
|
||||||
|
when:
|
||||||
|
run task
|
||||||
|
|
||||||
|
then: "precompile templates task must be present in task graph"
|
||||||
|
tasks {
|
||||||
|
succeeded ":${Rife2Plugin.PRECOMPILE_TEMPLATES_TASK_NAME}"
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
task << ['jar', 'test', 'uberJar']
|
||||||
|
}
|
||||||
|
}
|
17
gradle/libs.versions.toml
Normal file
17
gradle/libs.versions.toml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[versions]
|
||||||
|
jetty = "11.0.13"
|
||||||
|
jsoup = "1.15.3"
|
||||||
|
junit-jupiter = "5.9.1"
|
||||||
|
slf4j = "2.0.5"
|
||||||
|
spock = "2.3-groovy-3.0"
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
jetty-server = { module = "org.eclipse.jetty:jetty-server", version.ref = "jetty" }
|
||||||
|
jetty-servlet = { module = "org.eclipse.jetty:jetty-servlet", version.ref = "jetty" }
|
||||||
|
jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
|
||||||
|
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" }
|
||||||
|
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
||||||
|
spock-core = { module = "org.spockframework:spock-core", version.ref = "spock" }
|
||||||
|
|
||||||
|
[bundles]
|
||||||
|
jetty = [ "jetty-server", "jetty-servlet" ]
|
|
@ -1,4 +1,8 @@
|
||||||
pluginManagement {
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
includeBuild("build-logic")
|
includeBuild("build-logic")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,9 @@ plugins {
|
||||||
war
|
war
|
||||||
}
|
}
|
||||||
|
|
||||||
version = 1.0
|
|
||||||
|
|
||||||
base {
|
base {
|
||||||
archivesName.set("hello")
|
archivesName.set("hello")
|
||||||
|
version = 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue