Календарь на Май 2024 года: calendar2008.ru/2024/may/
Навигация
Главная »  Delphi 

Из Delphi в FreePascal под Mac OSX


Источник: delphifrantic
Delphi Zen
Но сначала немного о самом приложении: оно предназначено для безопасной и быстрой передачи файлов через интернет. Сам того не осознавая, в начале работы над ним я принял несколько решений, которые в итоге сильно упростили процесс переноса на другую платформу:

Четко разделил приложения на части, каждая из которых имела свой максимально простой интерфейс (IInterface) и одну реализацию (пример далее).
Ограничил использование сторонних компонентов, а те, которые использовались, выделил в отдельные интерфейсы по принципу №1.

Использовал DUnit для тестирования реализации основных компонентов.

Поигравшись с Lazarus на Mac, я понял что графическую часть приложения я буду писать на родном для OSX ObjectiveC/Cocoa, а всю логику подключу через Dylib (аналог DLL). А все из-за того, что формочки в Lazarus хоть и есть, но выглядят они очень убого (там старый Carbon widgetset, который навсегда останется 32-битным) + хочется теснее интегрироваться в систему, а абстрагированный от платформы код FreePascal этого не позволит в полной мере (нет, позволит, конечно, но с огромным количеством костылей)


Лирическое отступление №1.

Мыслить в терминах интерфейсов - очень удобно. Вот, к примеру, у меня был интерфейс, инкапсулирующий работу с HTTP:

type
  IHttpClient = interface
    ['{CBE784BC-8732-4CE0-868F-00AE659F11AA}']
    procedure Post(URL: string; PostData, ResultData: TStrings);
  end;
var
  HttpClient: IHttpClient = nil;


Здесь 2 важных пункта: во-первых, интерфейс максимально простой; во-вторых, регистрация реализации интерфейса делается вот так:

HttpClient := TIndyHttpClient.Create;

Очень просто. Никаких сверх-сложных репозиториев реализаций, сервисов автоматического поиска зависимостей или XML-файлов. Все присвоения интерфейс := реализация в одном отдельном файле. Так, в юнит-тестах вместо реального TIndyHttpClient можно использовать TFakeHttpClient. Забегая наперед: реализацию интерфейса, которая не сомгла быть портирована на FreePascal, очень легко заменить на другую (например вместо TIndyHttpClient в Mac-версии используется TSynapseHttpClient).

Lazarus & FreePascal on Mac

Перед установкой надо иметь в системе XCode, на момент написания сего текста у меня 4.2. Тем, кто собирается использовать старый XCode 3 - обязательно прочтите это. Установка очень проста - согласно инструкции качаем с sourceforge 3 DMG-образа и устанавливаем их в таком порядке: fpc, fpcsrc, lazarus. После этого Lazarus.app можно найти по адресу /Developer/lazarus/. Я понимаю, что создание такого IDE, как Lazarus, требует много сил, но пока что тяжело назвать его удобным.


Dylib

Dylib - это аналог Windows DLL. Создать такой в Lazarus очень просто: File - New - Library. В своем коде я выделил API для доступа к основному функционалу приложения. Эти функции следует объявлять с директивой cdecl и потом указвать их в секции exports *.lpr-файла:

function magic_sum(a, b: Integer): Integer; cdecl;
begin
  Result := a + b + 42;
end;


exports
  magic_sum;


Такой способ организации мне понравился - он заставляет окончательно отделить центральную логику работы приложения от представления и оформить API для доступа к ней.

FreePascal иммеет свои отличия от Delphi, но в целом где-то 90% кода осталось без изменений. Indy был заменен на Synapse, LockBox пришлось немного подправить, SuperObject чудесно скомпилировался. Generics.Collections и новомодные reference to procedure пришлось переписать, FPC их не поддерживает (точнее поддерживает, но синтаксис отличается).

