Приложение A. Редактирование make-файлов в разных операционных системами
Если, наряду с операционной системой Linux, вы работаете с операционными системами фирмы Microsoft (DOS, Windows), то при редактировании make-файлов в разных системах могут возникнуть определенные трудности.
Проблема состоит в том, что принятый в Unix-подобных операционных системах формат хранения текстовых файлов, несколько отличается от формата DOS/Windows. В Unix каждая строка текстового файла заканчивается символом "перевод строки" (код 0x0A). В DOS и Windows текстовые строки разделяются парой символов - "возврат каретки", "перевод строки" (0x0D, 0x0A).
Linux-версия программы GNU Make будет нормально работать с make-файлами, написанными в среде Linux. Версия утилиты GNU Make для Windows также будет нормально работать с make-файлами, подготовленными в среде Windows. Проблема возникнет лишь в том случае, если попытаться обработать Linux-версией программы GNU Make текстовой файл, подготовленный в среде DOS или Windows. Подобная ситуация может возникнуть "нечаянно" - достаточно лишь "сохранить" make-файл для среды Linux в текстовом редакторе DOS/Windows, чтобы он оказался "испорчен".
В отличие от компилятора GCC, который просто игнорирует символы "возврат каретки", GNU Make рассматривает их как "обычные" символы, которые вполне могут быть частью имени. В результате все слова, находящиеся в конце строк, искажаются, так как сзади к ним добавляется невидимый символ "возврат каретки". В следующем примере имя файла TextLine.o будет искажено (его длина будет составлять одиннадцать символов из-за невидимого "возврата строки"):
iEdit: main.o Editor.o TextLine.o
gcc $^ -o $@
Разумеется, на диске не найдется файла с таким "странным" именем и make-файл будет работать неверно. Дело осложняется еще и тем, что выдаваемые на экран диагностические сообщения также искажаются (при выводе на экран символ "возврат каретки" возвращает курсор в начало строки) и зачастую представляют собой лишь бессмысленные "обрывки" слов.
Описанную проблему можно решить разными способами. Организационный метод решения - никогда не пытаться редактировать make-файл, находясь в "чужеродной" для него среде. Другой возможный подход - "принудительно" удалять из make-файла символы "возврат каретки" (либо "вручную" - текстовым редактором, либо с помощью подходящей программы).
Приложение B. Организация иерархии каталогов в сложных проектах
Для сложных проектов, состоящих из большого количества файлов, я предпочитаю более сложную организацию каталогов, чем та, которая приводилась в качестве примера в разделе 1.7. "Разнесение разных версий программы по отдельным директориям". Основная идея заключается в том, чтобы файлы с разным "назначением" помещались в разные каталоги. В моих проектах дерево каталогов выглядит примерно так:
- имя_проекта /
- bin /
- linux_debug /
- linux_release /
- windows_debug /
- windows_release /
- doc /
- project /
- Makefile
- make_debug
- make_release
- src /
В каталог bin помещаются результаты компиляции - объектные и исполняемые файлы, библиотеки и тому подобное. Этот каталог можно "безболезненно" удалить - при следующей компиляции он будет автоматически создан заново. В этом каталоге каждая из возможных конфигураций программы имеет свою отдельную директорию. Как правило, я делаю четыре конфигурации программы - для каждой из двух операционных систем (Linux и Windows) имеется отладочная и рабочая версии программы.
В директорию doc я помещаю различные текстовые файлы - документацию, замечания, список ошибок и тому подобное. Здесь же располагается и файл README.txt.
В каталоге project находится make-файл проекта и командные файлы, используемые для сборки программы в разных конфигурациях.
В каталог src я помещаю исходные тексты программы. Внутри директории src имеется своя иерархия каталогов, отражающая логическую структуру программы.
Вот пример make-файла, который работает с подобной структурой директорий проекта:
program_name := iEdit
source_dirs := . Editor TextLine
include_dirs := /c/aproj/lib /c/aproj/lib/linux
link_flags := -static
source_dirs := $(addprefix ../../src/, $(source_dirs) )
source_files := $(wildcard $(addsuffix /*.cpp, $(source_dirs) ) )
object_files := $(notdir $(source_files) )
object_files := $(object_files:.cpp=.o)
$(program_name): $(object_files)
gcc $^ -o $@ $(link_flags) -pipe
VPATH := $(source_dirs)
%.o: %.cpp
gcc $< -c $(compile_flags) $(addprefix -I , $(include_dirs)) -MD -pipe 2>log
include $(wildcard *.d)
Список директорий, где располагаются файлы с исходными текстами (source_dirs), задается относительно каталога src. Вот как выглядит командный файл, собирающий отладочную версию программы:
mkdir -p ../bin/linux_debug
make compile_flags="-O0 -g" \
--directory=../bin/linux_debug \
--makefile=../../project/Makefile
Командный файл, собирающий рабочую версию программы выглядит аналогично:
mkdir -p ../bin/linux_release
make compile_flags="-O3 -funroll-loops -fomit-frame-pointer" \
--directory=../bin/linux_release \
--makefile=../../project/Makefile
Приложение C. Компилятор GCC
GNU Compiler Collection (GCC) - это семейство компиляторов с языков C, C++ и Object-C, которые объединены общей технологией и распространяются в рамках проекта GNU. Домашняя страничка компилятора находится по адресу www.gnu.org/software/gcc/gcc.html
Этот компилятор является "стандартным" средством для компиляции всех программ, входящих в проект GNU. GCC также является основным компилятором операционной системы Linux - с его помощью компилируется ядро системы.
Версии компилятора
Компилятор GCC развивается весьма динамично - программа улучшается, исправляются обнаруженные ошибки, добавляются новые возможности. Всегда желательно знать с какой версией компиляторы вы в данный момент работаете. Возможно, что эта версия еще не поддерживает нужные вам возможности или содержит ошибки, которые могут повлиять на работоспособность компилируемой программы. Узнать версию компилятора GCC можно с помощью ключа -v:
gcc -v
Отладка
"Стандартным" средством для отладки программ, скомпилированных компилятором GCC, является отладчик GDB. Этот отладчик свободно распространяется в рамках проекта GNU. Домашняя страничка отладчика находится по адресу www.gnu.org/software/gdb/gdb.html.
Для подготовки отладочной версии программы с помощью компилятора GCC, достаточно отключить оптимизацию и включить генерацию отладочной информации. Для этого я использую следующие опции компиляции:
-g | Генерировать отладочную информацию |
-O0 | Отключить оптимизацию |
Отладчик GDB имеет текстовой интерфейс командной строки. Мне этот интерфейс кажется не очень удобными, поэтому я пользуюсь графической оболочкой DataDisplayDebugger (DDD). Эта оболочка является надстройкой над "текстовыми" отладчиками, реализующей для них удобный графический интерфейс. Программа DDD также входит в проект GNU. Ее домашняя страничка находится по адресу www.gnu.org/software/ddd . DataDisplayDebugger работает в среде X-Windows.
Рабочий вариант
При компиляции рабочего варианта программы, я включаю максимальную оптимизацию по скорости. Возможно это приводит к некоторому увеличению размера программы, но я считаю это не слишком важным. Вряд ли кто-нибудь заметит увеличение размера программы на пять-десять килобайт. В то же время быстродействия программам всегда не хватает.
Компилятор GCC имеет большое количество опций, управляющих процессом кодогенерации и оптимизации, с которыми вы можете экспериментировать, добиваясь максимального быстродействия программы. Для своих проектов я использую следующие настройки:
Ключ Назначение
-O3 Максимальная оптимизация
-fomit-frame-pointer Не использовать указатель на стековый фрейм. Аналогичен флажку -Oy компилятора Visual-C. Компилятор будет адресовать переменные в стеке с помощью регистра ESP а регистр EBP "высвобождается" для использования в качестве регистра общего назначения.
-mcpu=pentium Оптимизировать код для процессора Pentium (однако программа по прежнему будет работать даже на i386)
Обработка исключений
Если вы используете механизм исключений (exceptions) языка C++, то при компиляции должна быть включена соответствующая опция:
Ключ компиляции Назначение
-fexceptions Включить поддержку механизма исключительных ситуаций языка C++
Статическая и динамическая компоновка
По умолчанию компилятор компонует собранную программу с динамическими версиями стандартных библиотек. Это не всегда удобно. Для того чтобы стандартные библиотеки компоновались статически, нужно использовать опцию --static. В этом случае генерируется полностью "статический" код, который для своей работы не требует наличия каких-либо загружаемых библиотек.
Получение листинга
Часто бывает полезным иметь ассемблерный листинг кода, генерируемого компилятором. С помощью такого листинга можно:
- Посмотреть, как те или иные опции оптимизации отражаются на генерируемом коде
- Посмотреть, каким образом компилятор обрабатывает те или иные конструкции языка программирования
- Выявлять ошибки, связанные с неправильной работой кодогенерации в компиляторе
- Узнать, какие в точности опции были включены при компиляции программы
Для получения ассемблерного листинга, я использую следующие опции компилятора GCC:
Ключ компиляции Назначение
-S Остановиться после стадии компиляции, перед стадией ассемблирования.
-fverbose-asm Генерировать дополнительные комментарии в ассемблерном листинге. Какие именно "дополнительные комментарии" будут помещены в текст листинга, зависит от версии компилятора.
Обратите внимание на то, что указание флажка -S просто "останавливает" компилятор после фазы генерации ассемблерного листинга, то есть процесс компиляции прерывается. Как следствие - процесс сборки программы и процесс генерации ассемблерных листингов "несовместимы" между собой. Можно либо получать листинги, либо собирать программу, но не то и другое одновременно. Для получения листингов я обычно создаю отдельный командный файл, который среди прочих опций компиляции содержит флажки -S и -fverbose-asm.
Весьма полезная возможность компилятора - помещать в листинг список всех опций компиляции, которые были включены в данный момент. Дело в том, что включение одних опций (например -O3) может "автоматически" приводить к включению других опций, а документация к GCC не всегда точна в описании подобных зависимостей. Некоторые версии GCC всегда помещают в листинг список используемых опций, другие версии делают это только при наличии флажка -fverbose-asm.
Переназначение ошибок в файл
По умолчанию, компилятор GCC выдает ошибки в стандартный поток сообщений об ошибках (файл с дескриптором 2). Иногда это не очень удобно - сообщения об ошибках могут быть очень длинными подробными а "прокрутка" экрана назад не всегда доступна. Поэтому я обычно перенаправляю сообщения компилятора в файл. Этот файл потом можно просмотреть любым стандартным средством. Вот, например, как может выглядеть правило для компиляции исходных текстов:
%.o: %.cpp
gcc -c $< 2>log
Опция -pipe
Компилятор GCC обрабатывает программу за несколько проходов, помещая промежуточные результаты компиляции во временные файлы. Процесс компиляцию можно ускорить, если воспользоваться опцией pipe. При включении этой опции, различные этапы компиляции начинают "общаться" между собой не через временные файлы, а через каналы обмена (pipes).
Тексты с символом "возврат каретки"
В отличие от утилиты GNU Make, компилятор GCC вполне "лояльно" относится к наличию символов "возврат каретки" в компилируемых текстах - такие символы попросту игнорируются. Поэтому, проблем при компиляции исходных текстов, подготовленных в среде DOS/Windows, возникнуть не должно.
Приложение D. "Гипотический" проект - текстовой редактор
В первой главе "Моя методика использования GNU Make" в качестве примера рассматривается "гипотический" проект - текстовой редактор. Он состоит из трех файлов с исходным текстом на языке C++ (main.cpp, Editor.cpp и TextLine.cpp), а также трех заголовочных файлов (main.h, Editor.h, TextLine.h). Ниже приведены листинги этих файлов.
Файл main.cpp:
#include "main.h"
void main()
{
}
Файл
main.h:
// main.h
Файл
Editor.cpp:
#include "Editor.h"
Файл Editor.h:
#include "TextLine.h"
Файл
TextLine.cpp:
#include "TextLine.h"
Файл
TextLine.h:
// TextLine.h
Назад |
Содержание