Category Archives: Linux/Unix
Решил, что пора бы добавить поддержку макоси тоже. Всё портирование свелось к нескольким простым вещам:
- Собрать libtermkey. Под мак mono почему-то исключительно 32-битный, поэтому нужно следить за разрядностью выходного dylib-модуля. По сути достаточно в майкфайл добавить CCFLAGS += -arch i386 -arch x86_64 (я собирал fat binary) и выполнить make. Если не указать i386, скорее всего будет собран 64-разрядный модуль, и моно не сможет его загрузить для интеропа, будет сбивающий с толку DllNotFoundException (на самом деле dll found, но просто не может быть загружена).
- Найти libncurses и сделать для него symlink. Сам пакет может быть установлен при помощи MacPorts. У меня он автоматически поставился при установке midnight commander (через MacPorts)
- Убрать eventfd в качестве объекта-источника poll-сигналов. Заменил на self-pipe.
- Корректно проставить symlinks к оставшимся библиотекам (libc.so). Или прописать dllmap в App.config-файле – моно умеет оттуда распарсивать пути к библиотекам.
Всё в принципе работоспособно, но, к сожалению, качество эмуляторов терминала под Mac оставляет желать лучшего..
Вчера ночью я наконец добил этот баг, а сегодня вечером патч уже был закоммичен в master git-репозитория konsole. Для того, чтобы разобраться в сути проблемы, понадобилось несколько вечеров, пара виртуалок с линуксами OpenSuse (здесь, собственно, баг был обнаружен) и Debian (на ней я работал с KDevelop, потому что мне показалось, что gdb будет работать лучше в стабильной версии KDevelop – на самом деле всё было так же как и в OpenSuse) и исходники konsole и xterm.
Поиск точки сбоя
Как известно, чтобы поправить что-то, нужно сначала разобраться, что именно работает не так, локализовать проблему. Предстояло определить, что служит причиной неверного вывода на экран. Исходники konsole написаны хорошо, с большим количеством комментариев и удачными названиями классов, поэтому кандидаты на пристальный осмотр были определены сразу: классы TerminalDisplay, Emulation, Vt102Emulation, Pty и Screen. Идея была следующая: написать дампер текущего состояния визуализации и вызывать его при каждом обновлении. Обновление производил TerminalDisplay в функции updateImage(), и я добавил в него дамп матрицы символов и атрибутов, которые должны быть выведены на экран. После испытаний выяснилось, что в этом месте ошибочные атрибуты последних символов уже сформированы, и, значит, надо искать проблему не в выводе на экран, а в коде, который формирует эту матрицу буфера вывода.
Если мы с вами посмотрим на то, что такое эмуляторы терминала, то увидим, что они работают следующим образом. На входе эти программы получают последовательность байт (в которых кодируются как символы для вывода, так и различные управляющие коды), внутри себя они интерпретируют её как последовательность команд вида “добавь символ X”, “установи курсор в (1;3)”, “поменяй текущий цвет на Green” и на выходе выдают ту самую матрицу символов и атрибутов, которая выводится на экран.
Чтобы быстро понять, какие команды генерирует тестовая программа на ncurses, я расставил брейкпоинты на большинство методов класса Screen. И после некоторых мучений обнаружил, что при ширине экрана > 80 ncurses генерирует не те команды, что приходят, когда ширина экрана меньше 80 ! Выяснилось, что ncurses внутри оптимизирует свой вывод, и в проблемном случае для строки возвращает последовательность управляющих кодов, которая делает только удаление одного символа! А в конец строки после этого уже ничего не дописывается, и там появляется пустота.
Исцеление
Встал вопрос – почему эта штука работает в других эмуляторах терминалов ? За ответом было решено лезть в исходники gnome-terminal (точнее, vte – библиотеки, которую gnome-terminal использует для эмуляции), но они оказались намного менее читаемы по сравнению с konsole. И я попробовал вместо гномовских посмотреть исходники xterm, благо они оказались более понятны. И нашёл код функции удаления символа, из которого стало понятно, что после удаления символов будут дописаны пробелы справа, но не с пустыми атрибутами, а с теми, которые установлены сейчас в консоли.
Собственно, после этого уже не составляло труда дописать пару строк, исправляющих проблему. Протестировал на тестовом приложении, запустил и console framework, проверил работоспособность нескольких других консольных программ (mc, vim), всё было ок, оставалось предложить патч для исправления бага. Для этого оказалось достаточным написать комментарий с детальным описанием сути проблемы и приложить патч. В течение следующего дня патч был проверен и закоммичен в master.
Успех.jpg:
На этом подвожу итог с разборками в Konsole, рад как слон, что не придётся писать обходные пути в console framework для рендеринга в konsole-based эмуляторах терминала. Интересно, сколько времени пройдёт до тех пор, пока фикс не будет втянут в пакеты популярных дистрибутивов ? Прямо реально интересно, готов даже не лениться и периодически проверять самые ходовые дистрибутивы (Fedora, CentOS, OpenSuse, Debian, Ubuntu, Arch, Gentoo). Только не знаю, как.
Ковыряясь с багом в Konsole, попробовал “родную” IDE для программирования под KDE – KDevelop. Мне нужно было чтобы IDE умела:
- Импортировать проект из cmake-файла
- Редактировать C++ код с более или менее удобной навигацией
- Собирать проект, желательно одной кнопкой
- Запускать проект в отладчике, желательно одной кнопкой
Сначала думал попробовать Eclipse, так как нашел в сети инфу о каком-то плагине, который по cmake-файлу умеет генерировать eclipse project, но в jabber конфочке мне подсказали, что KDevelop умеет это делать из коробки.
В общем, с первым пунктом KDevelop вполне справился. Единственное замечание: импорт не работает, если вы выбираете папку с проектом, к которой у KDevelop нет доступа. Я скачал исходники из-под рута, а KDevelop был запущен под обычным пользователем. Проблема заключается в том, что KDevelop не сообщает о сути ошибки, а просто молча ничего не делает. Возможно, эта проблема уже исправлена в более свежих версиях (я работал с KDevelop в Debian, в который как известно, попадают не самые свежие версии пакетов). Работает также импорт из обычных make-файлов (которые появляются после выполнения ./configure). Так я сымпортировал туда же проект xterm (то есть для обычного make сначала нужно выполнить ./configure, а потом импортировать проект в KDevelop).
Второй пункт тоже отработал замечательно – навигация по С++ действительно хороша, IDE умеет переходить не только по Ctrl+Click к объявлению функции, но и по клику на сигнатуру объявления функции – к её определению. Также хорошо показываются комментарии к функциям. Есть навигация вперёд-назад по Cmd+Right/Left. Тела макросов показываются при наведении мышкой. Неприятный момент: если отсутствуют нужные библиотеки, их инклуды подсвечиваются красным, и после установки требуемых заголовочных файлов это уже не исправляется, пришлось перезапускать IDE. Впрочем, это мелочь.
При сборке проекта нужно сконфигурировать хитрое окошко. В качестве исполняемого файла, который будет запускаться, можно выбрать таск, определённый в make-файле:
А можно выбрать просто путь к исполняемому файлу, который появится в результате сборки:
Для отладчика мне хватило прописать “gdb”:
Вообще, я не шарю в том, как устроены make-файлы, и для меня то, как KDevelop определяет по названию таски то, что мне действительно нужно запустить, тайна. Как-нибудь, может быть, разберусь и в этой загадке мироздания.
Кнопка Build selection вроде бы должна собирать только тот проект, который у меня сейчас под курсором мыши, но почему-то собираются все проекты. Непонятно, но фиг с ним, думается, что в противном случае было бы больше проблем.
Debug запускает выбранный Launch под отладчиком. С Konsole такое почему-то не заработало, но можно было сначала нажать Execute, а потом приаттачиться к процессу. Отладчик вполне работоспособен, хотя трассировка хромает не по-детски. Лучше ставить брейкпоинты. Несколько раз вылетало в одном и том же месте (gdb слетал, программа продолжала работать) – при наведении мыши на локальную переменную, которая должна содержать строку в unicode. Потом вроде починилось, но осадок остался. Видимо, отладка ещё довольно сырая, и уповать на неё не стоит.
В целом, впечатление хорошее. Раньше я для написания мелких тестовых программ на Си пользовался CodeBlocks, теперь, наверное, буду использовать KDevelop. В первую очередь, из-за хорошей поддержки make и cmake и удобной навигации по коду.
0