Меню Рубрики

Компиляция gcc в linux

Компиляция gcc в linux

Средствами, традиционно используемыми для создания программ для открытых операционных систем, являются инструменты разработчика GNU. Сделаем маленькую историческую справку. Проект GNU был основан в 1984 году Ричардом Столлманом. Его необходимость была вызвана тем, что в то время сотрудничество между программистами было затруднено, так как владельцы коммерческого программного обеспечения чинили многочисленные препятствия такому сотрудничеству. Целью проекта GNU было создание комплекта программного обеспечения под единой лицензией, которая не допускала бы возможности присваивания кем-то эксклюзивных прав на это ПО. Частью этого комплекта и является набор инструментов для разработчика, которым мы будем пользоваться, и который должен входить во все дистрибутивы Linux.

Одним из этих инструментов является компилятор GCC. Первоначально эта аббревиатура расшифровывалась, как GNU C Compiler. Сейчас она означает – GNU Compiler Collection.

Создадим первую программу с помощью GCC. По сложившейся традиции первая программа будет просто выводить в консоли приветствие «Hello world!» – «Здравствуй Мир!».

Файлы с исходными кодами программ, которые мы будем создавать, это обычные текстовые файлы, и создавать их можно с помощью любого текстового редактора (например GEdit KWrite, Kate, а также более традиционные для пользователей Linux – vi и emacs). Помимо текстовых редакторов, существуют специализированные среды разработки со своими встроенными редакторами. Одним из таких средств является KDevelop. Интересно, что в нём есть встроенный редактор и встроенная консоль, расположенная прямо под редактором. Так что можно прямо в одной программе, не переключаясь между окнами, и редактировать код и давать консольные команды.

Создайте отдельный каталог hello. Это будет каталог нашего первого проекта. В нём создайте текстовый файл hello.c со следующим текстом:

Затем в консоли зайдите в каталог проекта. Наберите команду

Теперь посмотрите внимательно, что произошло. В каталоге появился новый файл a.out. Это и есть исполняемый файл. Запустим его. Наберите в консоли:

Программа должна запуститься, то есть должен появиться текст:

Компилятор gcc по умолчанию присваивает всем созданным исполняемым файлам имя a.out. Если хотите назвать его по-другому, нужно к команде на компиляцию добавить флаг -o и имя, которым вы хотите его назвать. Давайте наберём такую команду:

gcc hello.c -o hello

Мы видим, что в каталоге появился исполняемый файл с названием hello. Запустим его.

Как видите, получился точно такой же исполняемый файл, только с удобным для нас названием.

Флаг -o является лишь одним из многочисленных флагов компилятора gcc. Некоторые другие флаги мы рассмотрим позднее. Чтобы просмотреть все возможные флаги, можно воспользоваться справочной системой man. Наберите в командной строке:

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

Вы, конечно, обратили внимание, что, когда мы запускаем программу из нашего каталога разработки, мы перед названием файла набираем точку и слэш. Зачем же мы это делаем?

Дело в том, что, если мы наберём только название исполняемого файла, операционная система будет искать его в каталогах /usr/bin и /usr/local/bin, и, естественно, не найдёт. Каталоги /usr/bin и /usr/local/bin – системные каталоги размещения исполняемых программ. Первый из них предназначен для размещения стабильных версий программ, как правило,входящих в дистрибутив Linux. Второй – для программ, устанавливаемых самим пользователем (за стабильность которых никто не ручается). Такая система нужна,чтобы отделить их друг от друга. По умолчанию при сборке программы устанавливаются в каталог /usr/local/bin. Крайне нежелательно помещать что-либо лишнее в /usr/bin или удалять что-то оттуда вручную, потому что это может привести к краху системы. Там должны размещаться программы, за стабильность которых отвечают разработчики дистрибутива.

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

Или другой вариант: прописать путь относительно текущего каталога, в котором вы в данной момент находитесь в консоли. При этом одна точка означает текущий каталог, две точки – родительский. Например, команда ./hello запускает программу hello, находящуюся в текущем каталоге, команда ../hello – программу hello, находящуюся в родительском каталоге, команда ./projects/hello/hello – программу во вложенных каталогах, находящихся внутри текущего.

Есть возможность добавлять в список системных путей к программам дополнительные каталоги. Для этого надо добавить новый путь в системную переменную PATH. Но давайте пока не будем отвлекаться от главной темы. Переменные окружения – это отдельный разговор.

Теперь рассмотрим, что же делает программа gcc. Её работа включает три этапа: обработка препроцессором, компиляция и компоновка (или линковка).

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

Вторая стадия – компиляция. Она заключается в превращении текста программы на языке C/C++ в набор машинных команд. Результат сохраняется в объектном файле. Разумеется, на машинах с разной архитектурой процессора двоичные файлы получаются в разных форматах, и на одной машине невозможно запустить бинарник, собранный на другой машине (разве только, если у них одинаковая архитектура процессора и одинаковые операционные системы). Вот почему программы для UNIX-подобных систем распространяются в виде исходных кодов: они должны быть доступны всем пользователям, независимо от того, у кого какой процессор и какая операционная система.

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

