MinGW + Qt + Emacs HOWTO (Windows)


Аннотация

Данное руководство ориентировано на использование Windows платформы для написания кроссплатформенных оконных приложений. Пособие не претендует на полноту изложения всех связанных вопросов, оно предназначено для быстрого старта начинающих писать на языке С++ и вставших перед выбором инструментария.

В статье рассмотрены вопросы установки компилятора языка С++, кросс-платформенной оконной библиотеки, установки и настройки среды разработки.

Заметка об авторских правах

This document, MinGW + Qt + Emacs HOWTO (Windows), is copyrighted © 2006 by Alexander Sidorov. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is available at http://www.gnu.org/copyleft/fdl.html.

Содержание

Преамбула.

Существует достаточно большой выбор свободно доступных компиляторов языка С++. От известных всем изделий от "китов" индустрии: Sun Studio Compilers and Tools, Free Microsoft Visual C++ 2005 Express, Intel C++ Compiler Non-Commercial Version, Borland C++ Compiler и так далее, до достаточно малоизвестных рядовому пользователю продуктов: eC, TenDRA C/++ Compiler и прочих. Все они в разной степени поддерживают стандарт, большинство имеет собственные "дополнения" "улучшающие" язык, генерируют разный по производительности код и комплектуются разным набором утилит, предназначенных для обработки исходных текстов. Мне представляется затруднительным давать однозначную оценку какому-либо компилятору, могу лишь сказать, чем был обусловлен мой собственный выбор. Выбор этот, GCC - GNU Compiler Collection, в состав которой входит g++ — компилятор для языка C++ достаточно высокого качества, портированный на множество платформ, создающий код достаточно быстрый и достаточно полно поддерживающий стандарт, чтобы можно было писать такие вещи как частичные специализации шаблонных классов, или полноценно использовать библиотеку Boost.

Для платформы Windows GCC существует в виде портов в составе Cygwin и MinGW. Первое является окружением предназначенным для возможности использования POSIX вызовов в ОС Windows, эмулирующее необходимые системные вызовы через использование функциональности подсистемы Win32. MinGW это набор свободно распространяемых заголовочных файлов и библиотек в сочетании с набором инструментов GNU (порта GCC и ряда утилит), позволяющих создавать приложения Windows не используя сторонние DLL в качестве стандартной библиотеки С.

В статье пойдёт речь об использовании пакета MinGW, для которого существует поддержка со стороны оконной библиотеки, описанной ниже.

Сравнение оконных библиотек классов — тема не этой статьи, единственное, что можно сказать, что будет близко к истине, это то, что среди существующего разнообразия свободно распространяемых кроссплатформенных оконных библиотек для языка С++ на данный момент лидирующие позиции занимают лишь две из них: Gtk и Qt. Библиотеки, хоть, и предназначены для одного и того же, и внутри, и снаружи очень сильно отличаются друг от друга. Первая написана на чистом С, но тем не менее объектно-ориентирована, имеет привязку к 28 языкам программирования, а внутри напоминает слоёный пирог из дюжины надстроек и сервисов, иногда не слишком хорошо согласованных между собой, например, в вопросе стратегии выделения памяти. Qt более монолитна, хорошо спроектирована, написана на С++ и для С++, имеет отличную документацию, развивается в предсказуемом направлении и, в отличии от Gtk, даёт несколько более высокоуровневый интерфейс программисту.

Ориентация статьи на Qt не означает, что Gtk плоха, просто, на мой взгляд, Qt более подходящая, по крайней мере, для начинающих С++ программистов, она избавляет от излишней низкоуровневости API оконной библиотеки и позволяет сосредоточится непосредственно на архитектуре программы, не привлекая черезмерного внимания к деталям взаимодействия с GUI, которые у начинающих вызывают навязчивую идею "улучшить" предоставляемые в распоряжение библиотекой элементы управления.

Часть первая. Установка Qt и MinGW.

Библиотека Qt (текущая версия на момент написания статьи - 4.1.2) для платформы Windows существует в виде коммерческой версии за которую надо платить, позволяющей создавать проприетарное программное обеспечение и в виде бесплатной Open Source редакции, в которой отсутствует интеграция с коммерческими компиляторами и которая обязывает использовать лицензию GPL для создаваемых с помощью неё программ. Фактически, для Open Source редакции существует явная поддержка только одного компилятора — GCC, идущего в составе пакета MinGW. На этой странице сайта компании-разработчика Qt можно найти два архива, один из которых — набор исходных текстов библиотеки, которые необходимо скомпилировать, второй — готовый бинарник, устанавливающий библиотеку с набором утилит к ней и, при необходимости, пакет MinGW.

