diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ad32a36..68f44cb 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -2,7 +2,8 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent plugins { - java + application + id("com.uwyn.rife2") } base { @@ -10,25 +11,22 @@ base { version = 1.0 } +application { + mainClass.set("hello.App") +} + repositories { // Use Maven Central for resolving dependencies. mavenCentral() maven { url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots") } // only needed for SNAPSHOT } -sourceSets { - main { - runtimeClasspath = files(file("src/main/resources"), runtimeClasspath); - } -} - -sourceSets.main { - resources.exclude("templates/**") +rife2 { + version.set("1.3.0") + useAgent.set(true) } dependencies { - implementation("com.uwyn.rife2:rife2:1.3.0") - runtimeOnly("com.uwyn.rife2:rife2:1.3.0:agent") 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") @@ -38,68 +36,11 @@ dependencies { } tasks { - val dependencies = configurations - .runtimeClasspath.get().files; - val rifeAgentJar = dependencies - .filter { it.toString().contains("rife2") } - .filter { it.toString().endsWith("-agent.jar") }[0] - test { - jvmArgs = listOf("-javaagent:$rifeAgentJar") useJUnitPlatform() testLogging { exceptionFormat = TestExceptionFormat.FULL events = setOf(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED) } } - - // Pre-compile the RIFE2 templates to bytecode for deployment - register("precompileHtmlTemplates") { - classpath = sourceSets["main"].runtimeClasspath - mainClass.set("rife.template.TemplateDeployer") - args = listOf( - "-verbose", - "-t", "html", - "-d", "${projectDir}/build/classes/java/main", - "-encoding", "UTF-8", "${projectDir}/src/main/resources/templates" - ) - } - - register("precompileTemplates") { - dependsOn("precompileHtmlTemplates") - } - - // Ensure that the templates are pre-compiled before building the jar - jar { - dependsOn("precompileTemplates") - } - - // Replace the run task with one that uses the RIFE2 agent - register("run") { - classpath = sourceSets["main"].runtimeClasspath - mainClass.set("hello.App") - jvmArgs = listOf("-javaagent:$rifeAgentJar") - } - - // These two tasks create a self-container UberJar - register("copyWebapp") { - from("src/main/") - include("webapp/**") - into("$buildDir/webapp") - } - - register("uberJar") { - dependsOn("jar") - dependsOn("copyWebapp") - archiveBaseName.set("hello-uber") - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - manifest { - attributes["Main-Class"] = "hello.AppUber" - } - val uberDependencies = dependencies - .filter { !it.toString().matches("rife2-.*agent\\.jar".toRegex()) } - .map(::zipTree) - from(uberDependencies, "$buildDir/webapp") - with(jar.get()) - } -} \ No newline at end of file +} diff --git a/app/src/main/resources/templates/hello.html b/app/src/main/templates/hello.html similarity index 100% rename from app/src/main/resources/templates/hello.html rename to app/src/main/templates/hello.html diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts new file mode 100644 index 0000000..b29e98f --- /dev/null +++ b/build-logic/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + `java-gradle-plugin` +} + +repositories { + mavenCentral() +} + +dependencies { + gradleApi() +} + +gradlePlugin { + plugins { + create("rife2") { + id = "com.uwyn.rife2" + implementationClass = "com.uwyn.rife2.gradle.Rife2Plugin" + } + } +} diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts new file mode 100644 index 0000000..7fbbd44 --- /dev/null +++ b/build-logic/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "build-logic" diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java new file mode 100644 index 0000000..871df2e --- /dev/null +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java @@ -0,0 +1,83 @@ +/* + * 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.CacheableTask; +import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputDirectory; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; +import org.gradle.process.ExecOperations; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@CacheableTask +public abstract class PrecompileTemplates extends DefaultTask { + + @Classpath + public abstract ConfigurableFileCollection getClasspath(); + + @InputDirectory + @PathSensitive(PathSensitivity.RELATIVE) + public abstract DirectoryProperty getTemplatesDirectory(); + + @Input + public abstract Property getType(); + + @Input + @Optional + public abstract Property getEncoding(); + + @Input + @Optional + public abstract Property getVerbose(); + + @OutputDirectory + public abstract DirectoryProperty getOutputDirectory(); + + @Inject + protected abstract ExecOperations getExecOperations(); + + @TaskAction + public void precompileTemplates() { + getExecOperations().javaexec(javaexec -> { + javaexec.setClasspath(getClasspath()); + javaexec.getMainClass().set("rife.template.TemplateDeployer"); + List args = new ArrayList<>(); + if (getVerbose().isPresent() && Boolean.TRUE.equals(getVerbose().get())) { + args.add("-verbose"); + } + args.add("-t"); + args.add(getType().get()); + args.add("-d"); + args.add(getOutputDirectory().get().getAsFile().getPath()); + args.add("-encoding"); + args.add(getEncoding().orElse("UTF-8").get()); + args.add(getTemplatesDirectory().get().getAsFile().getPath()); + javaexec.args(args); + }); + } +} diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java new file mode 100644 index 0000000..1fe9591 --- /dev/null +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java @@ -0,0 +1,26 @@ +/* + * 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.provider.Property; + +public abstract class Rife2Extension { + public abstract Property getVersion(); + + public abstract Property getUseAgent(); + + public abstract Property getUberMainClass(); +} diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java new file mode 100644 index 0000000..79d0a2f --- /dev/null +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -0,0 +1,139 @@ +/* + * 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.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ConfigurationContainer; +import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.file.DuplicatesStrategy; +import org.gradle.api.plugins.BasePluginExtension; +import org.gradle.api.plugins.JavaApplication; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.plugins.PluginContainer; +import org.gradle.api.tasks.JavaExec; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.bundling.Jar; +import org.gradle.api.tasks.testing.Test; +import org.gradle.process.CommandLineArgumentProvider; + +import java.util.Collections; +import java.util.stream.Collectors; + +public class Rife2Plugin implements Plugin { + @Override + public void apply(Project project) { + PluginContainer plugins = project.getPlugins(); + plugins.apply("java"); + JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class); + Rife2Extension rife2Extension = createRife2Extension(project, javaPluginExtension); + ConfigurationContainer configurations = project.getConfigurations(); + DependencyHandler dependencyHandler = project.getDependencies(); + Configuration rife2Configuration = createRife2Configuration(configurations, dependencyHandler, rife2Extension); + Configuration rife2CompilerClasspath = createRife2CompilerClasspathConfiguration(configurations, rife2Configuration); + Configuration rife2AgentClasspath = createRife2AgentConfiguration(configurations, dependencyHandler, rife2Extension); + configurations.getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME).extendsFrom(rife2Configuration); + TaskProvider precompileTemplates = registerPrecompileTemplateTask(project, rife2CompilerClasspath); + addTemplatesToMainOutput(precompileTemplates, javaPluginExtension); + configureAgent(project, plugins, rife2Extension, rife2AgentClasspath); + project.getTasks().register("uberJar", Jar.class, jar -> { + BasePluginExtension base = project.getExtensions().getByType(BasePluginExtension.class); + jar.getArchiveBaseName().convention(project.provider(() -> base.getArchivesName().get() + "-uber")); + jar.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE); + jar.into("webapp", spec -> spec.from("src/main/webapp")); + Configuration runtimeClasspath = project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME); + jar.from(javaPluginExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput()); + jar.from(runtimeClasspath.getElements().map(e -> e.stream().map(project::zipTree).collect(Collectors.toList()))); + plugins.withId("application", unused -> jar.manifest(manifest -> + manifest.getAttributes().put("Main-Class", rife2Extension.getUberMainClass().get())) + ); + }); + } + + private static void configureAgent(Project project, PluginContainer plugins, Rife2Extension rife2Extension, Configuration rife2AgentClasspath) { + CommandLineArgumentProvider agentProvider = () -> { + if (Boolean.TRUE.equals(rife2Extension.getUseAgent().get())) { + return Collections.singleton("-javaagent:" + rife2AgentClasspath.getAsPath()); + } + return Collections.emptyList(); + }; + project.getTasks().named("test", Test.class, test -> test.getJvmArgumentProviders().add(agentProvider)); + plugins.withId("application", unused -> project.getTasks().named("run", JavaExec.class, run -> run.getArgumentProviders().add(agentProvider))); + } + + private static Rife2Extension createRife2Extension(Project project, JavaPluginExtension javaPluginExtension) { + Rife2Extension rife2 = project.getExtensions().create("rife2", Rife2Extension.class); + rife2.getUseAgent().convention(false); + rife2.getUberMainClass().convention(project.getExtensions().getByType(JavaApplication.class).getMainClass() + .map(mainClass -> mainClass + "Uber")); + return rife2; + } + + private static Configuration createRife2CompilerClasspathConfiguration(ConfigurationContainer configurations, Configuration rife2Configuration) { + return configurations.create("rife2CompilerClasspath", conf -> { + conf.setDescription("The RIFE2 compiler classpath"); + conf.setCanBeConsumed(false); + conf.setCanBeResolved(true); + conf.extendsFrom(rife2Configuration); + }); + } + + private static Configuration createRife2AgentConfiguration(ConfigurationContainer configurations, + DependencyHandler dependencyHandler, + Rife2Extension rife2Extension) { + return configurations.create("rife2Agent", conf -> { + conf.setDescription("The RIFE2 agent classpath"); + conf.setCanBeConsumed(false); + conf.setCanBeResolved(true); + conf.setTransitive(false); + conf.getDependencies().addLater(rife2Extension.getVersion().map(version -> dependencyHandler.create("com.uwyn.rife2:rife2:" + version + ":agent"))); + }); + } + + private static Configuration createRife2Configuration(ConfigurationContainer configurations, + DependencyHandler dependencyHandler, + Rife2Extension rife2Extension) { + Configuration config = configurations.create("rife2", conf -> { + conf.setDescription("The RIFE2 framework dependencies"); + conf.setCanBeConsumed(false); + conf.setCanBeResolved(false); + }); + config.getDependencies().addLater(rife2Extension.getVersion().map(version -> dependencyHandler.create("com.uwyn.rife2:rife2:" + version))); + return config; + } + + private static void addTemplatesToMainOutput(TaskProvider precompileTemplates, + JavaPluginExtension javaPluginExtension) { + javaPluginExtension.getSourceSets() + .getByName(SourceSet.MAIN_SOURCE_SET_NAME) + .getOutput() + .dir(precompileTemplates); + } + + private static TaskProvider registerPrecompileTemplateTask(Project project, + Configuration rife2CompilerClasspath) { + return project.getTasks().register("precompileTemplates", PrecompileTemplates.class, task -> { + task.getVerbose().convention(true); + task.getClasspath().from(rife2CompilerClasspath); + task.getType().convention("html"); + task.getTemplatesDirectory().set(project.getLayout().getProjectDirectory().dir("src/main/templates")); + task.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir("generated/classes/rife2")); + }); + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..4f996f1 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.parallel=true +org.gradle.caching=true diff --git a/settings.gradle.kts b/settings.gradle.kts index 29842fd..f3be8d5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,11 +1,6 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * The settings file is used to specify which projects to include in your build. - * - * Detailed information about configuring a multi-project build in Gradle can be found - * in the user manual at https://docs.gradle.org/7.6/userguide/multi_project_builds.html - */ +pluginManagement { + includeBuild("build-logic") +} rootProject.name = "hello" include("app","war")