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 variable | Meaning |
|---|---|
37.0 | RAD Studio registry version to discover when a root path is not supplied; the default is 37.0 |
osxarm64 / osx64 | Select dccosxarm64.exe or dccosx64.exe; the existing default is osx64, so specify ARM64 explicitly for Apple silicon |
debug | Enable debug information, assertions, range checks and overflow checks, with optimisation disabled; omission selects optimised Release |
bdsroot path | Override PDFLIBPAS_BDS_ROOT and registry discovery; bdsroot=path is also accepted |
sdk path | Override PDFLIBPAS_MACOS_SDK; sdk=path is also accepted |
PDFLIBPAS_MACOS_LINKER | Optional absolute path to the Mach-O linker; unset it to use the compiler's default linker |
PDFLIBPAS_DYLIB_CONFIG | Select 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
| Compiler | FULL Release | FULL Debug | TRIAL Release | TRIAL Debug |
|---|---|---|---|---|
| Delphi ARM64 | Dylib/OSXARM64/Release | Dylib/OSXARM64/Debug | Dylib/OSXARM64/Release-TRIAL | Dylib/OSXARM64/Debug-TRIAL |
| FPC ARM64 | Dylib/OSXARM64/FPC-release-full | Dylib/OSXARM64/FPC-debug-full | Dylib/OSXARM64/FPC-release-trial | Dylib/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
| Provider | Default ARM64 location | Used for |
|---|---|---|
| OpenSSL 3 | /opt/homebrew/opt/openssl@3/lib/libcrypto.3.dylib | Native encryption, hashing, PFX/CMS signing and verification |
| Cairo | /opt/homebrew/lib/libcairo.2.dylib | Native bitmap rendering |
| Fontconfig | /opt/homebrew/lib/libfontconfig.1.dylib | Native font discovery |
| OpenJPEG | /opt/homebrew/lib/libopenjp2.7.dylib | JPEG 2000 |
| libcurl | /usr/lib/libcurl.4.dylib | HTTP/HTTPS, range access and TSA transport |
| libxml2 | /usr/lib/libxml2.2.dylib | Delphi 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 type | Foreign-language representation |
|---|---|
Integer | Signed 32-bit integer, including instance handles and most return codes |
Int64 | Signed 64-bit integer |
Double | 64-bit floating-point value |
PWideChar | Pointer to UTF-16 code units, normally terminated by a zero code unit |
PAnsiChar | Byte 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
- Call
DLCreateLibrary()and retain the returned positive 32-bit instance handle - Pass that handle to operations and check each operation's documented return convention
- Copy returned text before another call on the same instance can overwrite its return storage
- Release registered buffers with
DLReleaseBuffer(instance, address) - 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
| Symptom | Check |
|---|---|
| Missing macOS compiler | Install the matching macOS compiler and check bdsroot; a Windows compiler cannot produce the dylib |
| Missing or invalid SDK | Point to the SDK root rather than its usr/lib subdirectory and verify the required framework directory |
Linker rejects .tbd | Select the compatible Mach-O LLVM linker and verify the SDK version metadata |
| Wrong architecture on load | Compare 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 symbol | Check the native dependency architecture, path and version; dynamically loaded providers may not appear in otool -L |
| Garbled strings or truncated addresses | Use UTF-16 code units and declare pointer return types explicitly in the foreign-function interface |
| Window, printer or Windows device-context operation fails | Use 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