Качаем бинарник с любого зеркала, например, отсюда (около 44Мб).

Теперь, можно либо поставить самостоятельно MinGW и при установке Qt указать путь к установленному MinGW, либо в процессе установки Qt выбрать установку MinGW, последний будет скачан и установлен автоматически.

Для удобства стоит добавить к переменной окружения PATH путь к MinGW/bin и Qt/bin.

Сборка отладочной версии Qt программы.

Для того, чтобы собрать debug версию Qt программы (для возможности делать пошаговую отладку), потребуется предпринять дополнительные шаги. В том виде, в каком находится установленная библиотека, она не позволяет собирать отладочные версии программ. Т.к. установщик инсталлирует скомпилированные объектные модули библиотеки, которые потребуются для сборки пользовательской программы, только в release варианте. Требуется перекомпилировать библиотеку, добавив поддержку debug версии.

cd Qt_ROOT
configure.exe -debug-and-release
cd src
make

После пересборки библиотеки станет доступной компиляция Qt програм как в release, так и в debug варианте:

make debug
make release

Для того, чтобы стала доступной возможность пошаговой отладки необходимо также скачать и установить отладчик GNU gdb отсюда (ссылка взята с сайта MinGW).

Часть вторая. Быстрый старт, "Hello, World!" с использованием Qt.

Напишем и откомпилируем небольшой тест.

test.cpp:

#include <QApplication>
#include <QPushButton>

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    QPushButton hello("Hello, World!");
    hello.resize(100, 30);
    hello.show();
    return app.exec();
}

Запустим в командной строке в директории с файлом test.cpp (директория Qt/bin должна находится в переменной окружения PATH):

qmake -project 
qmake
make

qmake сгенерирует файл проекта, далее, на основе него создаст Makefile, после чего утилита make откомпилирует проект и поместит в директорию release полученный бинарник.

Советую посмотреть qtdemo, бинарник лежит в каталоге Qt/bin, чтобы получить представление о возможностях библиотеки. С выходом обновлений для MinGW можно безболезненно обновлять утилиты и сам компилятор. На этой странице лежат текущие обновления для MinGW.

Часть третья. Работа с проектом.

Компиляция программ.

Прежде чем заняться созданием собственной среды разработки, стоит получить представление о том, какими утилитами предстоит пользоваться при работе с проектом. В самом простейшем случае, когда исходный текст программы находится в одном-единственном файле, можно не тратя время в шелле запустить GCC, чтобы, в случае отсутствия ошибок, откомпилировать и слинковать программу и получить исполняемый бинарный файл:

g++ -o resultname source.cpp

Когда количество исходных файлов превышает один, такой подход нецелесообразен. Чтобы автоматизировать процесс создания исполняемого файла, используют программу GNU make, которая стала стандартом де-факто в области управления компиляцией. В составе пакета MinGW есть утилита mingw32-make.exe, которая является портом GNU make. Эта утилита использует спецификацию из Makefile, чтобы откомпилировать исходные тексты программы и произвести сборку. Makefile содержит записи, которые условно представимы в виде описаний: ЦЕЛЬ -> ЗАВИСИМОСТЬ -> КОМАНДА. Где ЦЕЛЬ — обычно имя файла генерируемого программой GNU make, ЗАВИСИМОСТЬ — файл или группа файлов используемых для порождения цели и КОМАНДА — действие, которое выполняет GNU make для того, чтобы получить ЦЕЛЬ из множества ЗАВИСИМОСТИ.

Ниже представлен пример простого Makefile, в котором описывается, что исполняемый файл decoder зависит от объектных файлов main.o и decoder.o, а также зависимости файлов между собой и правила их компиляции и сборки.

decoder : main.o decoder.o
	g++ -o decoder main.o decoder.o

main.o : main.cpp decoder.h
	g++ -c main.cpp

decoder.o : decoder.cpp decoder.h
	g++ -c decoder.cpp

clean :
	rm decoder main.o decoder.o

Для того, чтобы создать исполняемый файл decoder, надо набрать в шелле:

make

Для удаления исполняемого файла и очистки каталога от объектных файлов надо выполнить команду:

make clean

Детальное описание использования GNU make можно прочитать здесь.

qmake.

Библиотека Qt предоставляет в распоряжение программиста утилиту qmake, которая автоматизирует процесс создания Makefile для GNU make. Запуск:

qmake -project

в директории с исходными файлами приведёт к генерации файла проекта, который будет использован в дальнейшем утилитой qmake для генерации целевых Makefile -ов используемых GNU make: дальнейший запуск qmake без параметров создаст директории release и debug, файлы Makefile, Makefile.Debug, Makefile.Release, которые используются для сборки debug и release версий программой GNU make. Более подробную информацию о qmake можно получить вызвав Qt Assistant.

