mirror of
https://github.com/apple/pkl.git
synced 2026-08-27 06:04:03 +02:00
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:
co-authored by
Kushal Pisavadia
Jen Basch
Islon Scherer
parent
175e2b6273
commit
67df676359
Executable
+116
@@ -0,0 +1,116 @@
|
||||
#!/bin/bash
|
||||
#===----------------------------------------------------------------------===//
|
||||
# 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.
|
||||
#===----------------------------------------------------------------------===//
|
||||
## build_unix.sh
|
||||
# Adapted from https://github.com/oracle/graal/issues/3053#issuecomment-1866735057
|
||||
# This script intercepts compiler arguments from graalvm native shared images
|
||||
# and additionally generates a static library.
|
||||
#
|
||||
# Use with --native-compiler-path=${pathToThisScript}
|
||||
|
||||
set -e
|
||||
|
||||
# Detect OS
|
||||
OS=$(uname -s)
|
||||
case "$OS" in
|
||||
Darwin)
|
||||
SHARED_LIB_EXT="dylib"
|
||||
;;
|
||||
Linux)
|
||||
SHARED_LIB_EXT="so"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported OS: $OS"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Determine the project name and output path based on the output library argument
|
||||
for arg in "$@"; do
|
||||
if [[ "$arg" == *."$SHARED_LIB_EXT" ]]; then
|
||||
OUTPUT_PATH=$(dirname "$arg")
|
||||
LIB_NAME=$(basename "${arg%."$SHARED_LIB_EXT"}")
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Do a simple forward for any calls that are used to compile individual C files
|
||||
if [[ -z $LIB_NAME ]]; then
|
||||
cc "$@"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Create a debug log in $output/logs
|
||||
LOG_PATH="${OUTPUT_PATH}/logs"
|
||||
LOG_FILE="${LOG_PATH}/compiler_commands.txt"
|
||||
mkdir -p "$LOG_PATH"
|
||||
|
||||
WORKINGDIR=${PWD}
|
||||
echo "Working directory: ${WORKINGDIR}" > "${LOG_FILE}"
|
||||
echo "Output path: ${OUTPUT_PATH}" >> "${LOG_FILE}"
|
||||
echo "Library name: ${LIB_NAME}" >> "${LOG_FILE}"
|
||||
echo "OS: ${OS}" >> "${LOG_FILE}"
|
||||
|
||||
echo "=====================================================" >> "${LOG_FILE}"
|
||||
echo " SHARED LIBRARY " >> "${LOG_FILE}"
|
||||
echo "=====================================================" >> "${LOG_FILE}"
|
||||
|
||||
CC_ARGS=( "$@" )
|
||||
echo "cc ${CC_ARGS[*]@Q}" >> "${LOG_FILE}"
|
||||
cc "${CC_ARGS[@]}"
|
||||
|
||||
echo "=====================================================" >> "${LOG_FILE}"
|
||||
echo " STATIC LIBRARY " >> "${LOG_FILE}"
|
||||
echo "=====================================================" >> "${LOG_FILE}"
|
||||
# To create a single static library we need to call 'ar -r' on all .o files.
|
||||
# In order to also include all static library dependencies, we can first extract the
|
||||
# .o files and then include them as well.
|
||||
echo "======= Source archives" >> "${LOG_FILE}"
|
||||
OBJECTS=${OUTPUT_PATH}/objects
|
||||
rm -rf "${OBJECTS}"
|
||||
mkdir "${OBJECTS}"
|
||||
|
||||
# Remove existing archive to avoid "fat file" errors
|
||||
ARCHIVE_FILE="${OUTPUT_PATH}/${LIB_NAME}.a"
|
||||
rm -f "${ARCHIVE_FILE}"
|
||||
|
||||
AR_ARGS="-rcs ${ARCHIVE_FILE} ${OBJECTS}/*.o"
|
||||
for arg in "$@"
|
||||
do
|
||||
if [[ $arg =~ .*\.(a)$ ]]; then
|
||||
# extract the objects (.o) of each archive (.a) into
|
||||
# separate directories to avoid naming collisions
|
||||
echo "$arg" >> "${LOG_FILE}"
|
||||
ARCHIVE_DIR=${OBJECTS}/$(basename "${arg%.a}")
|
||||
mkdir "${ARCHIVE_DIR}"
|
||||
cp "$arg" "${ARCHIVE_DIR}"
|
||||
cd "${ARCHIVE_DIR}" || exit
|
||||
ar -x "$arg"
|
||||
cd "${WORKINGDIR}" || exit
|
||||
AR_ARGS+=" ${ARCHIVE_DIR}/*.o"
|
||||
fi
|
||||
if [[ $arg =~ .*\.(o)$ ]]; then
|
||||
cp "$arg" "${OBJECTS}"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "======= Objects" >> "${LOG_FILE}"
|
||||
find "${OBJECTS}" -name "*.o" >> "${LOG_FILE}"
|
||||
|
||||
echo "======= Archive command" >> "${LOG_FILE}"
|
||||
echo "ar $AR_ARGS" >> "${LOG_FILE}"
|
||||
# shellcheck disable=SC2086
|
||||
ar $AR_ARGS
|
||||
@@ -0,0 +1,85 @@
|
||||
::===----------------------------------------------------------------------===//
|
||||
:: 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.
|
||||
::===----------------------------------------------------------------------===//
|
||||
@echo off
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
REM Adapted from https://github.com/oracle/graal/issues/3053#issuecomment-1866735057
|
||||
REM This script intercepts compiler arguments generated by GraalVM's native-image for
|
||||
REM creating a shared library, and additionally generates a self-contained static library.
|
||||
REM
|
||||
REM Use with --native-compiler-path=${pathToThisScript}.bat
|
||||
|
||||
REM Determine the library name and output path based on the .dll argument. MSVC's output-naming
|
||||
REM flag is passed as `/Fe<path>` with no space between the flag and the path, so strip that
|
||||
REM prefix before parsing the path - otherwise ~dp/~n misparse the combined token.
|
||||
set LIB_NAME=
|
||||
set OUTPUT_PATH=
|
||||
for %%P in (%*) do (
|
||||
echo %%P | findstr /C:".dll" 1>nul
|
||||
if !errorlevel!==0 (
|
||||
set "ARG=%%~P"
|
||||
if "!ARG:~0,3!"=="/Fe" set "ARG=!ARG:~3!"
|
||||
for %%F in ("!ARG!") do (
|
||||
set LIB_NAME=%%~nF
|
||||
set OUTPUT_PATH=%%~dpF
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
REM Do a simple forward for any calls that are used to compile individual C files
|
||||
IF "%LIB_NAME%"=="" (
|
||||
cmd /c cl %*
|
||||
exit /b
|
||||
)
|
||||
|
||||
REM Setup log path and log file
|
||||
set LOG_PATH=%OUTPUT_PATH%logs
|
||||
set LOG_FILE=%LOG_PATH%\compiler_commands.txt
|
||||
if not exist %LOG_PATH% mkdir %LOG_PATH%
|
||||
|
||||
echo Working directory: %CD% > %LOG_FILE%
|
||||
echo Output path: %OUTPUT_PATH% >> %LOG_FILE%
|
||||
echo Library name: %LIB_NAME% >> %LOG_FILE%
|
||||
|
||||
echo ===================================================== >> %LOG_FILE%
|
||||
echo SHARED LIBRARY >> %LOG_FILE%
|
||||
echo ===================================================== >> %LOG_FILE%
|
||||
set CL_ARGS=%*
|
||||
echo cl.exe %CL_ARGS% >> %LOG_FILE%
|
||||
cmd /c cl.exe %CL_ARGS%
|
||||
|
||||
echo ===================================================== >> %LOG_FILE%
|
||||
echo STATIC LIBRARY >> %LOG_FILE%
|
||||
echo ===================================================== >> %LOG_FILE%
|
||||
REM lib.exe can combine .obj and .lib inputs directly into a single output archive, so unlike
|
||||
REM build_unix.sh we don't need to manually extract members from input archives first - we just
|
||||
REM collect every .obj/.lib argument that was passed to the shared-library link and re-archive
|
||||
REM them together into one self-contained static library with no runtime DLL dependency.
|
||||
REM We don't want to overwrite the import library needed to link against the DLL, so we append
|
||||
REM "_s" to indicate that it is the static variant.
|
||||
set STATIC_INPUTS=
|
||||
for %%P in (%*) do (
|
||||
set ARG=%%P
|
||||
if /I "!ARG:~-4!"==".obj" set STATIC_INPUTS=!STATIC_INPUTS! %%P
|
||||
if /I "!ARG:~-4!"==".lib" set STATIC_INPUTS=!STATIC_INPUTS! %%P
|
||||
)
|
||||
|
||||
echo ======= Static inputs >> %LOG_FILE%
|
||||
echo %STATIC_INPUTS% >> %LOG_FILE%
|
||||
|
||||
set LIB_ARGS=/OUT:%OUTPUT_PATH%%LIB_NAME%_s.lib %STATIC_INPUTS%
|
||||
echo lib.exe %LIB_ARGS% >> %LOG_FILE%
|
||||
cmd /c lib.exe %LIB_ARGS%
|
||||
Reference in New Issue
Block a user