Конфигурация и использование dylib
Платформы, сборка, C ABI
libPDFlibDylib.dylib — динамическая библиотека macOS с 880 точками входа C, реализованными в Dylib/PDFlibDylib.dpr; сборки Delphi и FPC дают одинаковые имена
Используйте раздельные сборки для x64 и ARM64 с процессом macOS той же архитектуры и нативными провайдерами
Кросс-компиляция Delphi на Windows
Установите компилятор macOS из RAD Studio и импортируйте SDK Apple macOS через SDK Manager; компилятору нужен локальный корень SDK, содержащий usr/lib, System/Library/Frameworks и метаданные версии SDK
Подключение SDK Manager использует PAServer на Mac; последующая компиляция из командной строки задействует локальный компилятор и кэш SDK, а тесты времени выполнения идут уже на реальном Mac
Скопируйте Dylib/Build-config.example.cmd в Dylib/Build-config.local.cmd и подправьте эти значения переменных окружения под свою машину
@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
Это примеры путей; подставьте пути и версию SDK, установленные на вашей машине, и убедитесь, что SDK совместим с компилятором
Если компоновщик по умолчанию не принимает заглушки .tbd из SDK, используйте Mach-O-компоновщик, например установленный в RAD Studio ld64.lld.exe; скрипт читает версию SDK и подставляет соответствующие аргументы -platform_version; сборки под Intel дополнительно компилируют небольшой адаптер путей SDK через Windows PowerShell, чтобы разрешить абсолютные POSIX-пути библиотек Delphi относительно выбранного кэша
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
| Аргумент или переменная | Описание |
|---|---|
37.0 | Версия RAD Studio в реестре, которую искать, если корневой путь не задан; по умолчанию 37.0 |
osxarm64 / osx64 | Выбирает dccosxarm64.exe или dccosx64.exe; текущее значение по умолчанию — osx64, поэтому для Apple Silicon указывайте ARM64 явно |
debug | Включает отладочную информацию, assertions, проверки диапазонов и переполнения, отключая оптимизацию; без аргумента собирается оптимизированный Release |
bdsroot path | Переопределяет PDFLIBPAS_BDS_ROOT и поиск по реестру; также принимается bdsroot=path |
sdk path | Переопределяет PDFLIBPAS_MACOS_SDK; также принимается sdk=path |
PDFLIBPAS_MACOS_LINKER | Опциональный абсолютный путь к Mach-O-компоновщику; сбросьте переменную, чтобы использовать компоновщик компилятора по умолчанию |
PDFLIBPAS_DYLIB_CONFIG | Выбирает другой локальный скрипт конфигурации; явно указанный отсутствующий файл приводит сборку к ошибке |
Скрипты загружают локальную конфигурацию до разбора аргументов, поэтому явно переданные в командной строке пути имеют приоритет; пример конфигурации сохраняет уже определённые значения переменных окружения
Файлы локальной конфигурации — исполняемые shell-скрипты для присваивания переменных окружения; Git их игнорирует, а ненулевой код выхода конфигурации останавливает сборку
Для разовой сборки без файла локальной конфигурации передайте пути напрямую и задайте компоновщик в текущем 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"
Пакетные скрипты пересобирают юниты, выбирают macOS RTL, игнорируют автоматические CFG-файлы компилятора с помощью --no-config и проверяют CPU Mach-O и тип файла динамической библиотеки, прежде чем заменять финальный результат
PDFlibDylib.dproj открывает конфигурации macOS Debug и Release для работы из IDE; пакетные скрипты дают документированное разделение FULL/TRIAL и явный маршрут SDK/компоновщика, а IDE использует собственные зарегистрированные SDK и настройки подключения
Нативный Free Pascal на macOS x64 и ARM64
Используйте FPC 3.3.1 с нативной целью aarch64-darwin или x86_64-darwin, подходящими RTL/пакетами и инструментами разработчика Apple из командной строки; проследите, чтобы вспомогательные инструменты вроде fpcres были в PATH или конфигурации компилятора, — скрипт отклоняет компилятор, чей CPU отличается от фактического хоста
Скопируйте Dylib/Build-config.example.sh в Dylib/Build-config.local.sh и поправьте пути либо экспортируйте переменные напрямую
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
Не задавайте PDFLIB_FPC_CONFIG, если FPC сам находит нужную конфигурацию; если переменная задана, раннер выбирает её явно через -n @config
Аргументы позиционные: [release|debug] [full|trial], по умолчанию release full; PDFLIBPAS_DYLIB_CONFIG может выбрать другой shell-файл конфигурации
PDFLIB_DYLIB_OUTPUT переопределяет выходной каталог; заведите отдельный каталог на каждую комбинацию компилятора, архитектуры, конфигурации и редакции, чтобы не получить устаревшие или несовпадающие юниты
FPC Debug включает информацию о строках, проверки диапазонов, переполнения и I/O с выключенной оптимизацией; Release использует -O2, а TRIAL добавляет -dTRIAL
Не добавляйте -FcUTF8: он меняет обработку старших байтов в AnsiString и может повредить бинарные идентификаторы PDF и значения шифрования
Выходные каталоги
Все пути ниже отсчитываются от корня репозитория и содержат libPDFlibDylib.dylib
| Компилятор | 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 |
Intel-результаты Delphi и FPC используют OSX64 вместо OSXARM64 с теми же подкаталогами конфигураций и редакций; универсальный бинарник эти скрипты не создают
Зависимости времени выполнения и развёртывание
| Провайдер | Расположение по умолчанию для ARM64 | Назначение |
|---|---|---|
| OpenSSL 3 | /opt/homebrew/opt/openssl@3/lib/libcrypto.3.dylib | Нативное шифрование, хеширование, подпись PFX/CMS и проверка |
| Cairo | /opt/homebrew/lib/libcairo.2.dylib | Нативный растровый рендеринг |
| Fontconfig | /opt/homebrew/lib/libfontconfig.1.dylib | Нативный поиск шрифтов |
| OpenJPEG | /opt/homebrew/lib/libopenjp2.7.dylib | JPEG 2000 |
| libcurl | /usr/lib/libcurl.4.dylib | HTTP/HTTPS, доступ к диапазонам и транспорт TSA |
| libxml2 | /usr/lib/libxml2.2.dylib | Разбор и сериализация XMP в Delphi |
На Intel macOS префикс провайдеров /opt/homebrew заменяется на /usr/local; системные пути /usr/lib не меняются
Берите зависимости и хост-процесс той же архитектуры, что и dylib; системные библиотеки могут поставляться общим кэшем macOS, а не отдельными файлами
FPC использует для XMP собственные XML-юниты; Delphi также задействует системный zlib и Apple ImageIO/CoreGraphics для нативных сценариев с изображениями
Для первичной интеграции скопируйте выбранную dylib на Mac и загружайте её по абсолютному пути; чтобы компоновать приложение против её install name @rpath/libPDFlibDylib.dylib, runpath приложения должен включать каталог развёртывания
На целевом Mac проверьте и сам файл, и его зависимости; otool -L перечисляет скомпонованные зависимости, а провайдеры, загружаемые в рантайме, всё равно требуют указанные выше расположения
file /path/to/libPDFlibDylib.dylib
otool -D /path/to/libPDFlibDylib.dylib
otool -L /path/to/libPDFlibDylib.dylib
nm -gjU /path/to/libPDFlibDylib.dylib
Нативный FPC-скрипт использует MACOSX_DEPLOYMENT_TARGET, чтобы компиляция и компоновка оставались согласованными; дополнительный -macosx_version_min через -k может нечаянно выбрать более старую цель
Цель развёртывания компоновщика — macOS 11.0 для ARM64 и 10.15 для x64; минимальная версия macOS вашего приложения зависит ещё и от рантайма компилятора и установленных провайдеров, поэтому один этот флаг совместимость со всеми старыми версиями macOS не гарантирует
Типы C ABI и имена символов
| Тип Pascal | Представление в других языках |
|---|---|
Integer | Знаковое 32-битное целое, включая дескрипторы экземпляров и большинство кодов возврата |
Int64 | Знаковое 64-битное целое |
Double | 64-битное значение с плавающей запятой |
PWideChar | Указатель на кодовые единицы UTF-16, обычно завершённый нулевой единицей |
PAnsiChar | Указатель на байты; кодировка, длина и правила бинарных буферов зависят от конкретной функции |
Функции используют cdecl; экспортируемое имя вроде DLCreateLibrary ищите через dlsym или внешний функциональный интерфейс — без ведущего подчёркивания, которое показывают инструменты символов Mach-O
Объявите все аргументы и типы возврата до вызова; для адресов используйте типы шириной с указатель, а не 32-битные целые
wchar_t на macOS и ctypes.c_wchar_p в Python — это UTF-32, поэтому они не могут представить параметры PWideChar этого ABI; вызывающие из Python должны выделять буферы UTF-16 и считать возвращённый текст указателем на 16-битные единицы
Авторитетный источник по C ABI — объявления экспорта в Dylib/PDFlibDylib.dpr; методы, существующие только в Pascal-классе, автоматически как экспорты DL не появляются
Экземпляры, строки и буферы
- Вызовите
DLCreateLibrary()и сохраните возвращённый положительный 32-битный дескриптор экземпляра - Передавайте этот дескриптор в операции и сверяйтесь с документированной конвенцией возврата каждой из них
- Копируйте возвращённый текст до того, как следующий вызов на том же экземпляре перезапишет область возврата
- Освобождайте зарегистрированные буферы через
DLReleaseBuffer(instance, address) - Вызовите
DLReleaseLibrary(instance)до выгрузки dylib
Возвращённый текст принадлежит экземпляру: не освобождайте его аллокатором хоста и не передавайте в DLReleaseBuffer; освобождение экземпляра делает недействительными весь одолженный текст и все незакрытые буферы
DLCreateBuffer(instance, length) выделяет принадлежащий экземпляру байтовый буфер, в котором могут быть встроенные нулевые байты; DLReleaseBuffer возвращает 1 при успехе и 0 для неизвестного, чужого или уже освобождённого буфера
DLAddToBuffer копирует до оставшейся ёмкости и возвращает оставшуюся ёмкость, поэтому 0 может означать как успешную полную запись, так и неверный ввод; это не булев признак успеха
Экземпляр 0 — унаследованный псевдоним по умолчанию; явные положительные дескрипторы делают владение прозрачнее и не переиспользуются, пока библиотека загружена
Сериализуйте вызовы одного экземпляра, включая доступ к одолженной области возврата; также сериализуйте создание, освобождение и выгрузку экземпляра относительно других вызовов C ABI — блокировка реестра не защищает время жизни экземпляра после поиска
Рабочий пример на Python
Dylib/examples/create_pdf.py привязывает C ABI с явными сигнатурами ctypes, создаёт настоящий PDF со стандартным шрифтом, загружает его заново, копирует извлечённый текст UTF-16 и освобождает экземпляр в блоке finally
python3 Dylib/examples/create_pdf.py /path/to/libPDFlibDylib.dylib /tmp/hello-dylib.pdf
Пример принимает dylib от любого из компиляторов и проверяет, что сохранённый PDF сохраняет текст; выбирайте архитектуру библиотеки под процесс Python
Проверка и устранение проблем
Запустите приведённый выше рабочий пример на Python с той dylib, которую собираетесь развёртывать; убедитесь, что она создаёт и перечитывает PDF и возвращает ожидаемый текст
Проверьте архитектуру и зависимости dylib через file и otool -L, а операции, которые использует приложение, сверьте с нативными ограничениями возможностей; одного наличия экспорта недостаточно, чтобы считать каждую операцию поддерживаемой на платформе
| Симптом | Что проверить |
|---|---|
| Отсутствует компилятор macOS | Установите подходящий компилятор macOS и проверьте bdsroot; компилятор под Windows dylib не соберёт |
| SDK отсутствует или неверен | Укажите на корень SDK, а не на его подкаталог usr/lib, и проверьте требуемый каталог фреймворков |
Компоновщик отклоняет .tbd | Выберите совместимый Mach-O-компоновщик LLVM и проверьте метаданные версии SDK |
| Неверная архитектура при загрузке | Сравните вывод file для dylib и хост-процесса; для сборок x64 берите Python x64, для ARM64 — нативный Python ARM64 |
| Отсутствует провайдер или символ | Проверьте архитектуру, путь и версию нативной зависимости; динамически загружаемые провайдеры могут не показаться в otool -L |
| Испорченные строки или усечённые адреса | Используйте кодовые единицы UTF-16 и явно объявляйте типы возврата-указателей во внешнем функциональном интерфейсе |
| Сбой операции окна, принтера или контекста устройства Windows | Используйте поддерживаемые операции без GUI и сверяйтесь с нативными ограничениями возможностей |
Подробности об XMP, рендеринге, подписях/TSA, зависимостях и ограничениях возможностей — включая границы системного хранилища сертификатов и проверки отзыва — см. на странице Native Linux and macOS