Пошаговая отладка программ.

Ключ компилятора -ggdb заставляет последний скомпилировать программу с отладочной информацией необходимой для пошаговой отладки с помощью программы GNU gdb:

g++ -ggdb -o resultname source.cpp

Выполнение команды:

gdb resultname

приведёт к запуску дебаггера GNU gdb и загрузке в него символьной таблицы отлаживаемого файла resultname. После старта дебаггер переходит в интерактивный режим и ожидает ввода команд от пользователя.

test1.cpp: int main(int, char**) { int a = 10; a++; a *= 7; a--; return 0; } g++ --ggdb -o result test1.cpp gdb result.exe GNU gdb 6.3 Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i686-pc-mingw32"... (gdb) list 1 int main(int, char**) 2 { 3 int a = 10; 4 a++; 5 a *= 7; 6 a--; 7 8 return 0; 9 } (gdb) break main Breakpoint 1 at 0x401305: file test1.cpp, line 2. (gdb) run Starting program: D:\test/result.exe gdb: do_initial_child_stuff: process 2588 gdb: kernel event for pid=2588 tid=3448 code=CREATE_PROCESS_DEBUG_EVENT) gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); ... gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_BREAKPOINT at 0x77f65a58 gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_BREAKPOINT at 0x00401305 Breakpoint 1, main () at test1.cpp:2 2 { (gdb) next gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_SINGLE_STEP at 0x00401400 gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_BREAKPOINT at 0x0040130a 3 int a = 10; (gdb) watch a Hardware watchpoint 2: a (gdb) continue Continuing. gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) еgdb: Target exception EXCEPTION_SINGLE_STEP at 0x00401311 Program received signal SIGTRAP, Trace/breakpoint trap. main () at test1.cpp:4 4 a++; (gdb) next gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_SINGLE_STEP at 0x00401314 gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_SINGLE_STEP at 0x00401316 Hardware watchpoint 2: a Old value = 2 New value = 11 main () at test1.cpp:5 5 a *= 7; (gdb) next gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_SINGLE_STEP at 0x00401319 gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_SINGLE_STEP at 0x0040131b gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_SINGLE_STEP at 0x0040131e gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_SINGLE_STEP at 0x00401320 gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_SINGLE_STEP at 0x00401323 Hardware watchpoint 2: a Old value = 11 New value = 77 main () at test1.cpp:6 6 a--; (gdb) next gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_SINGLE_STEP at 0x00401326 gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_SINGLE_STEP at 0x00401328 Hardware watchpoint 2: a Old value = 77 New value = 76 main () at test1.cpp:8 8 return 0; (gdb) next gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_SINGLE_STEP at 0x0040132d 9 } (gdb) next gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_SINGLE_STEP at 0x0040132e Watchpoint 2 deleted because the program has left the block in which its expression is valid. 0x0040132e in main () at test1.cpp:9 9 } (gdb) next gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_SINGLE_STEP at 0x00401237 0x00401237 in __mingw_CRTStartup () (gdb) next Single stepping until exit from function __mingw_CRTStartup, which has no line number information. gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_SINGLE_STEP at 0x00401239 gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_SINGLE_STEP at 0x004017f0 gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_BREAKPOINT at 0x0040123e gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_SINGLE_STEP at 0x00401241 gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXCEPTION_DEBUG_EVENT) gdb: Target exception EXCEPTION_SINGLE_STEP at 0x004018b0 gdb: child_resume.SetThreadContext: thread 2588.0xd78 ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: kernel event for pid=2588 tid=3448 code=EXIT_PROCESS_DEBUG_EVENT) Program exited normally. ContinueDebugEvent (cpid=2588, ctid=3448, DBG_CONTINUE); gdb: child_close, inferior_ptid=3448 (gdb)

Вывод GNU gdb достаточно многословен. В реальности применяют один из существующих фронтендов к gdb, который фильтрует вывод gdb и показывает параллельно исполнению кода текущее положение в исходном тексте программы. Ниже в статье будет показан процесс использования IDE для пошаговой отладки. Здесь вы можете найти краткий справочник по командам GNU gdb.

Qt Designer.

Qt Designer
Qt Designer

