Dylib Build Configuration and Usage

Platforms, build, C ABI

libPDFlibDylib.dylib is a macOS dynamic library with 880 C entry points implemented by Dylib/PDFlibDylib.dpr; Delphi and FPC builds expose the same names

Use separate x64 and ARM64 builds with a matching macOS process and native providers

Delphi cross-compilation on Windows

Install the RAD Studio macOS compiler and import an Apple macOS SDK through SDK Manager; the compiler needs a local SDK root containing usr/lib, System/Library/Frameworks and SDK version metadata

The SDK Manager connection uses PAServer on the Mac; subsequent command-line compilation uses the local compiler and SDK cache, while runtime tests execute on the actual Mac

Copy Dylib/Build-config.example.cmd to Dylib/Build-config.local.cmd and edit these environment defaults for the machine

@echo off
if not defined PDFLIBPAS_BDS_ROOT set "PDFLIBPAS_BDS_ROOT=C:\Path\To\RADStudio\37.0"
if not defined PDFLIBPAS_MACOS_SDK set "PDFLIBPAS_MACOS_SDK=C:\SDKs\MacOSX.sdk"
if not defined PDFLIBPAS_MACOS_LINKER set "PDFLIBPAS_MACOS_LINKER=C:\LLVM\bin\ld64.lld.exe"
exit /b 0

These are example paths; replace them with the paths and SDK version installed on your machine, and ensure the SDK is compatible with the compiler

Use a Mach-O linker such as the installed RAD Studio ld64.lld.exe when the default linker rejects the SDK's .tbd stubs; the script reads the SDK version and supplies the corresponding -platform_version arguments; Intel builds also compile a small SDK-path adapter with Windows PowerShell to resolve Delphi's absolute POSIX library inputs against the selected cache

Dylib\Build-dylib.bat osxarm64
Dylib\Build-dylib.bat osxarm64 debug
Dylib\Build-dylib-TRIAL.bat osxarm64
Dylib\Build-dylib-TRIAL.bat osxarm64 debug
Dylib\Build-dylib.bat osx64
Dylib\Build-dylib-TRIAL.bat osx64 debug
Argument or variableMeaning
37.0RAD Studio registry version to discover when a root path is not supplied; the default is 37.0
osxarm64 / osx64Select dccosxarm64.exe or dccosx64.exe; the existing default is osx64, so specify ARM64 explicitly for Apple silicon
debugEnable debug information, assertions, range checks and overflow checks, with optimisation disabled; omission selects optimised Release
bdsroot pathOverride PDFLIBPAS_BDS_ROOT and registry discovery; bdsroot=path is also accepted
sdk pathOverride PDFLIBPAS_MACOS_SDK; sdk=path is also accepted
PDFLIBPAS_MACOS_LINKEROptional absolute path to the Mach-O linker; unset it to use the compiler's default linker
PDFLIBPAS_DYLIB_CONFIGSelect another local configuration script; an explicitly selected missing file fails the build

The scripts load the local configuration before parsing arguments, so explicit command-line paths take precedence; the example configuration preserves environment values that are already defined

Local configuration files are executable shell scripts intended for environment assignments and are ignored by Git; a non-zero configuration exit status stops the build

For a one-time build without a local configuration file, pass the paths directly and set the linker in the current shell

set PDFLIBPAS_MACOS_LINKER=C:\LLVM\bin\ld64.lld.exe
Dylib\Build-dylib.bat 37.0 osxarm64 bdsroot "C:\Path\To\RADStudio\37.0" sdk "C:\SDKs\MacOSX.sdk"

The batch scripts rebuild units, select the macOS RTL, ignore automatic compiler CFG files with --no-config and validate the Mach-O CPU and dynamic-library file type before replacing the final output

PDFlibDylib.dproj exposes macOS Debug and Release configurations for IDE use; the batch scripts provide the documented FULL/TRIAL separation and explicit SDK/linker route, while the IDE uses its own registered SDK and connection settings

