Introduce C library for Pkl (#1238)

This uses native-image to generate a C library for Pkl.
This generated library from native-image is wrapped with our own library,
in `pkl.h`.

This produces a static and a dynamic library for each os/arch variant
that Pkl currently supports.

Co-authored-by: Kushal Pisavadia <kushal.p@apple.com>
Co-authored-by: Jen Basch <jbasch94@gmail.com>
Co-authored-by: Islon Scherer <i_desouzascherer@apple.com>
This commit is contained in:
Daniel Chao
2026-07-25 04:15:26 +00:00
committed by GitHub
co-authored by Kushal Pisavadia Jen Basch Islon Scherer
parent 175e2b6273
commit 67df676359
51 changed files with 4982 additions and 349 deletions
@@ -26,7 +26,10 @@ dependencies {
api(libs.junitApi)
api(libs.junitEngine)
api(libs.junitParams)
api(libs.wiremock)
api(projects.pklCommons) // for convenience
implementation(projects.pklCore)
implementation(projects.pklServer)
implementation(libs.assertj)
implementation(libs.msgpack)
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2025-2026 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.
@@ -179,3 +179,6 @@ class MessagePackDebugRenderer(bytes: ByteArray) {
currIndent.setLength(currIndent.length - indent.length)
}
}
val ByteArray.debugRendering: String
get() = MessagePackDebugRenderer(this).output
@@ -0,0 +1,74 @@
/*
* Copyright © 2026 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.
*/
package org.pkl.commons.test.server
import com.github.tomakehurst.wiremock.client.WireMock.equalTo
import com.github.tomakehurst.wiremock.client.WireMock.get
import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor
import com.github.tomakehurst.wiremock.client.WireMock.ok
import com.github.tomakehurst.wiremock.client.WireMock.stubFor
import com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo
import com.github.tomakehurst.wiremock.client.WireMock.verify
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo
import com.github.tomakehurst.wiremock.junit5.WireMockTest
import java.net.URI
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.pkl.server.EvaluateRequest
import org.pkl.server.EvaluateResponse
import org.pkl.server.Http
/**
* Extends [AbstractServerTest] with HTTP tests backed by a WireMock server.
*
* Some transports can't run these at all (e.g. `LibPklServerTest` - a real outbound HTTP request
* causes `doClose` to hang for about a minute, suspected to be an unclosed `HttpClient`'s default
* thread pool blocking native isolate teardown) - those extend [AbstractServerTest] directly
* instead of this class.
*/
abstract class AbstractHttpServerTest : AbstractServerTest() {
@Nested
@WireMockTest
inner class HttpTests {
@Test
fun `http headers`(wwRuntimeInfo: WireMockRuntimeInfo) {
stubFor(get(urlEqualTo("/foo.pkl")).willReturn(ok("foo = 1")))
val evaluatorId =
client.sendCreateEvaluatorRequest(
http =
Http(
caCertificates = null,
proxy = null,
rewrites = null,
headers = mapOf("**" to mapOf("X-Foo" to listOf("Foo"))),
)
)
client.send(
EvaluateRequest(
1,
evaluatorId,
URI("repl:text"),
"res = import(\"${wwRuntimeInfo.httpBaseUrl}/foo.pkl\")",
"output.text",
)
)
val response = client.receive<EvaluateResponse>()
assertThat(response.error).isNull()
verify(getRequestedFor(urlEqualTo("/foo.pkl")).withHeader("X-Foo", equalTo("Foo")))
}
}
}
@@ -0,0 +1,52 @@
/*
* Copyright © 2024-2026 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.
*/
package org.pkl.commons.test.server
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.BlockingQueue
import org.assertj.core.api.Assertions.*
import org.pkl.core.messaging.Message
import org.pkl.core.messaging.MessageTransport
class TestTransport(private val delegate: MessageTransport) : AutoCloseable {
val incomingMessages: BlockingQueue<Message> = ArrayBlockingQueue(10)
fun start() {
delegate.start({ incomingMessages.put(it) }, { incomingMessages.put(it) })
}
override fun close() {
delegate.close()
}
fun send(message: Message.Client.OneWay) {
delegate.send(message)
}
fun send(message: Message.Client.Request) {
delegate.send(message) { incomingMessages.put(it) }
}
fun send(message: Message.Client.Response) {
delegate.send(message)
}
inline fun <reified T : Message> receive(): T {
val message = incomingMessages.take()
assertThat(message).isInstanceOf(T::class.java)
return message as T
}
}