Пользовательский графический интерфейс можно создавать "руками" создавая в коде необходимые виджеты и размещая их "на глаз" в родительских контейнерах, но это отнимает много времени и становится слишком утомительным, если требуется сделать интерфейс, сложность которого превышает "примитивную". Библиотека позволяет упростить и автоматизировать процесс создания GUI предоставляя в распоряжение пользователям программу Qt Designer. Эта программа позволяет создавать диалоговые окна и виджеты (элементы управления) и сохранять их в виде xml файлов с расширением .ui. В состав утилит библиотеки входит uic - User Interface Compiler, который из xml файлов создаёт C++ заголовочные файлы содержащие декларации оконных классов. При использовании qmake вызов uic происходит автоматически, так, что всё, что остаётся сделать программисту, это подключить заголовочный файл в коде программы и написать код, создающий и инициализирующий необходимые объекты.

Qt включает в себя достаточно большой набор виджетов, чтобы обеспечить стандартную функциональность графического интерфейса, кроме того, позволяет пользователю разрабатывать и использовать собственные элементы управления. Для меж-объектного взаимодействия Qt применяет механизм сигналов и слотов как альтернативу механизму обратных вызовов, принятого в большинстве оконных библиотек. Суть механизма заключается в привязке объектов инициирующих сигналы с объектами содержащими слоты, между которыми установили привязку. Связывать сигналы и слоты виджетов, если есть такая необходимость, можно достаточно удобным способом непосредственно в Qt Designer. Более подробную информацию о механизме сигналов и слотов можно найти в Qt Assistant.

Пример редактирования диалогового окна
Пример редактирования диалогового окна

Разместив элементы управления, сделав необходимую привязку между сигналами и слотами и сохранив результат в .ui файле, требуется перегенерировать файл проекта, вызвав qmake -project, после чего в файл проекта добавятся созданные .ui файлы и, далее при вызове qmake будут созданы make файлы, при обработке которых, GNU make вызовет uic, который создаст необходимые заголовочные файлы. До вызова GNU make, для того чтобы задействовать в программе созданные объекты, необходимо подключить заголовочные файлы, которые будут созданы при вызове GNU make, и создать необходимые объекты. Более подробная информация, а также пример простого приложения, использующего результат работы Qt Designer можно найти в Qt Assistant.

Установка и настройка среды разработки.

Вступление.

До сих пор опускалась одна очень важная деталь: чем создавать и редактировать исходные тексты программ. Пора исправить этот пробел. Стандартные редакторы идущие вместе с операционной системой Windows: Notepad и Wordpad не подходят для обработки исходных текстов. Точнее сказать, они вообще ни для чего не подходят, т.к. попросту неудобны для работы. К счастью, существует множество альтернатив стандартным текстовым редакторам, среди которых, есть что выбирать.

К редактору для программиста предъявляются особые требования. Помимо удобного редактора текста, во-первых, он должен уметь делать специфичные вещи связанные с разработкой, такие как: подсветка текста, удобная навигация по коду, интеграция с окружением, чтобы можно было не переключаясь из редактора проводить процесс сборки и отладки и прочее, а во-вторых, должен иметь возможность гибкой настройки себя, чтобы пользователь мог избежать ненужного раздражения в работе.

GNU Emacs умеет всё. Не просто перечисленное, что должен уметь редактор для программиста, а вообще всё, что есть в области работы с текстовой информацией. Кроме того, GNU Emacs работает везде, что очень важно для многих, т.к. зачастую приходится работать в непривычном окружении. Достаточно указать GNU Emacs, где брать файл конфигурации и пользователь получает в своё распоряжение привычную среду для работы. GNU Emacs написан на языках C и ELisp и представляет собой ELisp интерпретатор, предоставляющий пользователю возможность переопределять любую ELisp функцию, что позволяет как угодно гибко настроить редактор под собственные нужды.

Для начинающего пользователя, возможно, GNU Emacs покажется слишком сложным для освоения: для конфигурации редактора применяется незнакомый язык ELisp, клавиатурные комбинации напоминают сложные распальцовки, состоящие из последовательностей нажатий нескольких клавиш, кроме того, возможно, для некоторых внешний вид редактора покажется отталкивающим.

Но, это так только на первый взгляд. Некоторая сложность освоения полностью компенсируется удобством работы, экономя потом многие часы. Клавиатурные комбинации вбиваются "в пальцы", результат работы дизайнеров выражающийся в красивом тулбаре или подмигивающей скрепке становится ненужным (справедливости ради, внешний вид тулбара тоже можно полностью изменить, использовав собственные иконки и привязки), а язык ELisp оказывается интуитивно-понятным, не требующим предварительного чтения здоровенных руководств.

Установка GNU Emacs и предварительные шаги.

На этой странице находится актуальная версия (на данный момент 22.0.50.1) GNU Emacs для Windows (около 23Мб). Выберите пункт: "Download latest EmacsW32+Emacs patched" — это пропатченная версия Emacs для более удобного использования в среде Windows.