Из важных особенностей могу отметить три, которые в свое время отобрали у меня кучу времени:

  • Eсли в dylib используется работа с потоками - cthreads должен стоять первым в uses
  • В старом XCode 3.2 был баг в линкере, который приводил к тому, что секции initialization в юнитах не исполнялись. Здесь (и здесь) написано как это исправить.
    Код Cocoa напичкан кучей floating point exception, которые не вываливаются потому что по-умолчанию все FP исключения игнорируются. А вот runtime FreePascal включает их, итого программа становится нестабильной. Лечится это вызовом в самом начале библиотеки (модуль math):
  • SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide,
  • exOverflow, exUnderflow, exPrecision]);


UI часть

Как упоминалось выше, от UI части на Lazarus я отказался, реализовав ее как Cocoa Application в XCode. Изучение Objective-C и Interface Builder не слишком простое для человека несколько лет работавшего с Delphi, ибо подходы совершенно разные. Единственное, что хочу отметить - все эти "плюсики" и "скобочки" в Objective-C кажутся бредовыми только на первых порах, надо копнуть глубже что бы полюбить этот язык и технологию.

Для доступа к функциям с dylib есть 2 способа: 1) через dlopen и ее друзья - функции нижнего уровня, 2) отдать эту работу linker-у. Я выбрал второй: drag&drop"ом добавил dylib в проект, а в отдельном header-e описал импортированные функции:

int magic_sum(int a, int b);

Обмен строками - через буферы const char*/PChar.

Если используете callback-функции (а никто не запрещает передать в dylib указатель на функцию и вызвать ее) и потоки - не забывайте об NSAutoreleasePool-е на стороне Objective-C/Cocoa


Отладка

Не все всегда работает как надо, поетому несколько советов по отладке:

  • Для одних и тех же исходников создайте 2 Lazarus проекта - один как библиотеку, второй как консольное приложение. Из второго запускайте юнит-тесты.
    В Lazarus есть возможность запускать dylib через hosted application: Run - Run Parameters…
  • Включите опцию "Generate GDB information", тогда при генерации исключения в библиотеке XCode сможет показать "красивый" стек и даже строку исходника, где это произошло.
  • Write/Writeln выводит строку на стандартный вывод, который можно посмотреть в XCode в окне All Output


Упаковка

Есть еще одна хитрая особенность подключения dylib: если делать это описанным мною способом, XCode "вшивает" в выходной бинарник абсолютный путь к dylib. Заменить его на относительный можно так (YourApplication - XCode приложение, libtest - Lazarus библиотека):

Копируем dylib в YouApplication.app/Contents/MacOS/ (программы .app в MacOSX - это папки)

Заходим через консоль в YouApplication.app/Contents/MacOS/ и выполняем:
install_name_tool -change /path/to/your/dylib/libtest.dylib @loader_path/libtest.dylib YourApplication

Проверяем (в выводе команды не должно быть абсолютного пути к libtest.dylib):
otool -L  YourApplication

 64-bit

Пока что и библиотека и приложение 32-битные. В ближайшем будущем планирую собрать все под 64битную систему, FPC уже давно поддерживает эту архитектуру.
 Лирическое отступление №2

Почему не Delphi XE for Mac?

Где-то около полугода назад я присутствовал на представлении XE2 в Киеве, в частности они показывали интеграцию с MacOS. Очень радует, что Embarcadero развивает Delphi и ведет его на новые рынки. Для свого проекта я отказался от XE2 в пользу FreePascal (core) + Cocoa (UI) по нескольким причинам:

  • Сырость самого XE2 (наверное основная причина)
  • Native UI
  • Доступ к Mac-овским платформенным плюшкам напрямую
  • Желание познавать новое. Только с переходом на другой ЯП, фреймворк и платформу осознал насколько был до этого "заточен" под Delphi
Хотя, наверное, новый Firemonkey может быть (и будет!) полезен при разработке других видов приложений.



 

 Средства разработки приложений.
 .Net глазами дельфийца. Использование Delphi в .Net (исходники).
 Разработка DLL для CTD с использованием Delphi.
 Использование инструментов криптографии в Delphi-приложениях (исходники).
 Windows Sockets, IOCP и Delphi.


Главная »  Delphi 

© 2024 Team.Furia.Ru.
Частичное копирование материалов разрешено.