В удивительном мире ИТ существуют проекты, узнав о которых можно сильно поменять свои взгляды на жизнь, реальность и саму разработку. Об одном из таких проектов и будет наш рассказ.
О чем речь
Нет, речь пойдет не про волшебные мухомор.. алкогольный делирий, а всего лишь про очередную сложную штуку для программистов — графический фреймворк.
Но особенный фреймворк, появление которого было шуткой, реальное использование — дичью, а современное применение является уже чистым фанатизмом.
Даже краткая аннотация из википедии легко вгоняет в диссонанс современных MacOS‑разработчиков:
GNUstep — свободная реализация Cocoa (ранее OpenStep) — объектно-ориентированного API (Objective-C) для объектно-ориентированных операционных систем.
Objective-C (основной язык разработки под MacOS и iOS)и свободная реализация Cocoa (главный UI/UX фреймворк Apple)— расскажите об этом типичному разработчику под Mac, потратившему несколько тысяч долларов на покупку железа Apple и платную подписку на XCode, увидите как у человека начинается нервный тик.
Еще можно троллинга шутки ради сдать тестовое задание на позицию «MacOS Developer»:
#import <AppKit/AppKit.h>
int main(int argc, const char *argv[])
{
return NSApplicationMain (argc, argv);
}
Код выше ничем не отличается от стандартного main()
на обычном маковском Objective-C, при этом никакого отношения к Apple и MacOS не имеет.
Даже если немного усложнить и углубиться в реализацию:
@implementation CalcBrain: NSObject
-(id) init
{
[super init];
result = 0;
enteredNumber = 0;
operation = none;
fractionalDigits = 0;
decimalSeparator = NO;
editing = YES;
face = nil;
return self;
}
Отличия все равно будут минимальны.
Настолько минимальны, что неподготовленный разработчик, не знающий о существовании проекта GNUStep не сможет отличить (с первой попытки).
Вот так выглядит «showcase» GNUStep — тестовое приложение с демонстрацией функционала:
История
Разумеется история появления такого чуда не менее прекрасна и удивительна:
Проект был начат Паулем Кунцем (Paul Kunz) с командой из Стенфордского Центра линейного ускорителя (Stanford Linear Accelerator Center) которым был нужен порт HippoDraw из NeXTSTEP на другую платформу. Вместо того, чтобы переписывать программу с нуля, используя ее архитектуру, разработчики решили переписать слой NeXTSTEP, от которого зависело приложение. Это была первая версия libobjcX.
Именно так выглядит настоящий хардкор в разработке:
взять и переписать часть операционной системы ради портирования одного приложения.
«Как тебе такое, Илон Маск?» (ц)
Справедливости ради стоит заметить, что в те былинные времена графические библиотеки тоже были сильно попроще, а само действо происходило в «закрытом НИИ» и на государственные гранты. Что конечно несколько отличается от современных потогонных реалий и перегретого рынка разработки ПО.
Поэтому не удивляйтесь, когда на предложение «переписать часть операционной системы в рамках проекта» вместо одобрения или хотя‑бы обсуждения, вам сразу же вызовут санитаров из дурки.
Разрыв шаблона номер один
На дворе далекий 1994й год, времена модемов, досок BBS, ФИДО и первых персональных компьютеров.
А кое-где уже cуществовал самый настоящий Graphical Interface Builder:
Вот в качестве иллюстрации снимок с ЭЛТ монитора тех лет:
Это не шутка, самый настоящий конструктор интерфейсов был реализован еще в далеком 1988 году:
Gorm (Graphical Object Relationship Modeller) is a graphical user interface builder application. It is part of the developer tools of GNUstep. Gorm is the equivalent of Interface Builder that was originally found on NeXTSTEP, then OPENSTEP, and finally on Mac OS X. It supports the old .nib files as well as its own .gorm file format.
Был создан и работал задолго до рождения большинства читающих сейчас эту статью. Вот вам для иллюстрации еще один снимок тех лет, с местной «косынкой»:
А теперь внимание на даты:
A preview release of NeXTSTEP (version 0.8) was shown with the launch of the NeXT Computer on October 12, 1988
Мне тогда было 5 лет а на дворе был Советский Союз. Для сравнения, вот так примерно в те времена выглядел весь софт в СССР а затем и в РФ:
Так что шаблон «раньше был страшный черный MS DOS и текстовые интерфейсы» только что треснул.
Еще один замечательный и интересный пример:
Видите на снимке выше открытый текстовый документ, с разными шрифтами, кернингом и лигатурой?
Как бы это не было удивительно, но данный функционал был реализован невероятно, даже шокирующе давно — вот так выглядел в 1988 году его далекий предок:
Расширение файла .wn
не что иное как формат текстового процессора WriteNow:
WriteNow is a word processor application for the original Apple Macintosh and later computers in the NeXT product line. The application is one of two word processors that were first developed with the goal that they be available at the time of the Mac product launch in 1984, and was the primary word processor for computers manufactured by NeXT.[2
Так что корни современных текстовых процессоров уходят невероятно далеко в прошлое, дальше чем вы думали.
Разрыв шаблона номер два
Компания NeXT давно закрыта, никакие ее продукты уже давно не продаются, даже само API OpenStep — фактически заброшено, а «свободная реализация» в виде GNUstep на сегодняшний день выглядит как оживший труп из далекого прошлого.
Но нашлись таки
некрофилыинтересные личности, которые в погоне за прибылью откопали и оживили это чудо для использования в реальном продукте.
Ни за что не догадаетесь кто это все затеял и зачем, что лишний раз показывает как мало мы знаем о мире информационных технологий:
Windows Bridge for iOS (codenamed "Islandwood") is an open-source middleware toolkit that allows iOS apps developed in Objective-C to be ported to Windows 10 by using Visual Studio 2015 to convert the Xcode project into a Visual Studio project.[7][9][10] An early build of Windows Bridge for iOS was released as open-source software under the MIT License on August 6, 2015, while the Android version was in closed beta.[7]
This "WinObjC" project is open source on GitHub. It contains code from various existing implementations of Cocoa Touch like Cocotron and GNUstep as well as Microsoft's own code that implements iOS frameworks using UWP methods. It uses a version of the LLVM clang compiler.[11]
Вот такие дела: Microsoft использовал исходный код GNUStep для создания конвертера проектов XCode под Windows. Причем проект очень даже живой.
Но выглядит это.. весьма своеобразно:
Сборка и запуск
Праздник «старой школы» был бы неполным без рассказа о том как собрать и запустить GNUstep своими силами.
Для написания статьи использовался Mageia Linux 9, но инструкции актуальны и для всех других дистрибьютивов и ОС.
Поскольку проект GNUstep находится в полузаброшенном состоянии, не стоит пытаться ставить его из пакетов — в большинстве дистрибутивов эти пакеты не имеют ментейнера и присутствуют «для галочки», просто потому что собираются.
Вместо этого, мы соберем GNUstep непосредственно из исходников.
tools-make
Начать придется со своей специфичной и уникальной системы сборки:
The makefile package is a simple, powerful and extensible way to write makefiles for a GNUstep-based project. It allows the user to write a project without having to deal with the complex issues associated with configuration, building, installation, and packaging. It also allows the user to easily create cross-compiled binaries.
Да, я тоже был удивлен, но наверное не так сильно, поскольку многие старые проекты (например оригинальный CDE) имеют собственные системы сборки.
Забираем это чудо:
git clone https://github.com/gnustep/tools-make.git
Собираем:
cd tools-make
./configure --prefix=/opt/gnustep
make
make install
Как видите тут используется ключ --prefix
- указание на установку в нестандартное место, которое нужно чтобы собираемый проект не попал при установке в системные каталоги.
Еще это означает, что придется добавлять путь /opt/gnustep
в переменные окружения, либо в LD_LIBRARY_PATH либо в PATH:
export PATH=/opt/gnustep/bin:$PATH
libs-base
Следующим шагом собираем commons — общую библиотеку классов:
The GNUstep Base Library is a library of general-purpose, non-graphical Objective C objects. For example, it includes classes for strings, object collections, byte streams, typed coders, invocations, notifications, notification dispatchers, moments in time, network ports, remote object messaging support (distributed objects), and event loops.
Вот что будет нужно иметь в системе из библиотек для сборки:
* ffi (HIGHLY RECOMMENDED)
* icu (HIGHLY RECOMMENDED)
* gnutls (HIGHLY RECOMMENDED)
* libxml2 (RECOMMENDED)
* libcurl (RECOMMENDED)
* libdispatch (RECOMMENDED)
* libavahi (RECOMMENDED for NSNetServices)
* libxslt (RECOMMENDED)
* zlib (RECOMMENDED)
* iconv (OPTIONAL, not needed if you have glibc)
* openssl (OPTIONAL, not needed if you have gnutls)
Стоит помнить, что речь идет об использовании этих библиотек для сборки, поэтому необходимо устанавливать версии пакетов «для разработчиков» — с заголовочными.h файлами.
Забираем исходный код проекта:
git clone https://github.com/gnustep/libs-base.git
Запускаем сборку:
./configure --prefix=/opt/gnustep
make
make install
libs-gui
Следующий шаг — сборка еще одной общей библиотеки, в этот раз графической, отвечающей за интерфейс:
The GNUstep gui library is a library of graphical user interface classes written completely in the Objective-C language; the classes are based upon Apple's Cocoa framwork (which came from the OpenStep specification). These classes include graphical objects such as buttons, text fields, popup lists, browser lists, and windows; there are also many associated classes for handling events, colors, fonts, pasteboards and images.
Тут будет нужно поставить еще несколько зависимых библиотек в систему:
* tiff (REQUIRED)
* jpeg (RECOMMENDED)
* png (RECOMMENDED)
* gif or ungif (OPTIONAL)
* aspell (OPTIONAL)
* cups (OPTIONAL)
* audiofile (OPTIONAL)
* portaudio, v19 which has several API changes previous version
(OPTIONAL)
Каких-либо проблем с этими библиотеками не было, поскольку они все старые и очень стабильные.
Забираем исходники:
git clone https://github.com/gnustep/libs-gui.git
Собираем:
./configure --prefix=/opt/gnustep
make
make install
libs-back
Еще одна библиотека, в этот раз — с реализацией «бекэнда» графического рендера.
Забираем исходный код:
git clone https://github.com/gnustep/libs-back.git
Собираем:
./configure --prefix=/opt/gnustep
make
make install
Перед запуском конечных приложений на GNUstep необходимо выполнить:
defaults write NSGlobalDomain GSBackend libgnustep-xlib
Эта команда установит бекэнд по-умолчанию, сама настройка будет сохранена в каталоге ~/GNUstep
На этом сборка самого фреймворка GNUstep завершена и можно наконец запускать примеры.
Примеры
Выложены в официальный репозиторий Github, вместе с самим проектом GNUstep, все примеры отлично собираются и запускаются.
Забираем исходники:
git clone https://github.com/gnustep/tests-examples.git
Каждый пример является отдельным проектом, собираемым через специфичный make GNUstep, поэтому для сборки необходимо иметь путь /opt/gnustep
в переменной PATH.
Вот так выглядит в работе пример графического редактора:
Также стоит рассказать о еще двух интересных проектах из мира древних и усопших.
Gorm
Тот самый «interface builder», которым я столько восхищался в начале статьи:
Gorm (Graphical Object Relationship Modeller) is a graphical user interface builder application. It is part of the developer tools of GNUstep. Gorm is the equivalent of Interface Builder that was originally found on NeXTSTEP, then OPENSTEP, and finally on Mac OS X. It supports the old .nib files as well as its own .gorm file format.
В запущенном виде он выглядит как-то так:
И сейчас мы будем его собирать, забираем исходный код:
git clone https://github.com/gnustep/apps-gorm.git
Напоминаю про специфичный make
для GNUstep и необходимость наличия /opt/gnustep
в переменной PATH, сама сборка выполняется вот так:
make
К сожалению autotools для этого проекта нет, поэтому попытка использования make install
приведет к установке в системный каталог /usr/lib
, что разумеется не очень надо.
Поэтому для теста было решено запускать бинарник gorm
сразу из места сборки:
export LD_LIBRARY_PATH=/opt/gnustep/lib:./InterfaceBuilder/obj:./GormObjCHeaderParser/obj:./GormCore/GormCore.framework/Versions/0
./Applications/Gorm/Gorm.app/Gorm
За что я конечно буду гореть в аду для программистов. Но потом.
Projectcenter: IDE для GNUstep
Да, у них в могиле есть даже свой собственный IDE:
IDE — Integrated Development Environment. PC manages code and builds applications, frameworks and libraries, use GORM for Interface editing.
И его тоже я смог собрать и запустить, выглядит в работе он как-то так:
Забираем:
git clone https://github.com/gnustep/apps-projectcenter.git
Сборка аналогична описанной выше сборке проекта Gorm, по тем же причинам я снова произвел «закат солнца вручную» для запуска:
export LD_LIBRARY_PATH=/opt/gnustep/lib:./Framework/ProjectCenter.framework/Versions/0.7.0
./ProjectCenter.app/ProjectCenter
Код
Думаю читателям будет интересно увидеть, как выглядит реальный код на Objective-C для работы с GNUstep.В качестве примера был взят тестовый проект калькулятора, который как раз запущен на первом скриншоте данной статьи.
Ниже немного порезанный код этого калькулятора:
#include <Foundation/Foundation.h>
#include <AppKit/AppKit.h>
#include "CalcFace.h"
#include "CalcBrain.h"
@implementation CalcFace: NSWindow
-(id)init
{
int i;
// Display
display = [[NSTextField alloc] initWithFrame: NSMakeRect (40, 84, 182, 24)];
[display setEditable: NO];
// [display setScrollable: YES];
[display setBezeled: YES];
[display setDrawsBackground: YES];
[display setAlignment: NSRightTextAlignment];
// Numbers
buttons[0] = [[NSButton alloc] initWithFrame: NSMakeRect (77, 3, 34, 24)];
[buttons[0] setButtonType: NSToggleButton];
[buttons[0] setTitle: @"0"];
[buttons[0] setTag: 0];
[buttons[0] setState: NO];
[buttons[0] setAction: @selector(digit:)];
[buttons[0] setKeyEquivalent: @"0"];
buttons[1] = [[NSButton alloc] initWithFrame: NSMakeRect (114, 3, 34, 24)];
[buttons[1] setButtonType: NSToggleButton];
[buttons[1] setTitle: @"1"];
[buttons[1] setTag: 1];
[buttons[1] setState: NO];
[buttons[1] setAction: @selector(digit:)];
[buttons[1] setKeyEquivalent: @"1"];
..
buttons[12] = [[NSButton alloc] initWithFrame: NSMakeRect (3, 30, 34, 24)];
[buttons[12] setButtonType: NSToggleButton];
[buttons[12] setTitle: @"+"];
[buttons[12] setTag: addition];
[buttons[12] setState: NO];
[buttons[12] setAction: @selector(operation:)];
[buttons[12] setKeyEquivalent: @"+"];
...
buttons[17] = [[NSButton alloc] initWithFrame: NSMakeRect (3, 3, 71, 24)];
[buttons[17] setButtonType: NSToggleButton];
[buttons[17] setTitle: @"="];
[buttons[17] setState: NO];
[buttons[17] setAction: @selector(equal:)];
[buttons[17] setKeyEquivalent: @"="];
// Window
[self initWithContentRect: NSMakeRect (100, 100, 225, 111)
styleMask: (NSTitledWindowMask | NSMiniaturizableWindowMask)
backing: NSBackingStoreBuffered
defer: NO];
[self setInitialFirstResponder: buttons[17]];
[self setDefaultButtonCell: [buttons[17] cell]];
for (i = 0; i < 18; i++)
{
[[self contentView] addSubview: buttons[i]];
[buttons[i] release];
}
[[self contentView] addSubview: display];
[display release];
[self setTitle: @"Calculator.app"];
[self center];
return self;
}
-(void) dealloc
{
[super dealloc];
}
-(void) setBrain: (CalcBrain *)aBrain
{
int i;
for (i = 0; i < 18; i++)
[buttons[i] setTarget: aBrain];
}
-(void) setDisplayedNumber: (double)aNumber
withSeparator: (BOOL)displayDecimalSeparator
fractionalDigits: (int)fractionalDigits
{
if (displayDecimalSeparator)
{
[display setStringValue:
[NSString stringWithFormat:
[NSString stringWithFormat:
@"%%#.%df", fractionalDigits],
aNumber]];
}
else // !displayDecimalSeparator
[display setStringValue: [NSString stringWithFormat: @"%.0f", aNumber]];
}
-(void) setError
{
[display setStringValue: @"Error"];
}
- (void)applicationDidFinishLaunching: (NSNotification *)aNotification
{
CalcBrain *brain;
NSMenu *mainMenu;
NSMenu *menu;
NSMenuItem *menuItem;
mainMenu = AUTORELEASE ([NSMenu new]);
// Info
[mainMenu addItemWithTitle: @"Info..."
action: @selector (orderFrontStandardInfoPanel:)
keyEquivalent: @""];
// Edit SubMenu
menuItem = [mainMenu addItemWithTitle: @"Edit"
action: NULL
keyEquivalent: @""];
menu = AUTORELEASE ([NSMenu new]);
[mainMenu setSubmenu: menu forItem: menuItem];
[menu addItemWithTitle: @"Copy"
action: @selector (copy:)
keyEquivalent: @"c"];
[menu addItemWithTitle: @"SelectAll"
action: @selector (selectAll:)
keyEquivalent: @"a"];
[mainMenu addItemWithTitle: @"Hide"
action: @selector (hide:)
keyEquivalent: @"h"];
[mainMenu addItemWithTitle: @"Quit"
action: @selector (terminate:)
keyEquivalent: @"q"];
[NSApp setMainMenu: mainMenu];
brain = [CalcBrain new];
[brain setFace: self];
[self setBrain: brain];
[self orderFront: self];
}
@end
А вот так выглядит главный метод, отвечающий за запуск приложения:
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import "CalcFace.h"
int main (void)
{
ENTER_POOL
CalcFace *face;
NSApplication *app;
app = [NSApplication sharedApplication];
face = [CalcFace new];
[app setDelegate: face];
[app run];
LEAVE_POOL
return 0;
}
Совместимость
После размещения статьи на ЛОРе, читатели задали интересный вопрос по поводу совместимости с оригинальной и современной Cocoa.
Был взят минимальный пример и немного адаптирован:
#import <Cocoa/Cocoa.h>
int main()
{
[NSAutoreleasePool new];
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
id menubar = [[NSMenu new] autorelease];
id appMenuItem = [[NSMenuItem new] autorelease];
[menubar addItem:appMenuItem];
[NSApp setMainMenu:menubar];
id appMenu = [[NSMenu new] autorelease];
id appName = [[NSProcessInfo processInfo] processName];
id quitTitle = [@"Quit " stringByAppendingString:appName];
id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle
action:@selector(terminate:) keyEquivalent:@"q"] autorelease];
[appMenu addItem:quitMenuItem];
[appMenuItem setSubmenu:appMenu];
id window = [[[NSWindow alloc] initWithContentRect:NSMakeRect(10, 10, 200, 200)
styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO]
autorelease];
[window setTitle:appName];
[window makeKeyAndOrderFront:nil];
[NSApp activateIgnoringOtherApps:YES];
[NSApp run];
return 0;
}
И.. оно заработало! Пусть и с минимальными переделками, вот так выглядит запущенное приложение на Linux:
Современность
Разработка GNUstep как ни странно до сих пор продолжается, так выглядит наверное самое популярное приложение (из еще актуальных) на этом фреймворке:
Но только все графические примитивы и паттерны использования, заложенные в GNUstep — устарели, слабо представляю как такой UI/UX возможно соотнести с современным использованием десктопа, даже профессионального.
А вы смогли бы пользоваться таким ПО каждый день?
Версия для.. Windows
Ну и наконец последний разрыв шаблона на сегодня:
вы можете легко и просто поставить GNUstep.. на Windows.
Вот так это выглядит в работе:
Даже готовый инсталлятор есть, так что ничего не помешает «прикоснуться к прекрасному с минимальными усилиями».
Эпилог
Разумеется как NEXTstep так и GNUstep ныне являются чистой историей, поскольку даже заложенные в них подходы к построению UI/UX — концептуально устарели. Устарели настолько, что пользоваться таким интерфейсом даже автору (видевшему многое) — откровенно тяжело.
Реальное практическое использование фреймворка GNUstep для разработки современного ПО честно говоря — на уровне фантастики, поэтому остается только для кино и самых ярых фанатов.
Тем не менее, о существовании такого проекта стоит знать, хотя‑бы для понимания — насколько далеко в прошлое уходят корни современных технологий.
Но рабочие станции конечно были очень красивые, для тех-то лет:
P.S.
Это немного отцезурированная версия статьи, более трешевый оригинал которой доступен в нашем блоге. Скриншот GNUstep в работе был также опубликован на ЛОРе, что вызвало интересную дискуссию и внесение ряда дополнений в статью.
С учетом присутствия на Хабре живых свидетелей тех лет и описываемых событий, с радостью дополню текст деталями, известными только им — пишите;)
0x08 Software
Мы небольшая команда ветеранов ИТ‑индустрии, создаем и дорабатываем самое разнообразное программное обеспечение, наш софт автоматизирует бизнес‑процессы на трех континентах, в самых разных отраслях и условиях.
Оживляем давно умершее, чиним никогда не работавшее и создаем невозможное — затем рассказываем об этом в своих статьях.
Автор: alex0x08