mirror of
https://github.com/apple/pkl.git
synced 2026-03-18 07:13:54 +01:00
Instead of bundling Pkl's built-in CA certificates as a class path resource and loading them at runtime, pass them to the native image compiler as the default SSL context's trust store. This results in faster SSL initialization and is more consistent with how default certificates are handled when running on the JVM. Further related improvements: - Remove HttpClientBuilder methods `addDefaultCliCertificates` and `addBuiltInCertificates`. - Remove pkl-certs subproject and the optional dependencies on it. - Move `PklCARoots.pem` to `pkl-cli/src/certs`. - Fix certificate related error messages that were missing an argument. - Prevent PklBugException if initialization of `CliBaseOptions.httpClient` fails. - Add ability to set CA certificates as a byte array - Add CA certificates option to message passing API
464 lines
16 KiB
Kotlin
464 lines
16 KiB
Kotlin
import java.security.KeyStore
|
|
import java.security.cert.CertificateFactory
|
|
|
|
plugins {
|
|
pklAllProjects
|
|
pklKotlinLibrary
|
|
pklPublishLibrary
|
|
pklNativeBuild
|
|
`maven-publish`
|
|
|
|
// already on build script class path (see buildSrc/build.gradle.kts),
|
|
// hence must only specify plugin ID here
|
|
@Suppress("DSL_SCOPE_VIOLATION")
|
|
id(libs.plugins.shadow.get().pluginId)
|
|
|
|
@Suppress("DSL_SCOPE_VIOLATION")
|
|
alias(libs.plugins.checksum)
|
|
}
|
|
|
|
// make Java executable available to other subprojects
|
|
val javaExecutableConfiguration: Configuration = configurations.create("javaExecutable")
|
|
|
|
publishing {
|
|
publications {
|
|
named<MavenPublication>("library") {
|
|
pom {
|
|
url.set("https://github.com/apple/pkl/tree/main/pkl-cli")
|
|
description.set("Pkl CLI Java library.")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
val stagedMacAmd64Executable: Configuration by configurations.creating
|
|
val stagedMacAarch64Executable: Configuration by configurations.creating
|
|
val stagedLinuxAmd64Executable: Configuration by configurations.creating
|
|
val stagedLinuxAarch64Executable: Configuration by configurations.creating
|
|
val stagedAlpineLinuxAmd64Executable: Configuration by configurations.creating
|
|
val stagedWindowsAmd64Executable: Configuration by configurations.creating
|
|
|
|
val certs: SourceSet by sourceSets.creating
|
|
|
|
dependencies {
|
|
compileOnly(libs.svm)
|
|
|
|
// CliEvaluator exposes PClass
|
|
api(projects.pklCore)
|
|
// CliEvaluatorOptions exposes CliBaseOptions
|
|
api(projects.pklCommonsCli)
|
|
|
|
implementation(projects.pklCommons)
|
|
implementation(libs.jansi)
|
|
implementation(libs.jlineReader)
|
|
implementation(libs.jlineTerminal)
|
|
implementation(libs.jlineTerminalJansi)
|
|
implementation(projects.pklServer)
|
|
implementation(libs.clikt) {
|
|
// force clikt to use our version of the kotlin stdlib
|
|
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8")
|
|
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-common")
|
|
}
|
|
|
|
testImplementation(projects.pklCommonsTest)
|
|
testImplementation(libs.wiremock)
|
|
|
|
fun executableDir(name: String) = files(layout.buildDirectory.dir("executable/$name"))
|
|
stagedMacAmd64Executable(executableDir("pkl-macos-amd64"))
|
|
stagedMacAmd64Executable(executableDir("pkl-macos-amd64"))
|
|
stagedMacAarch64Executable(executableDir("pkl-macos-aarch64"))
|
|
stagedLinuxAmd64Executable(executableDir("pkl-linux-amd64"))
|
|
stagedLinuxAarch64Executable(executableDir("pkl-linux-aarch64"))
|
|
stagedAlpineLinuxAmd64Executable(executableDir("pkl-alpine-linux-amd64"))
|
|
stagedWindowsAmd64Executable(executableDir("pkl-windows-amd64.exe"))
|
|
}
|
|
|
|
tasks.jar {
|
|
manifest {
|
|
attributes += mapOf("Main-Class" to "org.pkl.cli.Main")
|
|
}
|
|
|
|
// not required at runtime
|
|
exclude("org/pkl/cli/svm/**")
|
|
}
|
|
|
|
tasks.javadoc {
|
|
enabled = false
|
|
}
|
|
|
|
tasks.shadowJar {
|
|
archiveFileName.set("jpkl")
|
|
|
|
exclude("META-INF/maven/**")
|
|
exclude("META-INF/upgrade/**")
|
|
|
|
// org.antlr.v4.runtime.misc.RuleDependencyProcessor
|
|
exclude("META-INF/services/javax.annotation.processing.Processor")
|
|
|
|
exclude("module-info.*")
|
|
}
|
|
|
|
val javaExecutable by tasks.registering(ExecutableJar::class) {
|
|
inJar.set(tasks.shadowJar.flatMap { it.archiveFile })
|
|
outJar.set(layout.buildDirectory.file("executable/jpkl"))
|
|
|
|
// uncomment for debugging
|
|
//jvmArgs.addAll("-ea", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005")
|
|
}
|
|
|
|
val testJavaExecutable by tasks.registering(Test::class) {
|
|
testClassesDirs = tasks.test.get().testClassesDirs
|
|
classpath =
|
|
// compiled test classes
|
|
sourceSets.test.get().output +
|
|
// java executable
|
|
javaExecutable.get().outputs.files +
|
|
// test-only dependencies
|
|
// (test dependencies that are also main dependencies must already be contained in java executable;
|
|
// to verify that, we don't want to include them here)
|
|
(configurations.testRuntimeClasspath.get() - configurations.runtimeClasspath.get())
|
|
}
|
|
|
|
tasks.check {
|
|
dependsOn(testJavaExecutable)
|
|
}
|
|
|
|
// 0.14 Java executable was broken because javaExecutable.jvmArgs wasn't commented out.
|
|
// To catch this and similar problems, test that Java executable starts successfully.
|
|
val testStartJavaExecutable by tasks.registering(Exec::class) {
|
|
dependsOn(javaExecutable)
|
|
val outputFile = layout.buildDirectory.file("testStartJavaExecutable") // dummy output to satisfy up-to-date check
|
|
outputs.file(outputFile)
|
|
|
|
if (buildInfo.os.isWindows) {
|
|
executable = "java"
|
|
args("-jar", javaExecutable.get().outputs.files.singleFile.toString(), "--version")
|
|
} else {
|
|
executable = javaExecutable.get().outputs.files.singleFile.toString()
|
|
args("--version")
|
|
}
|
|
|
|
doFirst { outputFile.get().asFile.delete() }
|
|
|
|
doLast { outputFile.get().asFile.writeText("OK") }
|
|
}
|
|
|
|
tasks.check {
|
|
dependsOn(testStartJavaExecutable)
|
|
}
|
|
|
|
val trustStore = layout.buildDirectory.dir("generateTrustStore/PklCARoots.p12")
|
|
val trustStorePassword = "password" // no sensitive data to protect
|
|
|
|
// generate a trust store for Pkl's built-in CA certificates
|
|
val generateTrustStore by tasks.registering {
|
|
inputs.file(certs.resources.singleFile)
|
|
outputs.file(trustStore)
|
|
doLast {
|
|
val certificates = certs.resources.singleFile.inputStream().use { stream ->
|
|
CertificateFactory.getInstance("X.509").generateCertificates(stream)
|
|
}
|
|
KeyStore.getInstance("PKCS12").apply {
|
|
load(null, trustStorePassword.toCharArray()) // initialize empty trust store
|
|
for ((index, certificate) in certificates.withIndex()) {
|
|
setCertificateEntry("cert-$index", certificate)
|
|
}
|
|
val trustStoreFile = trustStore.get().asFile
|
|
trustStoreFile.parentFile.mkdirs()
|
|
trustStoreFile.outputStream().use { stream ->
|
|
store(stream, trustStorePassword.toCharArray())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fun Exec.configureExecutable(
|
|
graalVm: BuildInfo.GraalVm,
|
|
outputFile: Provider<RegularFile>,
|
|
extraArgs: List<String> = listOf()
|
|
) {
|
|
dependsOn(generateTrustStore)
|
|
|
|
inputs.files(sourceSets.main.map { it.output })
|
|
.withPropertyName("mainSourceSets")
|
|
.withPathSensitivity(PathSensitivity.RELATIVE)
|
|
inputs.files(configurations.runtimeClasspath)
|
|
.withPropertyName("runtimeClasspath")
|
|
.withNormalizer(ClasspathNormalizer::class)
|
|
val nativeImageCommandName = if (buildInfo.os.isWindows) "native-image.cmd" else "native-image"
|
|
inputs.files(file(graalVm.baseDir)
|
|
.resolve("bin/$nativeImageCommandName"))
|
|
.withPropertyName("graalVmNativeImage")
|
|
.withPathSensitivity(PathSensitivity.ABSOLUTE)
|
|
outputs.file(outputFile)
|
|
outputs.cacheIf { true }
|
|
|
|
workingDir(outputFile.map { it.asFile.parentFile })
|
|
executable = "${graalVm.baseDir}/bin/$nativeImageCommandName"
|
|
|
|
// JARs to exclude from the class path for the native-image build.
|
|
val exclusions = listOf(libs.truffleApi, libs.graalSdk).map { it.get().module.name }
|
|
// https://www.graalvm.org/22.0/reference-manual/native-image/Options/
|
|
argumentProviders.add(CommandLineArgumentProvider {
|
|
listOf(
|
|
// currently gives a deprecation warning, but we've been told
|
|
// that the "initialize everything at build time" *CLI* option is likely here to stay
|
|
"--initialize-at-build-time="
|
|
// needed for messagepack-java (see https://github.com/msgpack/msgpack-java/issues/600)
|
|
,"--initialize-at-run-time=org.msgpack.core.buffer.DirectBufferAccess"
|
|
,"--no-fallback"
|
|
,"-Djavax.net.ssl.trustStore=${trustStore.get().asFile}"
|
|
,"-Djavax.net.ssl.trustStorePassword=$trustStorePassword"
|
|
,"-Djavax.net.ssl.trustStoreType=PKCS12"
|
|
// security property "ocsp.enable=true" is set in Main.kt
|
|
,"-Dcom.sun.net.ssl.checkRevocation=true"
|
|
,"-H:IncludeResources=org/pkl/core/stdlib/.*\\.pkl"
|
|
,"-H:IncludeResources=org/jline/utils/.*"
|
|
,"-H:IncludeResourceBundles=org.pkl.core.errorMessages"
|
|
,"--macro:truffle"
|
|
,"-H:Class=org.pkl.cli.Main"
|
|
,"-H:Name=${outputFile.get().asFile.name}"
|
|
// the actual limit (currently) used by native-image is this number + 1400 (idea is to compensate for Truffle's own nodes)
|
|
,"-H:MaxRuntimeCompileMethods=1800"
|
|
,"-H:+EnforceMaxRuntimeCompileMethods"
|
|
,"--enable-url-protocols=http,https"
|
|
,"-H:+ReportExceptionStackTraces"
|
|
// disable automatic support for JVM CLI options (puts our main class in full control of argument parsing)
|
|
,"-H:-ParseRuntimeOptions"
|
|
// quick build mode: 40% faster compilation, 20% smaller (but presumably also slower) executable
|
|
,if (!buildInfo.isReleaseBuild) "-Ob" else ""
|
|
// native-image rejects non-existing class path entries -> filter
|
|
,"--class-path"
|
|
,((sourceSets.main.get().output + configurations.runtimeClasspath.get())
|
|
.filter { it.exists() && !exclusions.any { exclude -> it.name.contains(exclude) }})
|
|
.asPath
|
|
// make sure dev machine stays responsive (15% slowdown on my laptop)
|
|
,"-J-XX:ActiveProcessorCount=${
|
|
Runtime.getRuntime().availableProcessors() / (if (buildInfo.os.isMacOsX && !buildInfo.isCiBuild) 4 else 1)
|
|
}"
|
|
) + extraArgs
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Builds the pkl CLI for macOS/amd64.
|
|
*/
|
|
val macExecutableAmd64: TaskProvider<Exec> by tasks.registering(Exec::class) {
|
|
dependsOn(":installGraalVmAmd64")
|
|
configureExecutable(
|
|
buildInfo.graalVmAmd64,
|
|
layout.buildDirectory.file("executable/pkl-macos-amd64")
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Builds the pkl CLI for macOS/aarch64.
|
|
*/
|
|
val macExecutableAarch64: TaskProvider<Exec> by tasks.registering(Exec::class) {
|
|
dependsOn(":installGraalVmAarch64")
|
|
configureExecutable(
|
|
buildInfo.graalVmAarch64,
|
|
layout.buildDirectory.file("executable/pkl-macos-aarch64"),
|
|
listOf(
|
|
"-H:+AllowDeprecatedBuilderClassesOnImageClasspath"
|
|
)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Builds the pkl CLI for linux/amd64.
|
|
*/
|
|
val linuxExecutableAmd64: TaskProvider<Exec> by tasks.registering(Exec::class) {
|
|
dependsOn(":installGraalVmAmd64")
|
|
configureExecutable(
|
|
buildInfo.graalVmAmd64,
|
|
layout.buildDirectory.file("executable/pkl-linux-amd64")
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Builds the pkl CLI for linux/aarch64.
|
|
*
|
|
* Right now, this is built within a container on Mac using emulation because CI does not have
|
|
* ARM instances.
|
|
*/
|
|
val linuxExecutableAarch64: TaskProvider<Exec> by tasks.registering(Exec::class) {
|
|
dependsOn(":installGraalVmAarch64")
|
|
configureExecutable(
|
|
buildInfo.graalVmAarch64,
|
|
layout.buildDirectory.file("executable/pkl-linux-aarch64")
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Builds a statically linked CLI for linux/amd64.
|
|
*
|
|
* Note: we don't publish the same for linux/aarch64 because native-image doesn't support this.
|
|
* Details: https://www.graalvm.org/22.0/reference-manual/native-image/ARM64/
|
|
*/
|
|
val alpineExecutableAmd64: TaskProvider<Exec> by tasks.registering(Exec::class) {
|
|
dependsOn(":installGraalVmAmd64")
|
|
configureExecutable(
|
|
buildInfo.graalVmAmd64,
|
|
layout.buildDirectory.file("executable/pkl-alpine-linux-amd64"),
|
|
listOf("--static", "--libc=musl")
|
|
)
|
|
}
|
|
|
|
val windowsExecutableAmd64: TaskProvider<Exec> by tasks.registering(Exec::class) {
|
|
dependsOn(":installGraalVmAmd64")
|
|
configureExecutable(
|
|
buildInfo.graalVmAmd64,
|
|
layout.buildDirectory.file("executable/pkl-windows-amd64"),
|
|
listOf("-Dfile.encoding=UTF-8")
|
|
)
|
|
}
|
|
|
|
tasks.assembleNative {
|
|
when {
|
|
buildInfo.os.isMacOsX -> {
|
|
dependsOn(macExecutableAmd64)
|
|
if (buildInfo.arch == "aarch64") {
|
|
dependsOn(macExecutableAarch64)
|
|
}
|
|
}
|
|
buildInfo.os.isWindows -> {
|
|
dependsOn(windowsExecutableAmd64)
|
|
}
|
|
buildInfo.os.isLinux && buildInfo.arch == "aarch64" -> {
|
|
dependsOn(linuxExecutableAarch64)
|
|
}
|
|
buildInfo.os.isLinux && buildInfo.arch == "amd64" -> {
|
|
dependsOn(linuxExecutableAmd64)
|
|
if (buildInfo.hasMuslToolchain) {
|
|
dependsOn(alpineExecutableAmd64)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// make Java executable available to other subprojects
|
|
// (we don't do the same for native executables because we don't want tasks assemble/build to build them)
|
|
artifacts {
|
|
add("javaExecutable", javaExecutable.map { it.outputs.files.singleFile }) {
|
|
name = "pkl-cli-java"
|
|
classifier = null
|
|
extension = "jar"
|
|
builtBy(javaExecutable)
|
|
}
|
|
}
|
|
|
|
//region Maven Publishing
|
|
publishing {
|
|
publications {
|
|
register<MavenPublication>("javaExecutable") {
|
|
artifactId = "pkl-cli-java"
|
|
|
|
artifact(javaExecutable.map { it.outputs.files.singleFile }) {
|
|
classifier = null
|
|
extension = "jar"
|
|
builtBy(javaExecutable)
|
|
}
|
|
|
|
pom {
|
|
url.set("https://github.com/apple/pkl/tree/main/pkl-cli")
|
|
description.set("""
|
|
Pkl CLI executable for Java.
|
|
Can be executed directly on *nix (if the `java` command is found on the PATH) and with `java -jar` otherwise.
|
|
Requires Java 17 or higher.
|
|
""".trimIndent())
|
|
}
|
|
}
|
|
create<MavenPublication>("macExecutableAmd64") {
|
|
artifactId = "pkl-cli-macos-amd64"
|
|
artifact(stagedMacAmd64Executable.singleFile) {
|
|
classifier = null
|
|
extension = "bin"
|
|
builtBy(stagedMacAmd64Executable)
|
|
}
|
|
pom {
|
|
name.set("pkl-cli-macos-amd64")
|
|
url.set("https://github.com/apple/pkl/tree/main/pkl-cli")
|
|
description.set("Native Pkl CLI executable for macOS/amd64.")
|
|
}
|
|
}
|
|
create<MavenPublication>("macExecutableAarch64") {
|
|
artifactId = "pkl-cli-macos-aarch64"
|
|
artifact(stagedMacAarch64Executable.singleFile) {
|
|
classifier = null
|
|
extension = "bin"
|
|
builtBy(stagedMacAarch64Executable)
|
|
}
|
|
pom {
|
|
name.set("pkl-cli-macos-aarch64")
|
|
url.set("https://github.com/apple/pkl/tree/main/pkl-cli")
|
|
description.set("Native Pkl CLI executable for macOS/aarch64.")
|
|
}
|
|
}
|
|
create<MavenPublication>("linuxExecutableAmd64") {
|
|
artifactId = "pkl-cli-linux-amd64"
|
|
artifact(stagedLinuxAmd64Executable.singleFile) {
|
|
classifier = null
|
|
extension = "bin"
|
|
builtBy(stagedLinuxAmd64Executable)
|
|
}
|
|
pom {
|
|
name.set("pkl-cli-linux-amd64")
|
|
url.set("https://github.com/apple/pkl/tree/main/pkl-cli")
|
|
description.set("Native Pkl CLI executable for linux/amd64.")
|
|
}
|
|
}
|
|
create<MavenPublication>("linuxExecutableAarch64") {
|
|
artifactId = "pkl-cli-linux-aarch64"
|
|
artifact(stagedLinuxAarch64Executable.singleFile) {
|
|
classifier = null
|
|
extension = "bin"
|
|
builtBy(stagedLinuxAarch64Executable)
|
|
}
|
|
pom {
|
|
name.set("pkl-cli-linux-aarch64")
|
|
url.set("https://github.com/apple/pkl/tree/main/pkl-cli")
|
|
description.set("Native Pkl CLI executable for linux/aarch64.")
|
|
}
|
|
}
|
|
create<MavenPublication>("alpineLinuxExecutableAmd64") {
|
|
artifactId = "pkl-cli-alpine-linux-amd64"
|
|
artifact(stagedAlpineLinuxAmd64Executable.singleFile) {
|
|
classifier = null
|
|
extension = "bin"
|
|
builtBy(stagedAlpineLinuxAmd64Executable)
|
|
}
|
|
pom {
|
|
name.set("pkl-cli-alpine-linux-amd64")
|
|
url.set("https://github.com/apple/pkl/tree/main/pkl-cli")
|
|
description.set("Native Pkl CLI executable for linux/amd64 and statically linked to musl.")
|
|
}
|
|
}
|
|
|
|
create<MavenPublication>("windowsExecutableAmd64") {
|
|
artifactId = "pkl-cli-windows-amd64"
|
|
artifact(stagedWindowsAmd64Executable.singleFile) {
|
|
classifier = null
|
|
extension = "exe"
|
|
builtBy(stagedWindowsAmd64Executable)
|
|
}
|
|
pom {
|
|
name.set("pkl-cli-windows-amd64")
|
|
url.set("https://github.com/apple/pkl/tree/main/pkl-cli")
|
|
description.set("Native Pkl CLI executable for windows/amd64.")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
signing {
|
|
sign(publishing.publications["javaExecutable"])
|
|
sign(publishing.publications["linuxExecutableAarch64"])
|
|
sign(publishing.publications["linuxExecutableAmd64"])
|
|
sign(publishing.publications["macExecutableAarch64"])
|
|
sign(publishing.publications["macExecutableAmd64"])
|
|
sign(publishing.publications["alpineLinuxExecutableAmd64"])
|
|
sign(publishing.publications["windowsExecutableAmd64"])
|
|
}
|
|
//endregion
|