Native Free Pascal on macOS x64 and ARM64

Use FPC 3.3.1 with the native aarch64-darwin or x86_64-darwin target, matching RTL/packages and Apple's command-line developer tools; ensure companion tools such as fpcres are on PATH or in the compiler configuration, and the script rejects a compiler whose CPU differs from the actual host

Copy Dylib/Build-config.example.sh to Dylib/Build-config.local.sh and edit the paths, or export the variables directly

export FPC=/path/to/aarch64-darwin/fpc
export PDFLIB_FPC_CONFIG=/path/to/aarch64-darwin/fpc.cfg
bash Dylib/Build-native.sh release full
bash Dylib/Build-native.sh debug trial

Omit PDFLIB_FPC_CONFIG when FPC already discovers the correct configuration; when supplied, the runner uses -n @config to select it explicitly

Arguments are positional: [release|debug] [full|trial], with release full as the default; PDFLIBPAS_DYLIB_CONFIG can select another shell configuration file

PDFLIB_DYLIB_OUTPUT overrides the output directory; use a separate directory for each compiler, architecture, configuration and edition to prevent stale or mismatched units

FPC Debug enables line information, range, overflow and I/O checks with optimisation disabled; Release uses -O2, and TRIAL adds -dTRIAL

Do not add -FcUTF8: it changes high-byte AnsiString handling and can corrupt binary PDF identifiers and encryption values

Output directories

All paths below are relative to the repository root and contain libPDFlibDylib.dylib

CompilerFULL ReleaseFULL DebugTRIAL ReleaseTRIAL Debug
Delphi ARM64Dylib/OSXARM64/ReleaseDylib/OSXARM64/DebugDylib/OSXARM64/Release-TRIALDylib/OSXARM64/Debug-TRIAL
FPC ARM64Dylib/OSXARM64/FPC-release-fullDylib/OSXARM64/FPC-debug-fullDylib/OSXARM64/FPC-release-trialDylib/OSXARM64/FPC-debug-trial

Both Delphi and FPC Intel outputs use OSX64 in place of OSXARM64, with the same configuration/edition subdirectories; no universal binary is produced by these scripts

Runtime dependencies and deployment

ProviderDefault ARM64 locationUsed for
OpenSSL 3/opt/homebrew/opt/openssl@3/lib/libcrypto.3.dylibNative encryption, hashing, PFX/CMS signing and verification
Cairo/opt/homebrew/lib/libcairo.2.dylibNative bitmap rendering
Fontconfig/opt/homebrew/lib/libfontconfig.1.dylibNative font discovery
OpenJPEG/opt/homebrew/lib/libopenjp2.7.dylibJPEG 2000
libcurl/usr/lib/libcurl.4.dylibHTTP/HTTPS, range access and TSA transport
libxml2/usr/lib/libxml2.2.dylibDelphi XMP parsing and serialisation

Intel macOS replaces the provider prefix /opt/homebrew with /usr/local; system /usr/lib paths stay the same

Use dependencies and a host process with the same architecture as the dylib; system libraries may be provided by the macOS shared cache rather than standalone files

FPC uses its XML units for XMP; Delphi also uses system zlib and Apple ImageIO/CoreGraphics for native image workflows

Copy the chosen dylib to the Mac and load it by absolute path for an initial integration; linking an application against its @rpath/libPDFlibDylib.dylib install name requires the application's runpath to include the deployment directory

Inspect both the file and its dependencies on the target Mac; otool -L lists linked dependencies, while providers loaded at runtime still require the locations above

file /path/to/libPDFlibDylib.dylib
otool -D /path/to/libPDFlibDylib.dylib
otool -L /path/to/libPDFlibDylib.dylib
nm -gjU /path/to/libPDFlibDylib.dylib

The native FPC script uses MACOSX_DEPLOYMENT_TARGET to keep compilation and linking consistent; adding another -macosx_version_min through -k can select an unintended older target

