Конфигурация и использование 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 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

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.dylibJPEG 2000
libcurl/usr/lib/libcurl.4.dylibHTTP/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-битное целое
Double64-битное значение с плавающей запятой
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 не появляются

Экземпляры, строки и буферы

  1. Вызовите DLCreateLibrary() и сохраните возвращённый положительный 32-битный дескриптор экземпляра
  2. Передавайте этот дескриптор в операции и сверяйтесь с документированной конвенцией возврата каждой из них
  3. Копируйте возвращённый текст до того, как следующий вызов на том же экземпляре перезапишет область возврата
  4. Освобождайте зарегистрированные буферы через DLReleaseBuffer(instance, address)
  5. Вызовите 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

Указатель документации