GCC 2014
GCC 2014
УТВЕРЖДАЮ
Проректор-директор ИК
А.В. Замятин
« » 2013 г.
Издательство
Томского политехнического университета
2013
УДК 629.76
ББК 00000
А00
Савенко И.И.
А00 Компиляторы. Компиляция программного обеспечения в ОС
FreeBSD: методические указания к выполнению лабораторных ра-
бот по курсу «Системное программное обеспечение» для студен-
тов направлений 230100 «Информатика и вычислительная техни-
ка» и 230400 «Информационные системы и технологии»
Института кибернетики
ТПУ / Cост.: И.И. Савенко; Томский политехнический уни-
верситет. – Томск: Изд-во Томского политехнического универси-
тета, 2014. – 28 с.
УДК 000000
ББК 00000
Председатель учебно-методической
комиссии __________
Рецензент
Кандидат технических наук, доцент кафедры АиКС ИК ТПУ
А.С. Фадеев
2
Содержание
ВВЕДЕНИЕ .....................................................................................................4
1 Процесс компиляции. Компиляторы. .....................................................5
2 Компиляция приложения, разработанного на языке С++ ....................7
2.1 Предупреждение об ошибках ...........................................................9
2.2 Оптимизация программы ..................................................................9
2.3 Разделенная трансляция ..................................................................10
2.4 Заключение .......................................................................................12
3 Автоматизация процесса компиляции, используя утилиту make
(GNU make)....................................................................................................13
3.1 Структура make-файла.....................................................................13
3.2 Использование переменных ............................................................15
3.3 Функции (GNU make) ......................................................................16
3.4 Использование переменной VPATH. .............................................17
3.5 Заключение .......................................................................................18
ЗАКЛЮЧЕНИЕ .............................................................................................19
Список используемых источников .............................................................24
Приложение 1. Исходный код демонстрационной программы ...............25
Приложение 2. Основной список ключей компилятора g++ ...................27
Приложение 3. Ключи утилиты GNU make ...............................................28
3
ВВЕДЕНИЕ
Unix-подобные операционные системы (ОС) являются основными
ОС используемыми в научных центрах. Их использование позволяет
получить максимум производительности от аппаратного обеспечения
при выполнение сложных математических расчетов. Таким образом,
разработка собственных программ для Unix-подобных ОС является од-
ной из важных задач, которую должен уметь решать программист.
Целью данной работы является ознакомление с таким понятием как
компиляция, а также получение навыков по разработке программного
обеспечения в операционной системе FreeBSD.
4
1 Процесс компиляции. Компиляторы.
Компилятор
Целевая программа
Рис. 1. Компилятор
Препроцессор
Модифицированная исходная
программа
Компилятор
Ассемблер
Перемещаемый машинный код
Библиотечные файлы
Компоновщик/загрузчик Перемещаемые объектные файлы
Целевая программа
6
(Linker) и Загрузчик (Loader). Процесс компоновки не так уж и прост,
как может показаться, некоторые особенности будут рассмотрены ниже.
Выбор компилятора при разработке программ зависит как от ОС,
архитектуры ЭВМ, а также от ЯП и его версии. Наиболее популярным
компилятором при разработке приложений под ОС Windows является
MSVC++, разработанный компанией Microsoft. Что же касается Unix-
подобных систем, то тут чаще всего используется пакет компиляторов
GCC. В данном пакете содержатся компиляторы для различных ЯП,
например C (компилятор gcc), C++(компилятор g++), Java (компилятор
gjc), Objective C и других, а также компиляторы для различных архитек-
тур (ARM, x86 и т.д.) [1].
В ходе лабораторной работы в качестве исходного языка для напи-
сания программы будет использоваться язык программирования C++.
Программа на языке С++ будет преобразована компилятором в машин-
ный код. Целью данной работы не является разработка собственного
компилятора, поэтому мы не будем глубоко погружаться в структуру
компилятора и рассматривать другие существующие языковые процес-
соры, например, интерпретаторы. Главное понять, как осуществляется
преобразование исходного кода программы в машинный код.
Листинг 1
#include <iostream> //подключение заголовочного файла
/**
* argc - содержит количество переданных аргументов
7
* argv - значения аргументов
*/
int main( int argc, const char* argv[] )
{
int x; //Инициализация переменой целочисленного типа
int y;
std::cout << x << " + " << y << " = " << x + y <<
std::endl;
}
}
В листинге 1 приведен исходный код программы на языке С++, ко-
торая осуществляет сложение двух чисел. Если приведенный в листинге
1 код вам не понятен, вы можете познакомиться с основами языка С++,
которые отлично описаны на сайте [2].
Перейдем к компиляции, чтобы откомпилировать нашу программу
необходимо вызвать компилятор g++ и указать путь к файлу с исход-
ными данными в качестве параметра.
%g++ [Link]
%ls
%[Link] [Link]
В результате создался исполняемый файл с именем по умолчанию
[Link], если проводить аналогию с операционной системой Windows, был
создан *.exe файл. Для того, чтобы запустить данную программу необ-
ходимо воспользоваться следующей командой.
%./[Link] 45 43
45 + 43 = 88
Если вас не устраивает имя программы, присвоенное по умолча-
нию, то при компиляции можно задать новое, используя ключ –o.
%g++ [Link] –o demo
Вышеуказанное - это минимальное, что можно получить от всех
возможностей компилятора. На самом деле, умение использовать опре-
деленный набор ключей компиляции, позволит разрабатывать безоши-
бочные приложения, а также за минимальный промежуток времени. Как
8
вы могли заметить, компиляция довольно медленный процесс, даже на
примере одного файла, а представьте, сколько времени займет данный
процесс если программа состоит из нескольких тысяч файлов, поэтому
очень важно использовать правильный набор ключей, чтобы сократить
время компиляции (Приложение 2).
9
-O1 – мягкая оптимизация, незначительное увеличение скоро-
сти процесса компиляции, а также уменьшение размера целевой
программы;
-O2 – использует практически все доступные компилятору ме-
тоды оптимизации, как скорости выполнения, так и размера
программы;
-O3 – максимальная оптимизация, которая включает такие ме-
тоды оптимизации как развертку циклов и автоматическое
встраивание функций.
-Os – позволяет автоматизировать размер программы.
Способ 1
Рассмотрим первый способ компиляции, без разделения трансля-
ции исходных файлов программы. Все максимально просто, необходи-
мо компилятору передать пути к файлам с расширением *.cpp.
%g++ [Link] [Link] -o demo -std=gnu++98
%./demo 45 43
45 + 43 = 88
Как вы можете заметить, программа успешно откомпилирована и
может быть использована. Для справки, если файлов много, то можно
воспользоваться шаблоном поиска файлов типа: *.cpp.
%g++ *.cpp -o demo -std=gnu++98 –Os
Способ 2
Разделенная трансляция подразумевает собой разбиение процесса
компиляции на 2 стадии:
создание объектных файлов;
компоновка, объектных файлов в целевую программу.
Создание объектных файлов обеспечивается при помощи исполь-
зования ключа –с, это делается следующим образом:
%g++ *.cpp –c
%ls -l *.o
-rw-r--r-- 1 root root 3316 23 июн 17:07 main.o
-rw-r--r-- 1 root root 803 23 июн 17:07 summ.o
В результате у нас получилось 2 объектных файла, которые полно-
стью соответствуют исходным файлам программы. Далее необходимо
осуществить компоновку объектных файлов, для этого необходимо вос-
пользоваться ключом –o.
%g++ *.o -o demo_o
%./demo_o 1 2
1 + 2 = 3
11
Таким образом, если внести изменения только в один из файлов, то
трансляции других файлов не является обязательной, достаточно со-
здать объектный файл модифицированного файла и скомпоновать его с
уже существующими объектными файлами.
2.4 Заключение
12
3 Автоматизация процесса компиляции, используя утилиту make
(GNU make)
13
Структура make-файла максимально простая, он состоит из целей и
макрокоманд. Чтобы описать цель необходимо задать:
имя цели – имя файла, который должен быть создан;
список зависимости – список имен файлов, от которых зависит
достижение цели;
список команд интерпретатора shell, которые должны быть вы-
полнены для достижения поставленной цели.
цель: зависимости
[tab] команда (где [tab] – символ табуляции)
Имя цели и список зависимостей составляют заголовок цели, запи-
сываются в одну строку и разделяются двоеточием. Цель может быть
ложная – это значит, что в качестве цели не будет выступать файл. Спи-
сок команд записывается со следующей строки, причем все команды
начинаются с обязательного символа табуляции. Любая строка в после-
довательности списка команд, не начинающаяся с табуляции или '#' -
считается завершением текущей цели и началом новой. Рассмотрим
простой пример make-файла позволяющего скомпилировать демонстра-
ционную программу:
all://ложная цель вызываемая по умолчанию
g++ [Link] [Link] -o demo
15
CC:= $(CC) -O2
demo: main.o summ.o
$(CC) main.o summ.o -o demo
main.o: [Link]
$(CC) -c [Link]
summ.o: [Link]
$(CC) -c [Link]
Использоваться может как один, так и другой способ инициализа-
ции, второй способ является оптимальным и надежным, так как нет
необходимости каждый раз вычислять значение переменной при ее ис-
пользовании, но все зависит от ситуации.
Очень важный механизм утилиты gmake – это автоматические пе-
ременные. Некоторые существующие автоматические переменные при-
ведены в Таблица 1.
Таблица 1
Список некоторых автоматических переменных
Имя автомати- Значение
ческой пере-
менной
$@ Имя цели обрабатываемого правила
$< Имя первой зависимости обрабатываемого правила
$^ Список всех зависимостей обрабатываемого правила
16
файлов, переменными), например, некоторые из них: patsubst, wildcard,
addprefix, addsuffix и notdir (dir).
Общий вид вызова функций осуществляется следующим образом:
$(имя_функции параметр1, параметр2 ...)
3.5 Заключение
18
ЗАКЛЮЧЕНИЕ
19
Задание
1 Изучить основные ключи компилятора g++. Для этого вызовите
команду:
%man g++;
2 Разработать ПО в соответствии с вариантом. Входные данные
для программы необходимо передавать через аргументы. В коде про-
граммы осуществить проверку входных данных на корректность. Все
команды введённые в процессе выполнения лабораторной работы необ-
ходимо документировать в письменном отчете.
Вариант 1
Разработать программу, которая определяет факториал числа.
Вариант 2
Вывести случайное число в диапазоне, минимальное и макси-
мальное число диапазона задается через аргументы.
Вариант 3
Разработать программу для нахождения наименьшего общего
кратного двух чисел.
Вариант 4
Разработать программу для нахождения наибольшего общего де-
лителя двух чисел.
Вариант 5
Разработать программу, которая выводит на экран ряд Фибоначчи
до числа N переданного через аргумент.
Вариант 6
Разработать программу, которая определяет количество чисел в
слове переданного через аргумент.
Вариант 7
Разработать программу, которая определяет количество гласных
символов в слове.
Вариант 8
Разработать программу, которая считает количество слов в пред-
ложении переданного через аргумент. При передаче предложения в ка-
честве аргумента, строку необходимо поместить в двойные кавычки.
20
3 Специальным образом допустите синтаксическую ошибку в
программе. Сообщение об ошибке запишите в файл [Link] в вашей
домашней директории.
4 Используя механизм предупреждения компилятора gcc, добей-
тесь вывода текстового сообщения (предупреждения) в рабочей про-
грамме (без использования ключа –Wall, а выбрав из списка всех воз-
можных ключей, тот, который описывает вашу ошибку). Полученное
сообщение добавьте в файл [Link].
5 Оптимизируйте вашу программу, уменьшив объем памяти, ко-
торый она занимает на жестком диске. Определите время, которое было
затрачено на компиляцию до оптимизации и после (использовать можно
любой уровень оптимизации). Проведите пять экспериментов, составьте
таблицу с полученными данными. Сделайте выводы.
6 Разработанную программу представить в виде простого класса
на языке С++. Осуществить разделенную трансляцию исходных файлов.
Изменить содержимое одного из файлов и снова осуществить компиля-
цию. Проанализировать результаты (зафиксировать даты изменения
файлов в файле [Link]) и сделать выводы.
7 Написать простейший make-файл, который будет автоматиче-
ски собирать написанную вами программу, при компиляции необходи-
мо использовать ключ для вывода сообщений об ошибке. Переименуйте
make-файл в любое понравившееся вам имя и снова соберите проект
(Первая версия make-файла).
8 Создайте новый make-файл с возможностью разделенной
трансляцией (Вторая версия make-файла).
8.1 После успешного компилирования (информацию, получен-
ную в процессе компиляции, добавьте в файл [Link]) удали-
те один из объектных файлов, заново запустите процесс
компиляции (результат также добавьте в файл [Link]). Про-
анализируете полученный результат. Сделайте выводы.
8.2 Запустите утилиту make еще раз, полученное сообщение
также добавьте в файл [Link]. Сделайте выводы.
8.3 Используя ложную цель, добавьте в make-файл возможность
автоматического удаления объектных файлов после компи-
ляции.
9 Максимально оптимизируйте вторую версию make-файла, до-
бавив переменные (автоматические переменные) и функции, изученные
в ходе лабораторной работы (Третья версия).
10 Создайте в директории с вашей программой каталог с назва-
нием «src» переместите в него два файла описывающие ваш класс (*.h,
21
*.cpp), файл [Link] должен остаться в текущей директории. Добейтесь
успешной компиляции, доработав make-файл (четвертая версия), ис-
пользуя знания, полученные в ходе лабораторной работы (Возможно,
вам пригодится ключ –I компилятора g++ и переменная VPATH).
В результате проделанной работы, в вашей домашней директории
должны содержаться:
исходные коды программы (3 файла или более);
корректный исполняемый файл, соответствующий вашему ва-
рианту;
файл [Link] – файл, содержащий сообщения об ошибках и
предупреждениях;
файл [Link] – содержащий некоторую выходную информацию,
полученную в процессе выполнения работы;
make-файлы (4 версии);
Необходимо написать отчет по лабораторной работе, продемон-
стрировать работу программы преподавателю, а также процесс компи-
лирования программы с использованием последней версии make-файла.
Ответить на контрольные вопросы.
22
Контрольные вопросы
1 Что вы понимаете под компиляцией? Как вы считаете, чем от-
личается компилятор от интерпретатора? Приведите примеры
компилируемых и интерпретируемых языков программирова-
ния.
2
3 Ниже приведены простейшие примеры make-файлов. Что будет
выведено на экране в результате выполнения make-файла?
Задание 1
var1 = one
var2 = $(var1) two
var1 = three
all:
@echo $(var2)
Задание 2
var1 := one
var2 := $(var1) two
var1 := three
all:
@echo $(var2)
Задание 3
var1 = one
var1 = $(var1) two
all:
@echo $(var1)
Задание 4
source_dirs := src lib
search_matches := $(addsuffix /*.cpp, $(source_dirs))
all:
@echo $(search_wildcards)
23
Список используемых источников
1. Альфред В. Ахо, Моника С. Лам, Рави Сети, Джеффри Д. Уль-
ман Компиляторы: принципы, технологии и инструментарий =
Compilers: Principles, Techniques, and Tools. - 2 изд.-
М.:Вильямс, 2008.
2. Основы языка программирования C++. Режим доступа:
[Link]
24
Приложение 1
(обязательное)
Исходный код демонстрационной программы
В заголовочном файле (summ.h) будет содержаться описание
класса.
#ifndef SUMM_H
#define SUMM_H
class CSumm {
public:
CSumm(int v1, int v2);
int calculate();
private:
int a;
int b;
};
#endif
#include "summ.h"
CSumm::CSumm(int v1, int v2) {
a = v1;
b = v2;
}
int CSumm::calculate() {
return a + b;
}
26
Приложение 2
(обязательное)
Основной список ключей компилятора g++
v – версия компилятора
o – путь к целевой программу
std – указывает стандарт языка программирования (например
c++98, по умолчанию gnu++98)
Wall – выводит любые предупреждения об ошибках.
g – получение отладочной информации
pipe – позволяет ускорить компиляцию, передача информации
между различными этапами осуществляется через каналы, без
использования промежуточных файлов.
27
Приложение 3
(обязательное)
Ключи утилиты GNU make
-b, -m. Эти опции игнорируются и служат для совместимости с
другими версиями make.
-C dir - Изменить каталог на dir перед чтением make_файлов или
выполнением чего-то ещё. Если указано несколько опций –C, то каждая
интерпретируется относительно предыдущей: -C / -C etc эквивалентно –
C /etc. Обычно, это используется с рекурсивными вызовами make.
-d - Печатать отладочную информацию в дополнении к нормаль-
ной. Отладочная информация показывает, какие файлы было решено
переделать, какие времена файлов сравнивались и с каким результатом,
какие файлы на самом деле должны быть переделаны, какие скрытые
правила подразумевались и какие были применены---всё интересное о
том, как make решал что делать.
-e - дать переменным из среды превосходство над переменными
из make-файлов.
-f file – Использовать file как makefile.
-i - Игнорировать все ошибки в командах, выполняемых для об-
новления файлов.
-I dir - В заданном каталоге dir ищутся включаемые make-файлы.
Если используется несколько опций –I для задания нескольких катало-
гов, то поиск осуществляется в порядке задания. В отличие аргументов
других флагов make, каталоги в –I флагах можно указывать сразу после
флага: -I dir разрешено, также как и –I dir. Этот синтаксис разрешён для
совместимости с флагом –I препроцессора C.
-j jobs - Указать число заданий (команд) выполняемых одновре-
менно. Если есть больше чем одна –j опция, то используется последняя.
Если –j опция задана без аргумента, то make не ограничивает число за-
даний, которые могут выполняться одновременно.
-k - Продолжать после ошибки настолько, насколько возможно.
Несмотря на то, что объект нельзя обновить, и то, что от этого зависит,
не может быть переделано, в тоже время есть другие зависимости этого
объекта, которые могут быть обработаны.
-l load - Указывает, что новые задания (команды) не должны
начинаться, если другие задания запущены и средняя загрузка, по край-
ней мере, load (дробное число). Без аргументов удаляется предыдущий
предел загрузки.
28
-n - Напечатать команду, которая должна выполняться, но не вы-
полнять её.
-o file - Не переделывать файл file, даже если он старее, чем его за-
висимости, и ничего не переделывать за счёт изменений в file. По суще-
ству, файл трактуется как очень старый и его правила игнорируются.
-p - Напечатать базу данных (правила и значения переменных) по-
лученную при чтении make-файлов; затем выполняться, как обычно или
как указано иначе. Это также печатает информацию о версии, даваемую
переключателем –v (смотри ниже). Чтобы напечатать базу данных без
попыток пересобрать любые файлы, используется make –p -f/dev/null.
-q – «Режим запроса». Никаких команд не запускается и ничего не
печатается; только возвращается код завершения, который равен нулю,
если заданный объект уже самый новый, иначе не ноль.
-r - Исключить использование встроенных скрытых правил. Также
очистить список суффиксов по умолчанию для правил суффиксов.
-s -Тихая работа; не печатать команды при их выполнении.
-S - Отменить действие опции -k . Этого никогда не требуется,
кроме как при рекурсиях make где –k может быть унаследована от верх-
него уровня make через MAKEFLAGS или если –k установлена в
MAKEFLAGS среды.
-t - Прикоснуться к файлам (пометить их как новые без действи-
тельного их изменения) вместо запуска команд к ним. Это используется,
чтобы симулировать, что команды выполнены, чтобы обмануть буду-
щие вызовы make.
-v - Напечатать версию программы make плюс copyright, список
авторов и уведомление об отказе от гарантий.
-w - Напечатать сообщение, содержащее рабочий каталог перед и
после других работ. Это может быть полезно для отслеживания ошибок
при сложных вложенных рекурсивных make командах.
-W file - Сделать вид, что объект file только что был модифициро-
ван. Когда еще используется –n флаг, то это покажет, чтобы случилось,
если бы этот файл был изменён. Без -n, это почти тоже самое, что как
запустить команду touch с данным файлом перед запуском make, кроме
того, что время модификации изменилось только в представлении make.
29
Учебное издание
30