mirror of
https://github.com/ethauvin/rife2-hello.git
synced 2025-04-24 15:07:11 -07:00
Rework Gradle build
This commit introduces a _convention plugin_ for RIFE2 support. It provides a number of advantages: - the build logic is clearly separated from the build script, which now only contains user-specific configuration, like the framework version or how to configure test logging - it fixes a number of issues like hardcoded dependencies (`dependsOn` is in general a mistake) - it removes the need to resolve the agent path eagerly - it makes the agent configurable - it clearly separates the user classpath from the RIFE classpath (both for precompiling templates and for the agent) - template compilation is cached - it avoids the abuse of `src/main/resources` to put templates and uses a dedicated directory instead, which removes the need for exclusions In addition, this should make it relatively straightforward to convert the convention plugin into a proper RIFE2 plugin, by extracting the code in `build-logic` into its own repository then publishing to the Gradle plugin portal.
This commit is contained in:
parent
3a9bbec851
commit
2e025cd693
9 changed files with 284 additions and 77 deletions
|
@ -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<JavaExec>("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<JavaExec>("run") {
|
||||
classpath = sourceSets["main"].runtimeClasspath
|
||||
mainClass.set("hello.App")
|
||||
jvmArgs = listOf("-javaagent:$rifeAgentJar")
|
||||
}
|
||||
|
||||
// These two tasks create a self-container UberJar
|
||||
register<Copy>("copyWebapp") {
|
||||
from("src/main/")
|
||||
include("webapp/**")
|
||||
into("$buildDir/webapp")
|
||||
}
|
||||
|
||||
register<Jar>("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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
20
build-logic/build.gradle.kts
Normal file
20
build-logic/build.gradle.kts
Normal file
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
1
build-logic/settings.gradle.kts
Normal file
1
build-logic/settings.gradle.kts
Normal file
|
@ -0,0 +1 @@
|
|||
rootProject.name = "build-logic"
|
|
@ -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<String> getType();
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
public abstract Property<String> getEncoding();
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
public abstract Property<Boolean> 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<String> 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);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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<String> getVersion();
|
||||
|
||||
public abstract Property<Boolean> getUseAgent();
|
||||
|
||||
public abstract Property<String> getUberMainClass();
|
||||
}
|
139
build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java
Normal file
139
build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java
Normal file
|
@ -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<Project> {
|
||||
@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> 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> precompileTemplates,
|
||||
JavaPluginExtension javaPluginExtension) {
|
||||
javaPluginExtension.getSourceSets()
|
||||
.getByName(SourceSet.MAIN_SOURCE_SET_NAME)
|
||||
.getOutput()
|
||||
.dir(precompileTemplates);
|
||||
}
|
||||
|
||||
private static TaskProvider<PrecompileTemplates> 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"));
|
||||
});
|
||||
}
|
||||
}
|
2
gradle.properties
Normal file
2
gradle.properties
Normal file
|
@ -0,0 +1,2 @@
|
|||
org.gradle.parallel=true
|
||||
org.gradle.caching=true
|
|
@ -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")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue