Skip to content

Commit

Permalink
Merge pull request #104 from kelemen/generated-jakarta-sources
Browse files Browse the repository at this point in the history
FREEMARKER-218: Jakarta support
  • Loading branch information
ddekany committed Jan 23, 2024
2 parents 6543e50 + c88ed78 commit babdccb
Show file tree
Hide file tree
Showing 10 changed files with 513 additions and 46 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ jobs:
with:
java-version: 16
distribution: zulu
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: 17
distribution: oracle
- name: Validate Gradle wrapper
uses: gradle/[email protected]
- name: Run Build
Expand All @@ -60,4 +65,3 @@ jobs:
name: test-reports-${{ matrix.os }}
path: build/reports/**
retention-days: 30

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ If you haven't yet, download the source release, or checkout FreeMarker from
the source code repository. See repository locations here:
https://freemarker.apache.org/sourcecode.html

You need JDK 8 and JDK 16 to be installed
You need JDK 8, JDK 16 and JDK 17 (only for some tests) to be installed
(and [visible to Gradle](https://docs.gradle.org/current/userguide/toolchains.html)).

Be sure that your default Java version (which Gradle should use automatically) is at
Expand Down
96 changes: 81 additions & 15 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import java.io.FileOutputStream
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.util.*
import java.util.Properties
import java.util.stream.Collectors

plugins {
Expand Down Expand Up @@ -58,6 +58,36 @@ freemarkerRoot {
configureSourceSet("jython22")
configureSourceSet("jython25") { enableTests() }
configureSourceSet("core16", "16")

configureGeneratedSourceSet("jakartaServlet") {
val jakartaSourceGenerators = generateJakartaSources("javaxServlet")

val testSourceSet = enableTests("17").get().sources
val jakartaTestSourceGenerators = generateJakartaSources(
"javaxServletTest",
SourceSet.TEST_SOURCE_SET_NAME,
testSourceSet
)

(jakartaSourceGenerators + jakartaTestSourceGenerators).forEach { task ->
task.configure {
packageMappings.set(mapOf(
"freemarker.ext.jsp" to "freemarker.ext.jakarta.jsp",
"freemarker.ext.servlet" to "freemarker.ext.jakarta.servlet",
"freemarker.cache" to "freemarker.ext.jakarta.servlet",
))
noAutoReplacePackages.set(setOf("freemarker.cache"))
replacements.set(mapOf(
"package freemarker.cache" to "package freemarker.ext.jakarta.servlet",
"freemarker.cache.WebappTemplateLoader" to "freemarker.ext.jakarta.servlet.WebappTemplateLoader",
"javax.servlet" to "jakarta.servlet",
"javax.el" to "jakarta.el",
"http://java.sun.com/jsp/jstl/core" to "jakarta.tags.core",
"http://java.sun.com/jsp/jstl/functions" to "jakarta.tags.functions",
))
}
}
}
}

val compileJavacc = tasks.register<freemarker.build.CompileJavaccTask>("compileJavacc") {
Expand Down Expand Up @@ -127,6 +157,10 @@ configurations {
extendsFrom(named("jython25CompileClasspath").get())
extendsFrom(named("javaxServletCompileClasspath").get())
}
register("javadocClasspath") {
extendsFrom(named("combinedClasspath").get())
extendsFrom(named("jakartaServletCompileClasspath").get())
}
}

// This source set is only needed, because the OSGI plugin supports only a single sourceSet.
Expand Down Expand Up @@ -230,7 +264,7 @@ tasks.named<Javadoc>(JavaPlugin.JAVADOC_TASK_NAME) {
addStringOption("Xdoclint:-missing", "-quiet")
}

classpath = files(configurations.named("combinedClasspath"))
classpath = files(configurations.named("javadocClasspath"))
}

fun registerManualTask(taskName: String, localeValue: String, offlineValue: Boolean) {
Expand Down Expand Up @@ -441,14 +475,14 @@ val createBuildInfo = tasks.register("createBuildInfo") {
val props = Properties().apply {
// see https://reproducible-builds.org/docs/jvm/
setProperty("buildinfo.version", "1.0-SNAPSHOT")

setProperty("java.version", System.getProperty("java.version"))
setProperty("java.vendor", System.getProperty("java.vendor"))
setProperty("os.name", System.getProperty("os.name"))

setProperty("source.scm.uri", "scm:git:https://git-wip-us.apache.org/repos/asf/freemarker.git")
setProperty("source.scm.tag", "v${fmExt.versionDef.version}")

setProperty("build-tool", "gradle")
setProperty("build.setup", "https://github.com/apache/freemarker/blob/2.3-gae/README.md#building-freemarker")

Expand Down Expand Up @@ -560,9 +594,13 @@ eclipse {

val jettyVersion = "9.4.53.v20231009"
val slf4jVersion = "1.6.1"
val springVersion = "2.5.6.SEC03"
val springVersion = "5.3.31"
val tagLibsVersion = "1.2.5"

val jakartaJettyVersion = "11.0.19"
val jakartaSlf4jVersion = "2.0.9"
val jakartaSpringVersion = "6.1.2"

configurations {
compileOnly {
exclude(group = "xml-apis", module = "xml-apis")
Expand Down Expand Up @@ -599,6 +637,10 @@ dependencies {

testImplementation(xalan)

"jakartaServletCompileOnly"("jakarta.servlet:jakarta.servlet-api:5.0.0")
"jakartaServletCompileOnly"("jakarta.servlet.jsp:jakarta.servlet.jsp-api:3.0.0")
"jakartaServletCompileOnly"("jakarta.el:jakarta.el-api:4.0.0")

"javaxServletCompileOnly"("javax.servlet:javax.servlet-api:3.0.1")
"javaxServletCompileOnly"("javax.servlet.jsp:jsp-api:2.2")
"javaxServletCompileOnly"("javax.el:el-api:2.2")
Expand All @@ -619,6 +661,39 @@ dependencies {
"javaxServletTestImplementation"("org.springframework:spring-test:${springVersion}") {
exclude(group = "commons-logging", module = "commons-logging")
}
"javaxServletTestImplementation"("org.springframework:spring-web:${springVersion}") {
exclude(group = "commons-logging", module = "commons-logging")
}
"javaxServletTestImplementation"("com.github.hazendaz:displaytag:2.5.3")

"jakartaServletTestImplementation"("org.eclipse.jetty:jetty-server:${jakartaJettyVersion}")
"jakartaServletTestImplementation"("org.eclipse.jetty:jetty-annotations:${jakartaJettyVersion}")
"jakartaServletTestImplementation"("org.eclipse.jetty:jetty-webapp:${jakartaJettyVersion}")
"jakartaServletTestImplementation"("org.eclipse.jetty:jetty-util:${jakartaJettyVersion}")
"jakartaServletTestImplementation"("org.eclipse.jetty:apache-jsp:${jakartaJettyVersion}")
// Jetty also contains the servlet-api and jsp-api and el-api classes

"jakartaServletTestImplementation"("jakarta.servlet:jakarta.servlet-api:6.0.0")
"jakartaServletTestImplementation"("jakarta.servlet.jsp:jakarta.servlet.jsp-api:3.0.0")
"jakartaServletTestImplementation"("jakarta.el:jakarta.el-api:4.0.0")

// JSP JSTL (not included in Jetty):
"jakartaServletTestImplementation"("com.github.hazendaz:displaytag:3.0.0-M2")

"jakartaServletTestImplementation"("org.springframework:spring-core:${jakartaSpringVersion}") {
exclude(group = "commons-logging", module = "commons-logging")
}
"jakartaServletTestImplementation"("org.springframework:spring-test:${jakartaSpringVersion}") {
exclude(group = "commons-logging", module = "commons-logging")
}
"jakartaServletTestImplementation"("org.springframework:spring-web:${jakartaSpringVersion}") {
exclude(group = "commons-logging", module = "commons-logging")
}

"jakartaServletTestRuntimeOnly"("org.slf4j:slf4j-api:${jakartaSlf4jVersion}")
"jakartaServletTestRuntimeOnly"("org.slf4j:log4j-over-slf4j:${jakartaSlf4jVersion}")
"jakartaServletTestRuntimeOnly"("org.slf4j:jcl-over-slf4j:${jakartaSlf4jVersion}")
"jakartaServletTestRuntimeOnly"("ch.qos.logback:logback-classic:1.3.14")

"jython20CompileOnly"("jython:jython:2.1")

Expand All @@ -628,13 +703,6 @@ dependencies {
"jython25CompileOnly"(sourceSets["jython20"].output)
"jython25CompileOnly"("org.python:jython:2.5.0")

"testUtilsImplementation"("com.github.hazendaz:displaytag:2.5.3") {
exclude(group = "com.lowagie", module = "itext")
// We manage logging centrally:
exclude(group = "org.slf4j", module = "slf4j-log4j12")
exclude(group = "rg.slf4j", module = "jcl104-over-slf4j")
exclude(group = "log4j", module = "log4j")
}
"testUtilsImplementation"(sourceSets.main.get().output)
"testUtilsImplementation"("com.google.code.findbugs:annotations:3.0.0")
"testUtilsImplementation"(libs.junit)
Expand All @@ -643,7 +711,5 @@ dependencies {
"testUtilsImplementation"("commons-io:commons-io:2.7")
"testUtilsImplementation"("com.google.guava:guava:29.0-jre")
"testUtilsImplementation"("commons-collections:commons-collections:3.1")

// Override Java 9 incompatible version (coming from displaytag):
"testUtilsImplementation"("commons-lang:commons-lang:2.6")
}
114 changes: 97 additions & 17 deletions buildSrc/src/main/kotlin/freemarker/build/FreemarkerRootExtension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

package freemarker.build

import java.util.concurrent.atomic.AtomicBoolean
import org.gradle.api.NamedDomainObjectProvider
import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalogsExtension
Expand All @@ -29,19 +30,20 @@ import org.gradle.api.plugins.jvm.JvmTestSuite
import org.gradle.api.plugins.jvm.JvmTestSuiteTarget
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.javadoc.Javadoc
import org.gradle.jvm.toolchain.JavaLanguageVersion
import org.gradle.jvm.toolchain.JavaToolchainService
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.named
import org.gradle.kotlin.dsl.register
import org.gradle.kotlin.dsl.setProperty
import org.gradle.kotlin.dsl.the
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.gradle.language.jvm.tasks.ProcessResources
import org.gradle.testing.base.TestingExtension
import java.util.concurrent.atomic.AtomicBoolean

private const val TEST_UTILS_SOURCE_SET_NAME = "test-utils"

Expand Down Expand Up @@ -90,6 +92,7 @@ internal class JavaProjectContext constructor(
class FreemarkerModuleDef internal constructor(
private val context: JavaProjectContext,
private val ext: FreemarkerRootExtension,
private val generated: Boolean,
val sourceSetName: String,
val compilerVersion: JavaLanguageVersion
) {
Expand All @@ -99,27 +102,71 @@ class FreemarkerModuleDef internal constructor(
val sourceSet = context.sourceSets.maybeCreate(sourceSetName)

val sourceSetRootDirName = "freemarker-${baseDirName}"
val sourceSetSrcPath = "${sourceSetRootDirName}/src"
val sourceSetSrcPath = sourceSetRoot(context, generated, sourceSetRootDirName)

fun generateJakartaSources(
baseSourceSetName: String,
sourceSetKind: String = SourceSet.MAIN_SOURCE_SET_NAME,
targetSourceSet: SourceSet = sourceSet
): List<TaskProvider<JakartaSourceRootGeneratorTask>> {
val baseSourceSetRef = context.sourceSets.named(baseSourceSetName)
val taskNameClassifier = if (SourceSet.MAIN_SOURCE_SET_NAME == sourceSetKind) {
""
} else {
sourceSetKind.replaceFirstChar { it.uppercaseChar() }
}

fun enableTests(testJavaVersion: String = ext.testJavaVersion) {
configureTests(JavaLanguageVersion.of(testJavaVersion))
val generateJakartaSources = context.tasks
.register<JakartaSourceRootGeneratorTask>("generateJakarta${taskNameClassifier}Sources") {
sourceDirectory.set(baseSourceSetRef.get().java.srcDirs.single())
destinationDirectory.set(project.file(sourceSetSrcPath).resolve(sourceSetKind).resolve("java"))
}
targetSourceSet.java.srcDir(generateJakartaSources)

val generateJakartaResources = context.tasks
.register<JakartaSourceRootGeneratorTask>("generateJakarta${taskNameClassifier}Resources") {
sourceDirectory.set(baseSourceSetRef.get().resources.srcDirs.single())
destinationDirectory.set(project.file(sourceSetSrcPath).resolve(sourceSetKind).resolve("resources"))
}
targetSourceSet.resources.srcDir(generateJakartaResources)
return listOf(generateJakartaSources, generateJakartaResources)
}

private fun configureTests(testJavaVersion: JavaLanguageVersion) {
getOrCreateTestSuiteRef().configure {
private fun sourceSetRoot(
context: JavaProjectContext,
generated: Boolean,
sourceSetRootDirName: String
): String {
return if (generated) {
context.project.layout.buildDirectory.get().asFile
.resolve("generated")
.resolve(sourceSetRootDirName)
.toString()
} else {
"${sourceSetRootDirName}/src"
}
}

fun enableTests(testJavaVersion: String = ext.testJavaVersion) =
configureTests(JavaLanguageVersion.of(testJavaVersion))

private fun configureTests(testJavaVersion: JavaLanguageVersion): NamedDomainObjectProvider<JvmTestSuite> {
val testSuitRef = getOrCreateTestSuiteRef()
testSuitRef.configure {
useJUnit(context.version("junit"))

configureSources(sources, testJavaVersion)
targets.all { configureTarget(this, sources, testJavaVersion) }
}
return testSuitRef
}

private fun getOrCreateTestSuiteRef(): NamedDomainObjectProvider<JvmTestSuite> {
val suites = context.testing.suites
if (main) {
return suites.named<JvmTestSuite>(JvmTestSuitePlugin.DEFAULT_TEST_SUITE_NAME)
return if (main) {
suites.named<JvmTestSuite>(JvmTestSuitePlugin.DEFAULT_TEST_SUITE_NAME)
} else {
return suites.register("${sourceSetName}Test", JvmTestSuite::class.java)
suites.register("${sourceSetName}Test", JvmTestSuite::class.java)
}
}

Expand All @@ -134,9 +181,14 @@ class FreemarkerModuleDef internal constructor(

private fun configureSources(sources: SourceSet, testJavaVersion: JavaLanguageVersion) {
sources.apply {
val testSrcPath = "${sourceSetSrcPath}/test"
java.setSrcDirs(listOf("${testSrcPath}/java"))
resources.setSrcDirs(listOf("${testSrcPath}/resources"))
if (generated) {
java.setSrcDirs(emptyList<String>())
resources.setSrcDirs(emptyList<String>())
} else {
val testSrcPath = "${sourceSetSrcPath}/test"
java.setSrcDirs(listOf("${testSrcPath}/java"))
resources.setSrcDirs(listOf("${testSrcPath}/resources"))
}

if (!main) {
context.inheritCompileRuntimeAndOutput(this, sourceSet)
Expand Down Expand Up @@ -238,6 +290,21 @@ class FreemarkerRootExtension constructor(
}
}

fun configureGeneratedSourceSet(
sourceSetName: String,
configuration: FreemarkerModuleDef.() -> Unit = { }
) {
configureGeneratedSourceSet(sourceSetName, javaVersion, configuration)
}

fun configureGeneratedSourceSet(
sourceSetName: String,
sourceSetVersion: String,
configuration: FreemarkerModuleDef.() -> Unit = { }
) {
configureSourceSet(true, sourceSetName, sourceSetVersion, configuration)
}

fun configureSourceSet(
sourceSetName: String,
configuration: FreemarkerModuleDef.() -> Unit = { }
Expand All @@ -249,19 +316,32 @@ class FreemarkerRootExtension constructor(
sourceSetName: String,
sourceSetVersion: String,
configuration: FreemarkerModuleDef.() -> Unit = { }
) {
configureSourceSet(false, sourceSetName, sourceSetVersion, configuration)
}

private fun configureSourceSet(
generated: Boolean,
sourceSetName: String,
sourceSetVersion: String,
configuration: FreemarkerModuleDef.() -> Unit = { }
) {
if (testUtilsConfigured.compareAndSet(false, true)) {
configureTestUtils()
}

allConfiguredSourceSetNamesRef.add(sourceSetName)

FreemarkerModuleDef(context, this, sourceSetName, JavaLanguageVersion.of(sourceSetVersion)).apply {
val sourceSetSrcMainPath = "${sourceSetSrcPath}/main"

FreemarkerModuleDef(context, this, generated, sourceSetName, JavaLanguageVersion.of(sourceSetVersion)).apply {
sourceSet.apply {
java.setSrcDirs(listOf("${sourceSetSrcMainPath}/java"))
resources.setSrcDirs(listOf("${sourceSetSrcMainPath}/resources"))
if (generated) {
java.setSrcDirs(emptyList<String>())
resources.setSrcDirs(emptyList<String>())
} else {
val sourceSetSrcMainPath = "${sourceSetSrcPath}/main"
java.setSrcDirs(listOf("${sourceSetSrcMainPath}/java"))
resources.setSrcDirs(listOf("${sourceSetSrcMainPath}/resources"))
}
}

if (!main) {
Expand Down

0 comments on commit babdccb

Please sign in to comment.