C2017/Системы автоматизации сборки

Материал из iRunner Wiki

GNU Make

До создания make системы сборки ПО Unix обычно состояли из shell-скриптов сборки, сопровождавших исходный код программ.

make была создана Стюартом Фельдманом (Stuart Feldman) в 1976 году в Bell Labs.

В настоящее время существует множество утилит для отслеживания зависимостей, но make — одна из самых широко распространённых, в первую очередь благодаря тому, что она включена в UNIX-системы, начиная с давних времён.

Makefile

Программа make выполняет команды согласно правилам, указанным в специальном файле. Этот файл называется make-файл (makefile, мейкфайл). Как правило, make-файл описывает, каким образом нужно компилировать и компоновать программу.

Файл ищется в текущем каталоге. Если ключ -f не указан, используется имя по умолчанию для make-файла — Makefile (однако в разных реализациях make кроме этого могут проверяться и другие файлы, например GNUmakefile).

make [ -f make-файл ] [ цель ] ...

Язык make-файла декларативный. Описываются необходимые конечные условия, но порядок выполнения действий для их достижения не важен, в отличие от императивного программирования.

Правила

make-файл состоит из правил и переменных. Правила имеют следующий синтаксис:

цель1 цель2 ...: реквизит1 реквизит2 ...
    	команда1
    	команда2
    	...

Правило представляет собой набор команд, выполнение которых приведёт к сборке файлов-целей из файлов-реквизитов. Обычно у правила цель одна.

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

Обычно цель представляет собой имя файла, который генерируется в результате работы указанных команд. Целью также может служить название некоторого действия, которое будет выполнено в результате выполнения команд (например, цель clean в make-файлах для компиляции программ обычно удаляет все файлы, созданные в процессе компиляции).

Строки, в которых записаны команды, должны начинаться с символа табуляции. Иногда эти строки называют рецептом приготовления цели.

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

Переменные (макросы)

Синтаксис для определения переменных:

переменная = значение

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

Нужно отметить, что вычисление значений переменных происходит только в момент использования (используется так называемое ленивое вычисление). Чтобы использовать неленивое вычисление, нужно ставить := вместо = в присваивании.

Пример

Составим мейкфайл для сборки программы из нескольких C-файлов.

TARGET = program
CFLAGS = -std=c11 -MP -MMD
SRCS = main.c foo.c bar.c
 
all: $(TARGET)
 
$(TARGET): $(SRCS:%.c=%.o)
	$(CC) -o $@ $(SRCS:%.c=%.o)
 
.PHONY: clean all
 
clean:
	rm -f *.o program *.d
 
-include $(SRCS:%.c=%.d)

Проблемы

Одной из проблем автоматизации сборки является адаптация процесса сборки к данной платформе. Например, компилятор, используемый на одной платформе, может не принимать те же параметры, что используются на другой платформе. Программа make не умеет справляться с этим. Проблема обычно обрабатывается путем создания платформозависимых инструкций сборки, которые, в свою очередь, обрабатываются Make. Общими инструментами для этого процесса являются Autoconf и CMake.

Отмечается низкая производительность make на больших проектах.

Язык написания мейкфайлов критикуется за сложность и невыразительность. Хотя бы то, что обязательно надо использовать табуляцию для отступа и нельзя использовать пробелы (стало можно в версии 3.82, добавили настройку .RECIPEPREFIX). Организация языка такова, что его нельзя распарсить без исполнения. Подстановки переменных могут произойти где угодно. Пробелы где-то не значат ничего, а где-то влияют на результат.

Ссылки

CMake

CMake (от англ. cross platform make) — это кроссплатформенная система автоматизации сборки программного обеспечения из исходного кода. Разрабатывается с 2000 г. CMake не занимается непосредственно сборкой, a лишь генерирует файлы управления сборкой из файлов CMakeLists.txt:

  • Makefile в системах UNIX для сборки с помощью make;
  • файлы projects/solutions (.vcxproj/.vcproj/.sln) в Windows для сборки с помощью Visual C++;
  • проекты XCode в Mac OS X.

Файлы CMakeLists.txt содержат команды на специальном языке CMake.

Сборка обычно включает три этапа:

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