The linker deployment target is macOS 11.0 for ARM64 and 10.15 for x64; your application's minimum macOS version also depends on the compiler runtime and installed providers, so this flag alone does not establish compatibility with every older macOS version

C ABI types and symbol names

Pascal typeForeign-language representation
IntegerSigned 32-bit integer, including instance handles and most return codes
Int64Signed 64-bit integer
Double64-bit floating-point value
PWideCharPointer to UTF-16 code units, normally terminated by a zero code unit
PAnsiCharByte pointer; encoding, length and binary-buffer rules depend on the specific function

Functions use cdecl; resolve the exported name such as DLCreateLibrary with dlsym or a foreign-function interface, without the leading underscore shown by Mach-O symbol tools

Declare every argument and return type before calling; use pointer-width types for addresses rather than 32-bit integers

macOS wchar_t and Python ctypes.c_wchar_p are UTF-32, so they cannot represent this ABI's PWideChar parameters; Python callers must allocate UTF-16 buffers and treat returned text as a pointer to 16-bit units

The export declarations in Dylib/PDFlibDylib.dpr are authoritative for the C ABI; methods present only on the Pascal class are not automatically available as DL exports

Instances, strings and buffers

  1. Call DLCreateLibrary() and retain the returned positive 32-bit instance handle
  2. Pass that handle to operations and check each operation's documented return convention
  3. Copy returned text before another call on the same instance can overwrite its return storage
  4. Release registered buffers with DLReleaseBuffer(instance, address)
  5. Call DLReleaseLibrary(instance) before unloading the dylib

Returned text belongs to the instance: do not free it with the host allocator or pass it to DLReleaseBuffer; releasing the instance invalidates all borrowed text and outstanding buffers

DLCreateBuffer(instance, length) allocates an instance-owned byte buffer that can contain embedded zero bytes; DLReleaseBuffer returns 1 on success and 0 for an unknown, foreign-instance or already released buffer

DLAddToBuffer copies up to the remaining capacity and returns the remaining capacity, so 0 can mean a successful full write as well as invalid input; it is not a Boolean success result

Instance 0 is the legacy default alias; explicit positive handles make ownership clearer and are not reused during the loaded library's lifetime

Serialise calls on the same instance, including access to borrowed return storage; also serialise instance creation, release and unloading against other C ABI calls because the registry lock does not protect the lifetime of an instance after lookup

Working Python example

Dylib/examples/create_pdf.py binds the C ABI with explicit ctypes signatures, creates a real PDF with a standard font, reloads it, copies extracted UTF-16 text and releases the instance in a finally block

python3 Dylib/examples/create_pdf.py /path/to/libPDFlibDylib.dylib /tmp/hello-dylib.pdf

The example accepts either compiler's dylib and checks that the saved PDF retains its text; select the library architecture that matches the Python process

Verification and troubleshooting

Run the working Python example above with the dylib you plan to deploy; confirm that it creates and reloads the PDF and returns the expected text

Inspect the dylib architecture and dependencies with file and otool -L, and check the operations your application uses against the native capability limits; export presence alone does not establish every operation's platform support

SymptomCheck
Missing macOS compilerInstall the matching macOS compiler and check bdsroot; a Windows compiler cannot produce the dylib
Missing or invalid SDKPoint to the SDK root rather than its usr/lib subdirectory and verify the required framework directory
Linker rejects .tbdSelect the compatible Mach-O LLVM linker and verify the SDK version metadata
Wrong architecture on loadCompare file output for the dylib and the host process; use x64 Python for x64 builds and native ARM64 Python for ARM64 builds
Missing provider or symbolCheck the native dependency architecture, path and version; dynamically loaded providers may not appear in otool -L
Garbled strings or truncated addressesUse UTF-16 code units and declare pointer return types explicitly in the foreign-function interface
Window, printer or Windows device-context operation failsUse supported headless operations and consult the native capability limits

See Native Linux and macOS for XMP, rendering, signing/TSA, dependency details and capability limits, including system certificate-store and revocation boundaries

Reference index