From cf2faedfa62a60f5d235012bf1dfa7549e09d2f3 Mon Sep 17 00:00:00 2001 From: Dan Chao Date: Wed, 5 Aug 2026 11:45:48 -0700 Subject: [PATCH 01/10] Split libpkl libraries between shared and static libs This adds a `libpkl-static.pc` so pkg-config users can link either statically or dynamically via `pkg-config --libs libpkl`, or `pkg-config --libs libpkl-static`. Additionally, this fixes an issue where the replace tokens wasn't doing anything, leaving the `@version@` token intact in the archive. This also changes the format of the archive to put the static lib in its own subdirectory, and the shared lib in its own subdirectory. --- docs/modules/libpkl/pages/index.adoc | 27 ++++++++++++- libpkl/libpkl.gradle.kts | 38 ++++++++++++------- .../main/files/lib/pkgconfig/libpkl-static.pc | 11 ++++++ libpkl/src/main/files/lib/pkgconfig/libpkl.pc | 2 +- 4 files changed, 61 insertions(+), 17 deletions(-) create mode 100644 libpkl/src/main/files/lib/pkgconfig/libpkl-static.pc diff --git a/docs/modules/libpkl/pages/index.adoc b/docs/modules/libpkl/pages/index.adoc index 20b7bacb1..7158ded23 100644 --- a/docs/modules/libpkl/pages/index.adoc +++ b/docs/modules/libpkl/pages/index.adoc @@ -32,9 +32,12 @@ The archive has the following structure: ├── include │ └── pkl.h ├── lib - │ ├── libpkl. - │ ├── libpkl. + | └── shared + | | └── libpkl. + | └── static + | | └── libpkl. │ └── pkgconfig + | ├── libpkl-static.pc │ └── libpkl.pc ├── LICENSE.txt ├── README.md @@ -58,6 +61,26 @@ The file extensions are as follows: |`.dll` |=== +=== Linking to the static vs. shared library + +On macOS and Linux, the simplest way to configure your compiler to link to either the static or the shared library is to use https://en.wikipedia.org/wiki/Pkg-config[pkg-config]. + +The libpkl library ships with two pkg-config files: `libpkl.pc`, and `libpkl-static.pc`. + +To link to the static library, pass in library "libpkl-static". +To link to the shared library, pass in library "libpkl". + +For example, when using `cc`: + +[source,shell] +---- +# Link against the shared library +cc -o myprogram myprogram.c $(pkg-config --libs --cflags libpkl) + +# Link against the static library +cc -o myprogram myprogram.c $(pkg-config --static --libs --cflags libpkl-static) +---- + == Building libpkl from source To build libpkl from source, the apple/pkl repository first must be cloned down to a host OS. diff --git a/libpkl/libpkl.gradle.kts b/libpkl/libpkl.gradle.kts index 280d94de4..33c9b0607 100644 --- a/libpkl/libpkl.gradle.kts +++ b/libpkl/libpkl.gradle.kts @@ -212,7 +212,7 @@ val buildStaticLibrary = outputFile = targetMachine.outputDir.map { dir -> - dir.file("lib/libpkl.${targetMachine.os.staticLibraryExtension}") + dir.file("lib/static/libpkl.${targetMachine.os.staticLibraryExtension}") } } @@ -245,7 +245,7 @@ val buildSharedLibrary = positionIndependentCode = true sharedLibrary = true - outputFile = targetMachine.outputDir.map { it.file("lib/libpkl.${extension}") } + outputFile = targetMachine.outputDir.map { it.file("lib/shared/libpkl.${extension}") } if (buildInfo.os.isMacOS) { frameworks.addAll("Foundation", "CoreServices") @@ -291,6 +291,12 @@ val Target.outputDir val Target.libraryDir get() = layout.buildDirectory.dir("native-libs/$targetName/lib") +val Target.sharedLibraryDir + get() = layout.buildDirectory.dir("native-libs/$targetName/lib/shared") + +val Target.staticLibraryDir + get() = layout.buildDirectory.dir("native-libs/$targetName/lib/static") + val Target.tempOutputDir get() = layout.buildDirectory.dir("tmp/native-libs/$targetName") @@ -329,8 +335,14 @@ val processFiles = includeEmptyDirs = true into(buildInfo.targetMachine.outputDir) - filesMatching("libpkl.pc") { - filter("tokens" to mapOf("version" to buildInfo.pklVersion)) + val tokens = buildMap { + this["version"] = buildInfo.pklVersion + this["extra_static_libs"] = + if (buildInfo.os.isMacOS) " -framework Foundation -framework CoreServices" + else "" + } + filesMatching("**/libpkl*.pc") { + filter("tokens" to tokens) } } @@ -349,14 +361,14 @@ val testNativeJava = jvmArgumentProviders.add( CommandLineArgumentProvider { listOf( - "-Djna.library.path=" + buildInfo.targetMachine.libraryDir.get().asFile.absolutePath, - "-Djava.library.path=" + buildInfo.targetMachine.libraryDir.get().asFile.absolutePath, + "-Djna.library.path=" + buildInfo.targetMachine.sharedLibraryDir.get().asFile.absolutePath, + "-Djava.library.path=" + buildInfo.targetMachine.sharedLibraryDir.get().asFile.absolutePath, "--enable-native-access=ALL-UNNAMED", ) } ) - environment("LD_LIBRARY_PATH", buildInfo.targetMachine.libraryDir.get().asFile.absolutePath) + environment("LD_LIBRARY_PATH", buildInfo.targetMachine.sharedLibraryDir.get().asFile.absolutePath) useJUnitPlatform() } @@ -369,7 +381,7 @@ val compileNativeTestStatic = dependsOn(tasks.assembleNative) - val libDir = buildInfo.targetMachine.libraryDir.get().asFile + val libDir = buildInfo.targetMachine.staticLibraryDir.get().asFile val testSrc = file("src/nativeTest/c/test_pkl.c") link = true @@ -420,20 +432,18 @@ val compileNativeTestDynamic = "native-test/${buildInfo.targetMachine.targetName}/test_pkl_dynamic" ) - libraryPaths.from(buildInfo.targetMachine.libraryDir) + libraryPaths.from(buildInfo.targetMachine.sharedLibraryDir) if (buildInfo.os.isWindows) { // "pkl" would resolve to libpkl.lib, which is buildStaticLibrary's static archive, not // the DLL's import library — link against the import library directly instead. - libraryFiles.from(buildInfo.targetMachine.libraryDir.map { it.file("libpkl_dll.lib") }) + libraryFiles.from(buildInfo.targetMachine.sharedLibraryDir.map { it.file("libpkl_dll.lib") }) } else { libraries.add("pkl") - libraries.add("z") - libraries.add("pthread") // Bake the library's location into the test executable so the dynamic loader can find // libpkl.so/libpkl.dylib without needing LD_LIBRARY_PATH/DYLD_LIBRARY_PATH set at run time. linkerFlags.add("-rpath") - linkerFlags.add(buildInfo.targetMachine.libraryDir.get().asFile.absolutePath) + linkerFlags.add(buildInfo.targetMachine.sharedLibraryDir.get().asFile.absolutePath) } if (buildInfo.os.isLinux) { @@ -490,7 +500,7 @@ val testNativeCDynamic = doFirst { environment( "PATH", - "${buildInfo.targetMachine.libraryDir.get().asFile.absolutePath};${System.getenv("PATH")}", + "${buildInfo.targetMachine.sharedLibraryDir.get().asFile.absolutePath};${System.getenv("PATH")}", ) } } diff --git a/libpkl/src/main/files/lib/pkgconfig/libpkl-static.pc b/libpkl/src/main/files/lib/pkgconfig/libpkl-static.pc new file mode 100644 index 000000000..a19c0e0c1 --- /dev/null +++ b/libpkl/src/main/files/lib/pkgconfig/libpkl-static.pc @@ -0,0 +1,11 @@ +prefix=${pcfiledir}/../.. +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: libpkl +Description: C library for calling into Pkl's message passing API. +URL: https://pkl-lang.org/main/current/libpkl/index.html +Version: @version@ +Libs: -L${libdir}/static -lpkl -lz -lpthread@extra_static_libs@ +Cflags: -I${includedir} diff --git a/libpkl/src/main/files/lib/pkgconfig/libpkl.pc b/libpkl/src/main/files/lib/pkgconfig/libpkl.pc index 19470ca2c..dcfebd1dd 100644 --- a/libpkl/src/main/files/lib/pkgconfig/libpkl.pc +++ b/libpkl/src/main/files/lib/pkgconfig/libpkl.pc @@ -7,5 +7,5 @@ Name: libpkl Description: C library for calling into Pkl's message passing API. URL: https://pkl-lang.org/main/current/libpkl/index.html Version: @version@ -Libs: -L${libdir} -lpkl -lz -lpthread +Libs: -L${libdir}/shared -lpkl -lz -lpthread Cflags: -I${includedir} From c871981bc503b783550e61f1bc5f8eb9b4a833fc Mon Sep 17 00:00:00 2001 From: Dan Chao Date: Wed, 5 Aug 2026 12:29:15 -0700 Subject: [PATCH 02/10] Fix windows build --- libpkl/libpkl.gradle.kts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/libpkl/libpkl.gradle.kts b/libpkl/libpkl.gradle.kts index 33c9b0607..d3c0eb50d 100644 --- a/libpkl/libpkl.gradle.kts +++ b/libpkl/libpkl.gradle.kts @@ -267,7 +267,7 @@ val buildSharedLibrary = // Without an explicit /IMPLIB, MSVC would name the DLL's import library "libpkl.lib" — // the same path buildStaticLibrary writes its static archive to. linkerFlags.add( - targetMachine.libraryDir.map { dir -> "/IMPLIB:${dir.file("libpkl_dll.lib").asFile}" } + targetMachine.sharedLibraryDir.map { dir -> "/IMPLIB:${dir.file("libpkl_dll.lib").asFile}" } ) } else { libraries.add("z") @@ -338,12 +338,9 @@ val processFiles = val tokens = buildMap { this["version"] = buildInfo.pklVersion this["extra_static_libs"] = - if (buildInfo.os.isMacOS) " -framework Foundation -framework CoreServices" - else "" - } - filesMatching("**/libpkl*.pc") { - filter("tokens" to tokens) + if (buildInfo.os.isMacOS) " -framework Foundation -framework CoreServices" else "" } + filesMatching("**/libpkl*.pc") { filter("tokens" to tokens) } } val testNativeJava = @@ -361,14 +358,19 @@ val testNativeJava = jvmArgumentProviders.add( CommandLineArgumentProvider { listOf( - "-Djna.library.path=" + buildInfo.targetMachine.sharedLibraryDir.get().asFile.absolutePath, - "-Djava.library.path=" + buildInfo.targetMachine.sharedLibraryDir.get().asFile.absolutePath, + "-Djna.library.path=" + + buildInfo.targetMachine.sharedLibraryDir.get().asFile.absolutePath, + "-Djava.library.path=" + + buildInfo.targetMachine.sharedLibraryDir.get().asFile.absolutePath, "--enable-native-access=ALL-UNNAMED", ) } ) - environment("LD_LIBRARY_PATH", buildInfo.targetMachine.sharedLibraryDir.get().asFile.absolutePath) + environment( + "LD_LIBRARY_PATH", + buildInfo.targetMachine.sharedLibraryDir.get().asFile.absolutePath, + ) useJUnitPlatform() } From 535091ec0223fed8d6b03408f121c2852ed83d87 Mon Sep 17 00:00:00 2001 From: Dan Chao Date: Wed, 5 Aug 2026 12:44:28 -0700 Subject: [PATCH 03/10] fix pkg-config files for all OSes --- libpkl/libpkl.gradle.kts | 12 ++++++++++-- libpkl/src/main/files/lib/pkgconfig/libpkl-static.pc | 2 +- libpkl/src/main/files/lib/pkgconfig/libpkl.pc | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/libpkl/libpkl.gradle.kts b/libpkl/libpkl.gradle.kts index d3c0eb50d..1bd8344db 100644 --- a/libpkl/libpkl.gradle.kts +++ b/libpkl/libpkl.gradle.kts @@ -337,8 +337,16 @@ val processFiles = val tokens = buildMap { this["version"] = buildInfo.pklVersion - this["extra_static_libs"] = - if (buildInfo.os.isMacOS) " -framework Foundation -framework CoreServices" else "" + this["extra_static_libs"] = when { + buildInfo.os.isMacOS -> " -lpkl -lz -framework Foundation -framework CoreServices" + buildInfo.os.isWindows -> " -l:libpkl.lib" + else -> " -lpkl -lz" + } + this["extra_shared_libs"] = when { + buildInfo.os.isMacOS -> " -lpkl" + buildInfo.os.isWindows -> " -l:libpkl_dll.lib" + else -> " -lpkl" + } } filesMatching("**/libpkl*.pc") { filter("tokens" to tokens) } } diff --git a/libpkl/src/main/files/lib/pkgconfig/libpkl-static.pc b/libpkl/src/main/files/lib/pkgconfig/libpkl-static.pc index a19c0e0c1..ff3cb8d5e 100644 --- a/libpkl/src/main/files/lib/pkgconfig/libpkl-static.pc +++ b/libpkl/src/main/files/lib/pkgconfig/libpkl-static.pc @@ -7,5 +7,5 @@ Name: libpkl Description: C library for calling into Pkl's message passing API. URL: https://pkl-lang.org/main/current/libpkl/index.html Version: @version@ -Libs: -L${libdir}/static -lpkl -lz -lpthread@extra_static_libs@ +Libs: -L${libdir}/static@extra_static_libs@ Cflags: -I${includedir} diff --git a/libpkl/src/main/files/lib/pkgconfig/libpkl.pc b/libpkl/src/main/files/lib/pkgconfig/libpkl.pc index dcfebd1dd..67ec4b682 100644 --- a/libpkl/src/main/files/lib/pkgconfig/libpkl.pc +++ b/libpkl/src/main/files/lib/pkgconfig/libpkl.pc @@ -7,5 +7,5 @@ Name: libpkl Description: C library for calling into Pkl's message passing API. URL: https://pkl-lang.org/main/current/libpkl/index.html Version: @version@ -Libs: -L${libdir}/shared -lpkl -lz -lpthread +Libs: -L${libdir}/shared@extra_shared_libs@ Cflags: -I${includedir} From acf81b501693a903c2587a2ffed9d76ff918ffa5 Mon Sep 17 00:00:00 2001 From: Dan Chao Date: Wed, 5 Aug 2026 12:46:55 -0700 Subject: [PATCH 04/10] Run spotless apply --- libpkl/libpkl.gradle.kts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/libpkl/libpkl.gradle.kts b/libpkl/libpkl.gradle.kts index 1bd8344db..0a9d6d7b3 100644 --- a/libpkl/libpkl.gradle.kts +++ b/libpkl/libpkl.gradle.kts @@ -337,16 +337,18 @@ val processFiles = val tokens = buildMap { this["version"] = buildInfo.pklVersion - this["extra_static_libs"] = when { - buildInfo.os.isMacOS -> " -lpkl -lz -framework Foundation -framework CoreServices" - buildInfo.os.isWindows -> " -l:libpkl.lib" - else -> " -lpkl -lz" - } - this["extra_shared_libs"] = when { - buildInfo.os.isMacOS -> " -lpkl" - buildInfo.os.isWindows -> " -l:libpkl_dll.lib" - else -> " -lpkl" - } + this["extra_static_libs"] = + when { + buildInfo.os.isMacOS -> " -lpkl -lz -framework Foundation -framework CoreServices" + buildInfo.os.isWindows -> " -l:libpkl.lib" + else -> " -lpkl -lz" + } + this["extra_shared_libs"] = + when { + buildInfo.os.isMacOS -> " -lpkl" + buildInfo.os.isWindows -> " -l:libpkl_dll.lib" + else -> " -lpkl" + } } filesMatching("**/libpkl*.pc") { filter("tokens" to tokens) } } From d5e472516be8ce544712e332444b5a50ef6c41f1 Mon Sep 17 00:00:00 2001 From: Dan Chao Date: Wed, 5 Aug 2026 13:01:41 -0700 Subject: [PATCH 05/10] Update docs --- docs/modules/libpkl/pages/index.adoc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/modules/libpkl/pages/index.adoc b/docs/modules/libpkl/pages/index.adoc index 7158ded23..794f36644 100644 --- a/docs/modules/libpkl/pages/index.adoc +++ b/docs/modules/libpkl/pages/index.adoc @@ -63,7 +63,7 @@ The file extensions are as follows: === Linking to the static vs. shared library -On macOS and Linux, the simplest way to configure your compiler to link to either the static or the shared library is to use https://en.wikipedia.org/wiki/Pkg-config[pkg-config]. +The simplest way to configure your compiler to link to either the static or the shared library is to use https://en.wikipedia.org/wiki/Pkg-config[pkg-config]. The libpkl library ships with two pkg-config files: `libpkl.pc`, and `libpkl-static.pc`. @@ -81,6 +81,11 @@ cc -o myprogram myprogram.c $(pkg-config --libs --cflags libpkl) cc -o myprogram myprogram.c $(pkg-config --static --libs --cflags libpkl-static) ---- +If libpkl is installed in a non-system directory, set `PKG_CONFIG_PATH` to the subpath `lib/pkgconfig` within directory that contains libpkl. For example, given libpkl directory `/path/to/libpkl`, the environment variable should have `/path/to/libpkl/lib/pkgconfig`. + +NOTE: Windows can use pkg-config through the https://www.msys2.org/docs/pkgconfig/[MSYS2 toolchain]. Using pkg-config on Windows implies compiling with gcc-style compiler like https://code.visualstudio.com/docs/cpp/config-mingw[MinGW], instead of MSVC). + + == Building libpkl from source To build libpkl from source, the apple/pkl repository first must be cloned down to a host OS. From 8aee4ff14881292d67e91665bdeaf1c4ba85fa70 Mon Sep 17 00:00:00 2001 From: Dan Chao Date: Wed, 5 Aug 2026 21:52:44 -0700 Subject: [PATCH 06/10] Move both libs back to same dir; link against static lib via direct argument --- docs/modules/libpkl/pages/index.adoc | 27 ++++++++--- libpkl/libpkl.gradle.kts | 45 ++++++++----------- .../main/files/lib/pkgconfig/libpkl-static.pc | 2 +- libpkl/src/main/files/lib/pkgconfig/libpkl.pc | 2 +- 4 files changed, 41 insertions(+), 35 deletions(-) diff --git a/docs/modules/libpkl/pages/index.adoc b/docs/modules/libpkl/pages/index.adoc index 794f36644..c5ce56d42 100644 --- a/docs/modules/libpkl/pages/index.adoc +++ b/docs/modules/libpkl/pages/index.adoc @@ -1,4 +1,5 @@ = libpkl +include::ROOT:partial$component-attributes.adoc[] libpkl is a C library for driving Pkl evaluation. @@ -32,12 +33,10 @@ The archive has the following structure: ├── include │ └── pkl.h ├── lib - | └── shared - | | └── libpkl. - | └── static - | | └── libpkl. + │ ├── libpkl. + │ ├── libpkl. │ └── pkgconfig - | ├── libpkl-static.pc + │ ├── libpkl-static.pc │ └── libpkl.pc ├── LICENSE.txt ├── README.md @@ -67,8 +66,9 @@ The simplest way to configure your compiler to link to either the static or the The libpkl library ships with two pkg-config files: `libpkl.pc`, and `libpkl-static.pc`. -To link to the static library, pass in library "libpkl-static". -To link to the shared library, pass in library "libpkl". +The normal `libpkl.pc` links to the shared library. + +To unambiguously link to the static library, use `libpkl-static.pc`. For example, when using `cc`: @@ -81,10 +81,22 @@ cc -o myprogram myprogram.c $(pkg-config --libs --cflags libpkl) cc -o myprogram myprogram.c $(pkg-config --static --libs --cflags libpkl-static) ---- + If libpkl is installed in a non-system directory, set `PKG_CONFIG_PATH` to the subpath `lib/pkgconfig` within directory that contains libpkl. For example, given libpkl directory `/path/to/libpkl`, the environment variable should have `/path/to/libpkl/lib/pkgconfig`. NOTE: Windows can use pkg-config through the https://www.msys2.org/docs/pkgconfig/[MSYS2 toolchain]. Using pkg-config on Windows implies compiling with gcc-style compiler like https://code.visualstudio.com/docs/cpp/config-mingw[MinGW], instead of MSVC). +[NOTE] +==== +It's also possible to link to the static library with the normal `libpkl.pc`. + +For example: + +[source,shell] +---- +cc -Wl,-Bstatic $(pkg-config --libs --cflags libpkl) +---- +==== == Building libpkl from source @@ -108,6 +120,7 @@ cd pkl/ ---- The resulting archive will be written to `libpkl/build/distributions`. +Additionally, the archive contents will be written to `libpkl/build/native-libs/-`. Cross-compilation is not supported. diff --git a/libpkl/libpkl.gradle.kts b/libpkl/libpkl.gradle.kts index 0a9d6d7b3..2f6dea506 100644 --- a/libpkl/libpkl.gradle.kts +++ b/libpkl/libpkl.gradle.kts @@ -212,7 +212,7 @@ val buildStaticLibrary = outputFile = targetMachine.outputDir.map { dir -> - dir.file("lib/static/libpkl.${targetMachine.os.staticLibraryExtension}") + dir.file("lib/libpkl.${targetMachine.os.staticLibraryExtension}") } } @@ -245,7 +245,7 @@ val buildSharedLibrary = positionIndependentCode = true sharedLibrary = true - outputFile = targetMachine.outputDir.map { it.file("lib/shared/libpkl.${extension}") } + outputFile = targetMachine.outputDir.map { it.file("lib/libpkl.${extension}") } if (buildInfo.os.isMacOS) { frameworks.addAll("Foundation", "CoreServices") @@ -267,7 +267,7 @@ val buildSharedLibrary = // Without an explicit /IMPLIB, MSVC would name the DLL's import library "libpkl.lib" — // the same path buildStaticLibrary writes its static archive to. linkerFlags.add( - targetMachine.sharedLibraryDir.map { dir -> "/IMPLIB:${dir.file("libpkl_dll.lib").asFile}" } + targetMachine.libraryDir.map { dir -> "/IMPLIB:${dir.file("libpkl_dll.lib").asFile}" } ) } else { libraries.add("z") @@ -291,12 +291,6 @@ val Target.outputDir val Target.libraryDir get() = layout.buildDirectory.dir("native-libs/$targetName/lib") -val Target.sharedLibraryDir - get() = layout.buildDirectory.dir("native-libs/$targetName/lib/shared") - -val Target.staticLibraryDir - get() = layout.buildDirectory.dir("native-libs/$targetName/lib/static") - val Target.tempOutputDir get() = layout.buildDirectory.dir("tmp/native-libs/$targetName") @@ -323,7 +317,7 @@ val distZip = into(baseName) } -tasks.assembleNative { dependsOn(distZip, distTar) } +tasks.assembleNative { dependsOn(distZip, distTar, processFiles) } val processFiles = tasks.register("processFiles") { @@ -337,11 +331,15 @@ val processFiles = val tokens = buildMap { this["version"] = buildInfo.pklVersion + // The static archive is referenced by its literal path rather than via `-lpkl`, since + // `-lpkl` would resolve to the shared library instead (linkers prefer a dynamic library + // over a static one when both share the same base name in the same directory). + this["static_lib_filename"] = "libpkl.${buildInfo.os.staticLibraryExtension}" this["extra_static_libs"] = when { - buildInfo.os.isMacOS -> " -lpkl -lz -framework Foundation -framework CoreServices" - buildInfo.os.isWindows -> " -l:libpkl.lib" - else -> " -lpkl -lz" + buildInfo.os.isMacOS -> " -lz -framework Foundation -framework CoreServices" + buildInfo.os.isWindows -> "" + else -> " -lz" } this["extra_shared_libs"] = when { @@ -368,19 +366,14 @@ val testNativeJava = jvmArgumentProviders.add( CommandLineArgumentProvider { listOf( - "-Djna.library.path=" + - buildInfo.targetMachine.sharedLibraryDir.get().asFile.absolutePath, - "-Djava.library.path=" + - buildInfo.targetMachine.sharedLibraryDir.get().asFile.absolutePath, + "-Djna.library.path=" + buildInfo.targetMachine.libraryDir.get().asFile.absolutePath, + "-Djava.library.path=" + buildInfo.targetMachine.libraryDir.get().asFile.absolutePath, "--enable-native-access=ALL-UNNAMED", ) } ) - environment( - "LD_LIBRARY_PATH", - buildInfo.targetMachine.sharedLibraryDir.get().asFile.absolutePath, - ) + environment("LD_LIBRARY_PATH", buildInfo.targetMachine.libraryDir.get().asFile.absolutePath) useJUnitPlatform() } @@ -393,7 +386,7 @@ val compileNativeTestStatic = dependsOn(tasks.assembleNative) - val libDir = buildInfo.targetMachine.staticLibraryDir.get().asFile + val libDir = buildInfo.targetMachine.libraryDir.get().asFile val testSrc = file("src/nativeTest/c/test_pkl.c") link = true @@ -444,18 +437,18 @@ val compileNativeTestDynamic = "native-test/${buildInfo.targetMachine.targetName}/test_pkl_dynamic" ) - libraryPaths.from(buildInfo.targetMachine.sharedLibraryDir) + libraryPaths.from(buildInfo.targetMachine.libraryDir) if (buildInfo.os.isWindows) { // "pkl" would resolve to libpkl.lib, which is buildStaticLibrary's static archive, not // the DLL's import library — link against the import library directly instead. - libraryFiles.from(buildInfo.targetMachine.sharedLibraryDir.map { it.file("libpkl_dll.lib") }) + libraryFiles.from(buildInfo.targetMachine.libraryDir.map { it.file("libpkl_dll.lib") }) } else { libraries.add("pkl") // Bake the library's location into the test executable so the dynamic loader can find // libpkl.so/libpkl.dylib without needing LD_LIBRARY_PATH/DYLD_LIBRARY_PATH set at run time. linkerFlags.add("-rpath") - linkerFlags.add(buildInfo.targetMachine.sharedLibraryDir.get().asFile.absolutePath) + linkerFlags.add(buildInfo.targetMachine.libraryDir.get().asFile.absolutePath) } if (buildInfo.os.isLinux) { @@ -512,7 +505,7 @@ val testNativeCDynamic = doFirst { environment( "PATH", - "${buildInfo.targetMachine.sharedLibraryDir.get().asFile.absolutePath};${System.getenv("PATH")}", + "${buildInfo.targetMachine.libraryDir.get().asFile.absolutePath};${System.getenv("PATH")}", ) } } diff --git a/libpkl/src/main/files/lib/pkgconfig/libpkl-static.pc b/libpkl/src/main/files/lib/pkgconfig/libpkl-static.pc index ff3cb8d5e..9d320eb74 100644 --- a/libpkl/src/main/files/lib/pkgconfig/libpkl-static.pc +++ b/libpkl/src/main/files/lib/pkgconfig/libpkl-static.pc @@ -7,5 +7,5 @@ Name: libpkl Description: C library for calling into Pkl's message passing API. URL: https://pkl-lang.org/main/current/libpkl/index.html Version: @version@ -Libs: -L${libdir}/static@extra_static_libs@ +Libs: ${libdir}/@static_lib_filename@@extra_static_libs@ Cflags: -I${includedir} diff --git a/libpkl/src/main/files/lib/pkgconfig/libpkl.pc b/libpkl/src/main/files/lib/pkgconfig/libpkl.pc index 67ec4b682..d50d308ba 100644 --- a/libpkl/src/main/files/lib/pkgconfig/libpkl.pc +++ b/libpkl/src/main/files/lib/pkgconfig/libpkl.pc @@ -7,5 +7,5 @@ Name: libpkl Description: C library for calling into Pkl's message passing API. URL: https://pkl-lang.org/main/current/libpkl/index.html Version: @version@ -Libs: -L${libdir}/shared@extra_shared_libs@ +Libs: -L${libdir}@extra_shared_libs@ Cflags: -I${includedir} From ab16b683c095a9632587ab698940841ceb4eeadb Mon Sep 17 00:00:00 2001 From: Dan Chao Date: Wed, 5 Aug 2026 22:07:33 -0700 Subject: [PATCH 07/10] rm comment thats over-explanatory --- libpkl/libpkl.gradle.kts | 3 --- 1 file changed, 3 deletions(-) diff --git a/libpkl/libpkl.gradle.kts b/libpkl/libpkl.gradle.kts index 2f6dea506..37289f934 100644 --- a/libpkl/libpkl.gradle.kts +++ b/libpkl/libpkl.gradle.kts @@ -331,9 +331,6 @@ val processFiles = val tokens = buildMap { this["version"] = buildInfo.pklVersion - // The static archive is referenced by its literal path rather than via `-lpkl`, since - // `-lpkl` would resolve to the shared library instead (linkers prefer a dynamic library - // over a static one when both share the same base name in the same directory). this["static_lib_filename"] = "libpkl.${buildInfo.os.staticLibraryExtension}" this["extra_static_libs"] = when { From 9607f7c32e05a2be197bc6d4ac98b7f257434109 Mon Sep 17 00:00:00 2001 From: Dan Chao Date: Thu, 6 Aug 2026 12:58:59 -0700 Subject: [PATCH 08/10] Improve docs, add -install_name to dylib --- docs/modules/libpkl/pages/index.adoc | 6 ++++-- libpkl/libpkl.gradle.kts | 13 +++---------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/docs/modules/libpkl/pages/index.adoc b/docs/modules/libpkl/pages/index.adoc index c5ce56d42..1d359b11c 100644 --- a/docs/modules/libpkl/pages/index.adoc +++ b/docs/modules/libpkl/pages/index.adoc @@ -119,8 +119,10 @@ cd pkl/ ./gradlew libpkl:buildNative ---- -The resulting archive will be written to `libpkl/build/distributions`. -Additionally, the archive contents will be written to `libpkl/build/native-libs/-`. +This results in two directories of interest: + +* `libpkl/build/distributions/`: directory containing archives (`.zip` on Windows, `.tar.gz` on macOS/Linux) of C libraries. +* `libpkl/build/native-libs/-`: directory containing the built library files (the inner structure of the archive). Cross-compilation is not supported. diff --git a/libpkl/libpkl.gradle.kts b/libpkl/libpkl.gradle.kts index 37289f934..8c568e15b 100644 --- a/libpkl/libpkl.gradle.kts +++ b/libpkl/libpkl.gradle.kts @@ -251,16 +251,11 @@ val buildSharedLibrary = frameworks.addAll("Foundation", "CoreServices") linkerFlags.addAll("-current_version", project.version.toString()) linkerFlags.addAll("-compatibility_version", "0.1.0") - - // libpkl_internal_s bundles Native Image's own JNI-named implementations of JDK native - // methods (e.g. Java_sun_nio_ch_UnixFileDispatcherImpl_write0), which by default get - // exported with global visibility. Loading libpkl.dylib into a JVM process (e.g. via JNA) - // then lets the host JVM's own native-method resolution bind to these internal - // implementations instead of the JDK's real ones, silently corrupting unrelated file I/O. - // Restrict the dylib's exported symbols to just the public pkl_* API to prevent this. + // prevent JNI symbols from being exported and clobbering the symbol table val exportedSymbolsFile = file("src/main/c/pkl.exported_symbols") inputs.file(exportedSymbolsFile) linkerFlags.addAll("-exported_symbols_list", exportedSymbolsFile.absolutePath) + compilerArgs.addAll("-install_name", "@rpath/libpkl.dylib") } if (targetMachine.os.isWindows) { @@ -276,9 +271,7 @@ val buildSharedLibrary = if (targetMachine.os.isLinux) { libraries.add("dl") - - // Same symbol-collision hazard as the macOS case above (see comment there), but scoped via - // a GNU ld version script instead of an exported-symbols list. + // prevent JNI symbols from being exported and clobbering the symbol table val versionScriptFile = file("src/main/c/pkl.map") inputs.file(versionScriptFile) linkerFlags.add("--version-script=${versionScriptFile.absolutePath}") From ecc44df1655413224bc45540d8b34d102ff2bb61 Mon Sep 17 00:00:00 2001 From: Daniel Chao Date: Thu, 6 Aug 2026 13:27:05 -0700 Subject: [PATCH 09/10] Apply suggestions from code review Co-authored-by: Islon Scherer --- docs/modules/libpkl/pages/index.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/modules/libpkl/pages/index.adoc b/docs/modules/libpkl/pages/index.adoc index 1d359b11c..bde8e50e0 100644 --- a/docs/modules/libpkl/pages/index.adoc +++ b/docs/modules/libpkl/pages/index.adoc @@ -45,7 +45,7 @@ The archive has the following structure: The file extensions are as follows: |=== -|OS |Static Library Extension |Dynamic library extension +|OS |Static Library Extension |Shared library extension |macOS |`.a` @@ -82,9 +82,9 @@ cc -o myprogram myprogram.c $(pkg-config --static --libs --cflags libpkl-static) ---- -If libpkl is installed in a non-system directory, set `PKG_CONFIG_PATH` to the subpath `lib/pkgconfig` within directory that contains libpkl. For example, given libpkl directory `/path/to/libpkl`, the environment variable should have `/path/to/libpkl/lib/pkgconfig`. +If libpkl is installed in a non-system directory, set `PKG_CONFIG_PATH` to the subpath `lib/pkgconfig` within the directory that contains libpkl. For example, given libpkl directory `/path/to/libpkl`, the environment variable should have `/path/to/libpkl/lib/pkgconfig`. -NOTE: Windows can use pkg-config through the https://www.msys2.org/docs/pkgconfig/[MSYS2 toolchain]. Using pkg-config on Windows implies compiling with gcc-style compiler like https://code.visualstudio.com/docs/cpp/config-mingw[MinGW], instead of MSVC). +NOTE: Windows can use pkg-config through the https://www.msys2.org/docs/pkgconfig/[MSYS2 toolchain]. Using pkg-config on Windows implies compiling with gcc-style compiler like https://code.visualstudio.com/docs/cpp/config-mingw[MinGW], instead of MSVC. [NOTE] ==== From c0b5f794aba64ae221ec89a2870aa538802455eb Mon Sep 17 00:00:00 2001 From: Dan Chao Date: Fri, 7 Aug 2026 08:36:22 -0700 Subject: [PATCH 10/10] trigger gh actions