Files
pkl/buildSrc/src/main/kotlin/pklFatJar.gradle.kts
Daniel Chao 55eac2088b Bump GraalVM to 25.0.0 (#1226)
* Migrate shadow plugin to org.gradleup to correctly bundle Truffle in
  a fat jar
* Switch to GraalVM Community Edition
2025-10-07 21:23:15 -07:00

224 lines
7.6 KiB
Kotlin

/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* 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.
*/
import org.gradle.api.GradleException
import org.gradle.api.artifacts.Configuration
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.testing.Test
import org.gradle.kotlin.dsl.*
plugins {
`java-library`
`maven-publish`
id("com.gradleup.shadow")
}
// make fat Jar available to other subprojects
val fatJarConfiguration: Configuration = configurations.create("fatJar")
val fatJarPublication: MavenPublication = publishing.publications.create<MavenPublication>("fatJar")
// ideally we'd configure this automatically based on project dependencies
val firstPartySourcesJarsConfiguration: Configuration =
configurations.create("firstPartySourcesJars")
val relocations =
mapOf(
// pkl-core dependencies
"org.organicdesign.fp." to "org.pkl.thirdparty.paguro.",
"org.snakeyaml.engine." to "org.pkl.thirdparty.snakeyaml.engine.",
"org.msgpack." to "org.pkl.thirdparty.msgpack.",
"org.w3c.dom." to "org.pkl.thirdparty.w3c.dom.",
"com.oracle.svm.core." to "org.pkl.thirdparty.svm.",
// pkl-cli dependencies
"org.jline." to "org.pkl.thirdparty.jline.",
"com.github.ajalt.clikt." to "org.pkl.thirdparty.clikt.",
"com.github.ajalt.colormath." to "org.pkl.thirdparty.colormath.",
"com.github.ajalt.mordant." to "org.pkl.thirdparty.mordant.",
"com.sun.jna." to "org.pkl.thirdparty.jna.",
"kotlin." to "org.pkl.thirdparty.kotlin.",
"kotlinx." to "org.pkl.thirdparty.kotlinx.",
"org.intellij." to "org.pkl.thirdparty.intellij.",
"org.fusesource.jansi." to "org.pkl.thirdparty.jansi.",
"org.fusesource.hawtjni." to "org.pkl.thirdparty.hawtjni.",
// pkl-doc dependencies
"org.commonmark." to "org.pkl.thirdparty.commonmark.",
"org.jetbrains." to "org.pkl.thirdparty.jetbrains.",
"_COROUTINE." to "org.pkl.thirdparty.kotlinx._COROUTINE.",
// pkl-config-java dependencies
"io.leangen.geantyref." to "org.pkl.thirdparty.geantyref.",
// pkl-codegen-java dependencies
"com.palantir.javapoet." to "org.pkl.thirdparty.javapoet.",
// pkl-codegen-kotlin dependencies
"com.squareup.kotlinpoet." to "org.pkl.thirdparty.kotlinpoet.",
)
for ((key, value) in relocations) {
if (!key.endsWith(".")) {
throw GradleException(
"Invalid relocation `\"$key\" to \"$value\"`: `$key` should end with a dot"
)
}
if (!value.endsWith(".")) {
throw GradleException(
"Invalid relocation `\"$key\" to \"$value\"`: `$value` should end with a dot"
)
}
}
val nonRelocations = listOf("com/oracle/truffle/", "org/graalvm/")
tasks.shadowJar {
inputs.property("relocations", relocations)
archiveClassifier.set(null as String?)
configurations = listOf(project.configurations.runtimeClasspath.get())
addMultiReleaseAttribute = true
// not required at runtime / fat JARs can't be used in native-image builds anyway
exclude("org/pkl/cli/svm/**")
exclude("META-INF/maven/**")
exclude("META-INF/upgrade/**")
exclude("DebugProbesKt.bin")
val info = project.extensions.getByType<BuildInfo>()
val minimumJvmTarget = JavaVersion.toVersion(info.jvmTarget)
manifest.attributes(
// Certain exports need to be added to the Java modulepath for Java 17 to work properly with
// shaded JARs. See the following link for an explanation of this syntax:
// https://bugs.openjdk.org/browse/JDK-8335225
"Add-Exports" to info.jpmsExportsForJarManifest
)
// effectively, this results in calls excluding:
// `META-INF/versions/{18-25}/**`
// at the time of this writing; multi-release JARs beyond JDK 21 break the current
// version of the Shadow plugin, and aren't needed for Truffle's use by Pkl.
JavaVersionRange.startingAt(JavaLanguageVersion.of(minimumJvmTarget.majorVersion.toInt() + 1))
.forEach { exclude("META-INF/versions/${it.asInt()}/**") }
exclude("module-info.*")
for ((from, to) in relocations) {
relocate(from, to)
}
// necessary for service files to be adapted to relocation
mergeServiceFiles()
}
shadow { addShadowVariantIntoJavaComponent = false }
val testFatJar by
tasks.registering(Test::class) {
testClassesDirs = files(tasks.test.get().testClassesDirs)
classpath =
// compiled test classes
sourceSets.test.get().output +
// fat Jar
tasks.shadowJar.get().outputs.files +
// test-only dependencies
// (test dependencies that are also main dependencies must already be contained in fat Jar;
// to verify that, we don't want to include them here)
(configurations.testRuntimeClasspath.get() - configurations.runtimeClasspath.get())
}
tasks.check { dependsOn(testFatJar) }
val validateFatJar by
tasks.registering {
val outputFile = layout.buildDirectory.file("validateFatJar/result.txt")
inputs.files(tasks.shadowJar)
inputs.property("nonRelocations", nonRelocations)
outputs.file(outputFile)
doLast {
val unshadowedFiles = mutableListOf<String>()
zipTree(tasks.shadowJar.get().outputs.files.singleFile).visit {
val fileDetails = this
val path = fileDetails.relativePath.pathString
if (
!(fileDetails.isDirectory ||
path.startsWith("org/pkl/") ||
path.startsWith("META-INF/") ||
nonRelocations.any { path.startsWith(it) })
) {
// don't throw exception inside `visit`
// as this gives a misleading "Could not expand ZIP" error message
unshadowedFiles.add(path)
}
}
if (unshadowedFiles.isEmpty()) {
outputFile.get().asFile.writeText("SUCCESS")
} else {
outputFile.get().asFile.writeText("FAILURE")
throw GradleException("Found unshadowed files:\n" + unshadowedFiles.joinToString("\n"))
}
}
}
tasks.check { dependsOn(validateFatJar) }
val resolveSourcesJars by
tasks.registering(ResolveSourcesJars::class) {
configuration.set(configurations.runtimeClasspath)
outputDir.set(layout.buildDirectory.dir("resolveSourcesJars"))
}
val fatSourcesJar by
tasks.registering(MergeSourcesJars::class) {
plugins.withId("pklJavaLibrary") { inputJars.from(tasks.named("sourcesJar")) }
inputJars.from(firstPartySourcesJarsConfiguration)
inputJars.from(resolveSourcesJars.map { fileTree(it.outputDir) })
mergedBinaryJars.from(tasks.shadowJar)
relocatedPackages.set(relocations)
outputJar.fileProvider(
provider {
file(tasks.shadowJar.get().archiveFile.get().asFile.path.replace(".jar", "-sources.jar"))
}
)
}
artifacts { add("fatJar", tasks.shadowJar) }
publishing {
publications {
named<MavenPublication>("fatJar") {
from(components["shadow"])
// sources Jar is fat
artifact(fatSourcesJar.flatMap { it.outputJar.asFile }) { classifier = "sources" }
plugins.withId("pklJavaLibrary") {
val javadocJar by tasks.existing(Jar::class)
// Javadoc Jar is not fat (didn't invest effort)
artifact(javadocJar.flatMap { it.archiveFile }) { classifier = "javadoc" }
}
}
}
}