Установка не должна вызвать каких-то сложностей. Просто принимаем всё по-умолчанию, что предлагает установщик.

Что необходимо знать для дальнейшего чтения. Клавиатурные комбинации используемые в Emacs принято давать в виде C-..., M-..., C-M-..., где "C" означает клавишу "Ctrl", "M" (Meta) означает клавишу "Alt" или "Esc", запись C-x означает нажатие и удерживание клавиши "Ctrl" и последующее нажатие "x", а M M l означает последовательное без удерживаний нажатие клавиш Meta Meta l. Там где встречается комбинация Meta-key, используется "Alt", а там где Meta key — "Esc". Для комбинаций C-key1 C-key2 клавишу "Ctrl" между нажатиями "key1" и "key2" отпускать необязательно. Домашним каталогом для Emacs, обозначаемым при операциях с файлами "~/" является директория "Documents and Settings/user name/Application Data/" на диске, где установлен Windows. Название директории приведено для английской версии Windows. Файлом конфигурации загружаемым при старте Emacs, является файл ~/.emacs, который будет создан автоматически после первого сохранения опций, доступного через меню "Options -> Save options", при условии, что были изменены какие-либо настройки.

Итак, заставим создать Emacs файл конфигурации. Выберем пункт "Options -> Show/Hide -> Column Numbers", чтобы Emacs отображал колонку текущей позиции курсора. Затем сохраним изменения: "Options -> Save options". Посмотрите результат, открыв файл "~/.emacs", используя C-x C-f. В результате появится файл конфигурации, в котором будут содержаться следущие строки:

