diff --git a/docs/modules/libpkl/pages/index.adoc b/docs/modules/libpkl/pages/index.adoc index 20b7bacb1..bde8e50e0 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,9 +33,10 @@ The archive has the following structure: ├── include │ └── pkl.h ├── lib + │ ├── libpkl. │ ├── libpkl. - │ ├── libpkl. │ └── pkgconfig + │ ├── libpkl-static.pc │ └── libpkl.pc ├── LICENSE.txt ├── README.md @@ -43,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` @@ -58,6 +60,44 @@ The file extensions are as follows: |`.dll` |=== +=== Linking to the static vs. shared library + +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`. + +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`: + +[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) +---- + + +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] +==== +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 To build libpkl from source, the apple/pkl repository first must be cloned down to a host OS. @@ -79,7 +119,10 @@ cd pkl/ ./gradlew libpkl:buildNative ---- -The resulting archive will be written to `libpkl/build/distributions`. +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 280d94de4..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}") @@ -317,7 +310,7 @@ val distZip = into(baseName) } -tasks.assembleNative { dependsOn(distZip, distTar) } +tasks.assembleNative { dependsOn(distZip, distTar, processFiles) } val processFiles = tasks.register("processFiles") { @@ -329,9 +322,23 @@ 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["static_lib_filename"] = "libpkl.${buildInfo.os.staticLibraryExtension}" + this["extra_static_libs"] = + when { + buildInfo.os.isMacOS -> " -lz -framework Foundation -framework CoreServices" + buildInfo.os.isWindows -> "" + else -> " -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) } } val testNativeJava = @@ -428,8 +435,6 @@ val compileNativeTestDynamic = libraryFiles.from(buildInfo.targetMachine.libraryDir.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") 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..9d320eb74 --- /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: ${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 19470ca2c..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} -lpkl -lz -lpthread +Libs: -L${libdir}@extra_shared_libs@ Cflags: -I${includedir}