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

в 18:03, , рубрики: GNU, IDE, linux, UNIX, Программирование

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

GCC

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

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

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

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

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

$ gcc -c example.c -o example.o

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

$ objdump -D example.o

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

$ gcc -c -S example.c -o example.s

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

$ gcc -c -g -Wa,-a,-ad example.c > example.lst

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

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

$ cpp example.c

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

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

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

$ gcc example.o -o example

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

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

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

$ gcc example.c -o example

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

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

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

$ gcc -I/usr/include/somelib.h example.c -o example

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

$ gcc -lncurses example.c -o example

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

$ export CFLAGS=-I/usr/include/somelib.h
$ export CLIBS=-lncurses
$ gcc $CFLAGS $CLIBS example.c -o example

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

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

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

$ gcc -v -c example.c -o example.o

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

$ gcc -### -c example.c -o example.o

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

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

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

$ gcc -Wall -pedantic -c example.c -o example.o

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

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

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

$ gcc -time -c example.c -o example.o

Оптимизация

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

gcc -O1
gcc -O2
gcc -O3

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

:!gcc % -o example

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

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

Inline-код

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

$ perl -e 'print "Hello world.n";'
$ perl <<<'print "Hello world.n";'
$ echo 'print "Hello world.n";' | perl

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

$ perl hello.pl

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

$ perl -c hello.pl

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

#!/usr/bin/env perl
print "Hello, world.n";

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

$ mv hello{.pl,}
$ chmod +x hello

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

$ ./hello

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

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

Продолжение следует...

Unix как IDE: Введение
Unix как IDE: Файлы
Unix как IDE: Работа с текстом
Unix как IDE: Компиляция

Автор: kamelopardus


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js