(custom-set-variables
  ;; custom-set-variables was added by Custom.
  ;; If you edit it by hand, you could mess it up, so be careful.
  ;; Your init file should contain only one such instance.
  ;; If there is more than one, they won't work right.
 '(column-number-mode t))
(custom-set-faces
  ;; custom-set-faces was added by Custom.
  ;; If you edit it by hand, you could mess it up, so be careful.
  ;; Your init file should contain only one such instance.
  ;; If there is more than one, they won't work right.
 )

Эти два блока генерируются автоматически и должны существовать в единственном экземпляре в файле конфигурации. Некоторые настройки мы будем задавать в этих блоках, чтобы сохранить возможность переключать опции из меню, большинство же настроек, будут задаваться вполне нормальным способом с помощью конструкции (setq variable value).

Прежде чем продолжить, настоятельно рекомендую потратить двадцать минут на чтение руководства на родном языке, доступного через вызов М-х help-with-tutorial-spec-language, для того, чтобы не возникало совсем детских вопросов при работе с Emacs. Кроме того, первое время полезно держать под рукой распечатку краткого справочника по наиболее используемым клавиатурным привязкам XEmacs, большинство из которых совпадает с клавиатурными привязками Emacs.

Базовая настройка, не требующая дополнительных пакетов.

Есть вещи, настроенные по-умолчанию в Emacs, которые я не принял. Мне неудобно выделение текста с помощью C-SPC, вместо него, я использую привычную комбинацию с помощью клавиши "Shift" и команд перемещения курсора (здесь и далее строки из ~/.emacs файла):

;; text selection
(cua-selection-mode t)

Установка этого режима, кроме того, включит transient-mark-mode, отвечающий за подсветку выделенной области, так, что не будет необходимости дополнительно устанавливать transient-mark-mode.

По-умолчанию Emacs создаёт и сохраняет текст в ANSI кодировке. Используемая версия редактора скомпилирована с поддержкой MULE - пакета обеспечивающего интернационализацию. Как приемлимый вариант, я выбрал среду с UTF-8 кодировкой, во-первых, при сохранении текста содержащего только латинские символы он будет сохранён как обычный ANSI текст, во-вторых, UTF-8 позволяет работать одновременно как с кириллицей, так и с умляутами и символами с аксцентом, а в третьих, GCC понимает файлы в данной кодировке, так что не возникнет проблем при написании программ с комментариями на русском языке, к примеру. Наконец, утилиты Windows XP Pro понимают эту кодировку (Home Edition не распознаёт UTF-8). "Родная" Unicode- кодировка для Windows это UTF-16-LE, при желании можно выбрать её, но в этом случае не получится иметь ANSI текст при использовании букв только латинского алфавита и понимает ли GCC UTF-16-LE или нет, я не выяснял. Для ввода кириллицы, я как владелец ноутбука с немецкой клавиатурой не желающий ни обезображивать последнюю наклейками с русскими буквами, ни учить дополнительно слепой десяти пальцевый ввод используя йцукен- раскладку, использую транслитерацию латинских символов при вводе. Настройка языковой среды и метода ввода:

;; Mule cyrillic config
(set-language-environment 'UTF-8)
(setq default-input-method "cyrillic-translit")

Для переключения метода ввода, ввиду невозможности задействовать стандартную комбинацию C-M-\ на моей клавиатуре, я переназначил переключение на последовательность C-x C-y:

;; C-x C-y switchs input method
(global-set-key (kbd "\C-x \C-y") 'toggle-input-method)

Следущее, что мне не нужно, это стартовое сообщение, его отключение:

;; Prevent the startup message
(setq inhibit-startup-message t)

Если фрейм разделён на несколько окон (попробуйте C-x 2), Emacs по-умолачанию показывает "неактивные" курсоры во всех остальных окнах, мне больше нравится отображение принятое по-умолчанию в XEmacs, когда показывается только один курсор в текущем, активном окне. Для достижения этого требуется внести значение переменной cursor-in-non-selected-windows в список custom-set-variables:

(custom-set-variables
  ;; custom-set-variables was added by Custom.
  ;; If you edit it by hand, you could mess it up, so be careful.
  ;; Your init file should contain only one such instance.
  ;; If there is more than one, they won't work right.
 '(column-number-mode t)
  ;; inhibit hallow cursors in inactive windows
 '(cursor-in-non-selected-windows nil))

Так же беру способ переключения между окнами внутри текущего фрейма из XEmacs, когда переключение осуществляется клавишами C-TAB:

;; C-tab switchs to a next window
(global-set-key [(control tab)] 'other-window)

Есть одна вещь жутко раздражавшая меня: прыжковый скроллинг при достижении курсора нижней или верхней границ экрана. Я предпочитаю "консервативный" способ, когда при скроллинге происходит сдвиг на одну строку, кроме того, мне удобно иметь "отступ" при скроллинге, когда последний происходит не при достижении границы экрана, а на несколько строк раньше:

;; conservatively scrolling
(setq scroll-conservatively 50)
(setq scroll-margin 4)

При работе с исходными текстами программ я предпочитаю иметь отступ в виде табуляции длинной в два символа и всегда, а не только при вводе, при попадании курсора на скобку, подсвечивать парную ей, кроме того, я немного изменил цвет подсветки парных скобок:

(custom-set-variables
  ;; custom-set-variables was added by Custom.
  ;; If you edit it by hand, you could mess it up, so be careful.
  ;; Your init file should contain only one such instance.
  ;; If there is more than one, they won't work right.
  '(column-number-mode t)
  ;; inhibit hallow cursors in inactive windows
  '(cursor-in-non-selected-windows nil))
  '(show-paren-mode t)
  '(standard-indent 2)
  '(tab-width 2))
(custom-set-faces
  ;; custom-set-faces was added by Custom.
  ;; If you edit it by hand, you could mess it up, so be careful.
  ;; Your init file should contain only one such instance.
  ;; If there is more than one, they won't work right.
  '(show-paren-match ((((class color) (background light)) (:background "#bfe8df")))))

Одной из замечательных особенностей Emacs является умение форматировать исходные тексты. Причём, не только по команде для выбранного текста (Вам не нравится стиль в котором написана программа кем-то, когда приходится править чужие исходники? Не проблема — выделите текст и наберите команду M-x indent-region), но и непосредственно при его наборе, когда курсор достигает "электрик-символов" происходит форматирование текущей строки. В стиле отступов используемом по-умолчанию для c++-mode меня всё устраивает кроме отступа для подоператоров (термин из документации Emacs, означающий строки после операторов if, else, while, do, switch, for, try, catch, finally, synchronized и т.д.) и case- меток. Установка нужных значений:

;; c-style
(c-set-offset 'substatement-open 0)
(c-set-offset 'case-label 0)

Расширение возможностей редактора.

У Emacs есть собрат - XEmacs, который возник в результате форка первого из-за несогласия некоторых разработчиков GNU Emacs с его создателем Ричардом Столманом в вопросе дальнейшего направления развития редактора. Главное отличие XEmacs от Emacs для пользователей заключается в наличии в нём пакетного менеджера позволяющего не заботится о ручной установке модулей, которые пишутся к редактору и дальнейшем отслеживании их версий. Второе "большое" отличие заключается в наличии табов в XEmacs, и их отсутствии в Emacs. Впрочем, это нисколько не недостаток, т.к. табы в Emacs всё же имеются. Надо лишь поставить необходимый модуль. Здесь лежит исходный текст модуля, позволяющего работать с табами в Emacs. Для того, чтобы его установить, необходимо создать в директории, в которой Emacs может найти модули, файл с именем tabbar.el и скопировать в него содержимое, доступное по данной ссылке. Примером подобной директории является site-lisp, которую можно найти в директории с установленным Emacs. Для активации модуля надо прописать в ~/.emacs:

;; tabs
(require 'tabbar)
(tabbar-mode t)
(global-set-key [(control shift tab)] 'tabbar-forward)
(global-set-key [(control meta shift tab)] 'tabbar-backward)
Табы в Emacs
Табы в Emacs

Последние две строки создают привязку клавиш к командам переключения вкладок. Табы группируются по режимам, которые установлены в содержащихся в них фреймах. Двойная стрелочка на скриншоте — при клике на неё переключает отображение табов в выбранном режиме и отображение режимов всех фреймов, которые открыты в редакторе.

tabbar.el не единственный модуль обеспечивающий табы в Emacs. Точнее сказать, существует модуль, облегчающий операции с файлами и буферами. На этой странице находится исходный текст модуля ido.el, после создания файла пропишите в файл конфигурации:

;; ido
(require 'ido)
(ido-mode t)

Этот модуль дополнительно к автодополнению, вызываемому нажатием клавиши табуляции, предлагает список выбора при открытии файла или при переключении на другой буфер, вызываемому при нажатии C-x b. Возможно вы найдёте этот модуль полезным и станете использовать его так же как я, совместно с tabbar.

Следущее удобное расширение, на которое стоит обратить внимание, это session.el. Оно позволяет сохранять историю команд, буфер kill-ring, историю открытия файлов и т.д., а так же, добавляет два полезных пункта меню, где можно выбрать файлы, которые открывались в последний раз или последние изменённые файлы. Установка:

;; session
(require 'session)
(add-hook 'after-init-hook 'session-initialize)

На этом, пожалуй, завершится обзор доводки Emacs для более удобного использования. Существует масса вещей, которые можно сделать с этим редактором, от превращения его в просмотрщик картинок, органайзер или средство ведения блогов до настройки его в качестве мощного nntp и почтового клиента, но, это тема не этой статьи, сейчас же перейдём к созданию из Emacs среды разработки C++ программ.

C++ IDE.

В этой главе речь пойдёт о вещах специфичных для процесса создания программ, которые облегчат процесс навигации по исходному коду, по файлам проекта, об интеграции Emacs с окружением для компиляции, дебаггером и навигации по ошибкам.

Существуют разные способы для того, чтобы включить автодополнение в Emacs. Самый простой из них — использование etags. Эта утилита устанавливается вместе с Emacs в директорию bin/, если вы захотите её использовать, то необходимо включить эту директорию в переменную окружения PATH, чтобы Emacs мог найти etags и использовать её для индексации файлов. Автодополнение привязано к комбинации M-TAB, но т.к. в Windows сочетание Alt-TAB переключает окна, можно использовать в качестве клавиши "Meta" клавишу "ESC", либо прописать свою собственную привязку для функции complete-symbol. etags служит не только для автодополнения, но и для того, чтобы перемещаться на определение функций, для этого надо поместить курсор на нужную функцию и нажать M-..

Я использую другой способ — пользуюсь функциональностью пакета Semantic, идущего в составе пакета CEDET. Посколько, последний необходим для дальнейших, описанных ниже шагов, я рекомендую не ставить Semantic отдельно, а скачать и установить CEDET. Отсюда скачайте последнюю версию (в данный момент 1.0pre3) CEDET, и разархивируйте содержимое каталога. В файл конфигурации пропишите:

;; Cedet
(setq semantic-load-turn-useful-things-on t)
(load-file "C:/Programme/Emacs/cedet-1.0pre3/common/cedet.el")
(global-set-key [?\C- ] 'semantic-ia-complete-symbol)
Autocompletion в Emacs
Autocompletion в Emacs

откорректировав путь к cedet.el. C-SPC вызовет автодополнение, а последовательность C-c , SPC всплывающее меню со списком методов для автодополнения.

При программировании бывает довольно удобным скрыть часть / части кода. Например, свернуть тела методов или комментарии. В Emacs есть режим Outline, который предназначен именно для этого. Кроме того, существуют модули, упрощающие / дополняющие использование данной функциональности. Я пользуюсь упрощённым вариантом свёртки, который доступен после установки модуля hideshow. Следущие строки в файле конфигурации установят загрузку модуля (после копирования содержимого, доступного по ссылке, в файл hideshow.el) и установят ловушку для c++-mode, при переходе Emacs в этот режим, автоматически запустится вспомогательный режим "hideshow":

;; hideshow
(load-library "hideshow")
(add-hook 'c++-mode-hook  ; other modes similarly
           (lambda () (hs-minor-mode 1)))

Клавиатурные комбинации C-c @ C-h, C-c @ C-s, C-c @ C-M-h, C-c @ C-M-s покажут / скроют блок, покажут / скроют все блоки, где блоком является либо операторный блок кода заключённый в "{ }", либо комментарий. Остальные привязки можно посмотреть открыв исходный код hideshow.el и прочитав комментарии к коду в начале файла.

Следущая, весьма полезная вещь, которую стоит установить в Emacs, это ECB — Emacs Code Browser. Этот модуль показывает дерево директорий, список исходных файлов, список классов и методов в текущем файле, историю посещений и выходное окно компиляции. Для работы модуля требуется наличие установленных дополнительных модулей, но с установкой CEDET, всё необходимое будет уже установлено. Для установки ECB необходимо скачать CEDET с этой страницы (на момент написания версия 2.32beta1), разархивировать модуль и прописать в файл конфигурации:

;; ECB
(add-to-list 'load-path "c:/Programme/Emacs/ecb-2.32/") 
(require 'ecb)
(global-set-key (kbd "\e\el") 'ecb-toggle-ecb-windows)
(global-set-key (kbd "\e\eea") 'ecb-activate)
(global-set-key (kbd "\e\eed") 'ecb-deactivate)

скорректировав путь к ECB. Последние три строки создают привязки активизации / деактивизации модуля и переключения редактирования кода в полноэкранный режим. M M e aвключает ECB, M M e d выгружает ECB, a M M l переключает режим окна редактирования кода. Надо отметить, что использование ECB удобно не только при работе с проектом. Дерево директорий настраивается на отображение используемых директорий, делая открытие файлов весьма неутомительным. Добавьте следущие строки в список custom-set-variables для подавления стартового "Совета дня" и для задания отображаемых директорий в окне дерева директорий, скорректировав список необходимых каталогов:

 '(ecb-source-path (quote ("~/" "c:/develop")))
 '(ecb-tip-of-the-day nil)
Emacs Code Browser
Emacs Code Browser

Примерно так выглядит ECB — внешний вид можно изменить удалив или переместив куда угодно любые окна. Для навигации по окнам используйте комбинации: C-c . g d - дерево директорий, C-c . g s - окно со списком исходных файлов, C-c . g m - окно со списком функций и переменных, C-c . g h - история открытия файлов и C-c . g 1 - окно редактирования кода. Все привязки можно переназначить на более удобные комбинации, если есть в этом необходимость. Также, можно выбирать файлы с помощью мыши, кнопкой выбора по-умолчанию является средняя кнопка.

После обработки исходных текстов наступает момент компиляции проекта. В Emacs запуск компиляции вызывается M-x compile. По-умолчанию Emacs вызывает make с ключём -k означающим, что следует продолжать компиляцию так долго, насколько это возможно, несмотря на ошибки. Это, естественно, можно перенастроить, внеся в список custom-set-variables значение для переменной compile-command (строковые значения необходимо брать в кавычки). Выход процесса компиляции отображается в служебном буфере *compilation*, который становится видимым. Если в процессе компилятор выдал предупреждения или ошибки, то они отображаются в этом буфере, визуально выделяясь цветом. При клике на сообщении или при нажатии клавиши "Enter", если курсор находится на сообщении, происходит переход на соответствующую строку в нужном буфере. По-умолчанию комбинации C-x ` и M-g p осуществляют переход на строки соответствующие следущей / предыдущей ошибке.

Если программа была собрана с отладочной информацией (make debug), в Emacs можно запустить дебаггер GNU gdb для пошаговой отладки: M-x gdb. Emacs спросит имя отлаживаемого файла, загрузит из него символьную таблицу и перейдёт в интерактивный режим ожидания ввода команд пользователя. Если установить точку останова и запустить дебаггер на выполнение программы, при достижении точки останова Emacs разделит окно на две части и покажет исходный код программы синхронизируя текущее положение исполнения команд с показом соответствующего места в исходном тексте.

Заключение.

На этом пришло время закончить обзор средств имеющихся в распоряжении программиста, который и так уже затянулся. За кадром осталось множество вопросов, такие как детальное руководство для пользователя Emacs или, например, интеграция Emacs с системой контроля версий, или использование средств для рефакторинга в Emacs, но предполагается, что читатель или заинтересуется написанным и сократит для себя дорогу, а в дальнейшем самостоятельно прочитает / найдёт всё необходимое, либо на этой строке закончит чтение и займётся поиском других средств для создания кроссплатформенных оконных приложений.

Коллекция ссылок.