Теперь посмотрим на практике, как всё это выглядит. Напишем другую программу. Это будет примитивнейший калькулятор, способный складывать, вычитать, умножать и делить. При запуске он будет запрашивать по очереди два числа, над которыми следует произвести действие, а затем потребует ввести знак арифметического действия. Это могут быть четыре знака: «+», «–», «*», «/». После этого программа выводит результат и останавливается (возвращает нас в операционную систему, а точнее – в командный интерпретатор, из которого мы программу и вызывали).

Источник

Unix как IDE: Компиляция

Под Unix существует множество компиляторов и интерпретаторов, но здесь мы будем обсуждать лишь gcc как средство компиляции C-кода, и коротко коснемся использования perl в качестве примера интерпретатора.

GCC — это набор компиляторов, обладающий очень почтенным возрастом и распространяемый под лицензией GPL. Он известен как инструмент работы с программами на C и C++. Свободная лицензия и повсеместная распространенность на Unix-подобных системах стали залогом его неизменной популярности, хотя есть и более современные альтернативы, использующие инфраструктуру LLVM, такие как Clang.

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

Здесь я не буду обсуждать использование make-файлов, хотя они наверняка понадобятся для любого проекта сложнее, чем в один файл. Make-файлов я коснусь в следующей статье о средствах автоматизации сборки.

Компиляция и сборка объектного кода

Объектный код компилируется вот такой командой:

Если код верен, будет создан нелинкованный двоичный объектный файл под именем example.o в текущей папке, или выведены сообщения о проблемах. Заглянуть внутрь полученного файла и увидеть его содержимое на языке ассемблера можно так:

Как вариант, можно попросить gcc сразу показать итоговый ассемблерный код при помощи параметра -S:

Вывод ассемблерного кода может быть полезно совместить с выводом самого исходника, чего можно добиться, набрав:

Препроцессор

Препроцессор C (cpp) обычно используется для подключения заголовочных файлов и определения макросов. Это стандартная часть процесса компиляции gcc, но можно просмотреть генерируемый им код, вызвав cpp напрямую:

Исходный код будет выведен в конечном виде, готовым к компиляции, с замененными макросами и подстановкой включаемых внешних файлов.

Связывание объектов

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

В этом примере gcc просто вызывает ld, линковщик GNU. Команда создаст исполняемый файл по имени example .

Компиляция, сборка и связывание

Все вышеперечисленное может быть выполнено в один шаг при помощи команды:

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

Включение внешних файлов и связывание

Файлы C и заголовочные файлы могу быть явно включены в компиляцию при помощи параметра -l:

Аналогично, если код нужно динамически связать с уже скомпилированной системной библиотекой, доступной в одной из системных папок ( /lib или /usr/lib ), например, ncurses, этого можно добиться использованием ключа -l:

Если в процессе компиляции внешних связей много, имеет смысл внести их в переменные окружения:

Кстати, Makefile затем и создан, чтобы избавить нас от беспокойства о таких мелочах.

План компиляции

Чтобы посмотреть подробности внутренней кухни gcc, можно добавить ключ -v, и план компиляции будет выведен в стандартный поток вывода ошибок:

Если нет нужды генерировать объектные или исполняемые файлы, то для аккуратности можно использовать -###:

Очень полезно посмотреть, какие действия gcc предпринимает без нашего ведома, кроме того, так мы можем выявить нежелательные шаги при компиляции.

Расширенный вывод сообщений об ошибках

Существует возможность добавить ключи -Wall и/или -pedantic, чтобы gcc предупреждал нас о случаях, которые не обязательно являются ошибками, но могут ими быть:

Удобно включать такие опции в Makefile или в определении makeprg для Vim, так как они отлично сочетаются с окном quickfix, и помогают писать читабельный, совместимый и безошибочный код.

Профилирование процесса компиляции

Вы можете включить опцию -time, чтобы gcc отображал в тексте вывода время выполения каждого из шагов:

Оптимизация

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

Подобно любой команде Bash, все это можно вызывать прямо из Vim:

Интерпретаторы

Подход к интерпретируемому коду в Unix-системах иной. В своих примерах я буду использовать Perl, но те же принципы применимы для кода, например, на Python или Ruby.

Inline-код

Можно строку Perl-кода прямо на исполнение интерпретатору любым из перечисленных ниже способов Первый, наверное, самый простой и общеупотребительный способ работы с Perl; второй использует синтаксис heredoc, а третий — это классический конвейер Unix.

Конечно, в будничной жизни мы храним код в файле, который можно вызвать прямо вот так:

Можно проверить синтаксис кода без его выполнения с помощью ключа -c:

Порой хочется использовать скрипт подобно любому исполняемому бинарнику, не беспокоясь о том, что он из себя представляет. Для этого в скрипт добавляют первой строкой так называемый «shebang«, указывающий путь к интерпретатору, которому следует передать на исполнение данный файл.

Скрипту после этого можно ставить атрибут исполняемого файла вызовом chmod. Также хорошим тоном считается переименовать файл, убрав расширения, поскольку он теперь считается почти настоящим исполняемым файлом:

Затем файл можно вызывать напрямую, без указания интерпретатора:

Вся эта кухня так здорово работает, что многие стандартные утилиты Linux-систем, такие как adduser, в действительности являются скриптами на Perl или Python.

В следующей публикации я расскажу о методах работы с make для сборки проектов, сравнимых с привычными IDE.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

  • Компиляторы c для linux
  • Компилятор c для linux mint
  • Компас на linux mint
  • Коммерческие программы для linux
  • Команды установки программ в linux через терминал