Во многих системах сборки во время создания проекта используются переменные среды окружения командной оболочки. Трудность этого этого подхода в том, что для того, чтобы сборка проекта работала, нужно каждый раз, когда выполняется сборка, устанавливать значения всех этих внешних переменных. Чтобы решить эту проблему, в CMake есть кеш-файл, в котором в одном месте запомнены все переменные, необходимые для сборки. Они являются переменными CMake, а не переменными командной оболочки или среды окружения. Когда CMake запускается первый раз для конкретного дерева сборки, он создает файл CMakeCache.txt, в котором постоянно хранятся все переменные, необходимые для этой сборки. Поскольку этот файл является частью дерева сборки, переменные всегда будут доступны для CMake при каждом его запуске.

Некоторые IDE напрямую работают с файлами CMakeLists, не требуя отдельного запуска CMake для генерации (например CLion и Visual Studio 2017). Это повышает удобство разработки.

Ссылки

Пример

Организуем сборку простейшей программы типа "hello, world". Содержимое CMakeLists.txt:

add_executable(a.out main.c)

Организуем сборку рассмотренного ранее примера библиотеки и программы с помощью CMake. Содержимое CMakeLists.txt:

add_library(foobar STATIC foo.c bar.c)
 
add_executable(a.out main.c)
target_link_libraries(a.out foobar)

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

$ mkdir build
$ cd build
$ cmake ~/path/to/source
-- Configuring done
-- Generating done
-- Build files have been written to: ~/path/to/source
$ make
[ 20%] Building C object CMakeFiles/foobar.dir/foo.c.o
[ 40%] Building C object CMakeFiles/foobar.dir/bar.c.o
[ 60%] Linking C static library libfoobar.a
[ 60%] Built target foobar
[ 80%] Building C object CMakeFiles/a.out.dir/main.c.o
[100%] Linking C executable a.out
[100%] Built target a.out

Поиск внешних библиотек

CMake обладает достаточно развитыми средствами поиска установленых библиотек, правда они не встроеные, а реализованы в виде отдельных модулей. В стандартной поставке довольно много модулей. Они позволяют системе автоматически определить наличие необходимых для линковки проекта библиотек.

find_package(LibXml2 REQUIRED)
if(NOT LIBXML2_FOUND)
	message(SEND_ERROR "Failed to find LibXml2")
	return()
else()
	include_directories(${LIBXML2_INCLUDE_DIR})
endif()

GNU Autotools

Autotools, или система сборки GNU, — это набор программных средств, предназначенных для поддержки переносимости исходного кода программ между UNIX-подобными системами.

Перенос кода с одной системы на другую может оказаться непростой задачей. Различные реализации компилятора языка C могут существенно отличаться: некоторые функции языка могут отсутствовать, иметь другое имя или находиться в разных библиотеках. Программист может решить эту задачу, используя макросы и директивы препроцессора, например #if, #ifdef и прочие. Но в таком случае пользователь, компилирующий программу на своей системе должен будет определить все эти макросы, что не так просто, поскольку существует множество разных дистрибутивов и вариаций систем. Autotools вызываются последовательностью команд

$ ./configure
$ make
$ make install

и решают эти проблемы автоматически.

В средства сборки входят Autoconf, Automake и Libtool. Другие средства, используемые с GNU Autotools: make, gettext, pkg-config и, конечно, gcc и binutils.

GNU Autoconf

Autoconf читает файл configure.ac (или устаревший configure.in) и генерирует скрипт для настройки под названием configure. Для обработки файлов autoconf использует GNU реализацию языка макрокоманд m4.

Сгенерированный скрипт настройки запускается пользователем. Он читает файлы с расширением ".in", например Makefile.in, обрабатывает их (выясняя все особенности системы) и получает конечный результат — Makefile.

Autoconf использует некоторые вспомогательные программы, написанные для упрощения работы. Например, Autoheader работает с заголовочными файлами, autoscan исследует код на наличие типичных проблем переносимости и создаёт изначальный файл configure.ac.

GNU Automake

Automake читает файлы Makefile.am и создаёт переносимый Makefile, то есть Makefile.in, который затем после обработки скриптом конфигурации становится Makefile и используется утилитой make.

GNU Libtool

Libtool управляет созданием статических и динамических библиотек на разных UNIX-подобных операционных системах.

Недостатки

Эту систему многие называют Autohell.

Escape from GNU Autohell!