Дата и время публикации:
Пример использования
1. Что такое GNU Make
Утилита , которая управляет в полуавтоматическом режиме генерацией программ из исходного кода в исполняемые файлы (executables) впрочем, как и данные к ним из не содержащих исходный код файлов(non-source files).
Информацию о том, как построить программу, утилита Make берет из файла, называемого makefile, в котором так же перечисляются каждый из не содержащих исходный код файлов и как их можно получить из других файлов. Поэтому нужно написать не только исходный код программы, но и написать makefile, чтобы осуществить её сборку и инсталляцию всех необходимых данных для её нормальной работы.
GNU Make обеспечивает:
- сборку и инсталляцию пакетов программ без знания деталей о том, как что собирать и куда ставить, т.к. все указанные действия прописаны в файле makefile;
- автоматический вывод файлов необходимых для обновления на основе сделанных изменений в исходных файлах, а при необходимости определяет правильный порядок обновления, в случае зависимости от одного и более файлов, не содержащие исходный код; в результате чего может быть пересобрана вся программ целиком;
- отсутствие каких-либо ограничений по применяемым языкам, что выражается в вычисление необходимых команд, используемых командным интерпретатором (shell), для каждого не содержащий исходный код файла; к примеру, эти команды могут выполняться компилятором для создания объектных файлов(object file), сборки исполняемых файлов (executable file), обновление библиотеки или TeX, или формата документации Makeinfo;
- построение пакетов в части выполнения процедуры инсталляции и де-инсталляции с генерацией таблиц тэгов для или все что заблагорассудится в пределах возможностей Make, о которых речь пойдет ниже.
2. Что такое правила и цели в GNU Make
Файл, который использует GNU Make, состоит из правил (rules), указывающих как выполнять последовательность команд (commands) для того, чтобы построить целевой (target) файл из исходного с учетом списка зависимостей (dependencies), который должен содержать все файлы исходного кода или другие цели (targets).
Таким образом, упрощенный порядок написания правил (rules) имеет вид:
target: dependencies ... commands
При выполнение Make необходимо указать все соответствующие цели (targets) в зависимостях (dependencies) для проведения процедуры обновления. В противном случае, произойдет обновление первой найденной цели в makefile или вся процедура завершится с ошибкой, потому что не были учтены другие цели, используемые ими. За исключением, неявных правил (implicit rules), которые обычно являются встроенными.
Ввиду того, что утилита Make обновляет целевые файлы согласно цели (targets) по дате. Соответственно, изменение ПОСЛЕДНЕЙ послужит фактически толчком к выполнению их обновления. В то же время, если целевой файл является новым, а все его зависимости обновлены по дате — в этом случае её генерация будет пропущена. Поэтому другие целевые файлы, используемые вновь водимой цели (или уже существующей), необходимо обновить до того, как они будут использованы.
3. Соглашения по написанию файлов Makefile
Для написания makefile существуют некоторые соглашения, которые описаны в п.7 стандартов написания исходных кодов(GNU Coding Standards), которые с легкостью соблюдаются c Automake, как это описывалось в статьях:
При этом, несмотря на растущую популярность Automake и легкость использования, всегда может возникнуть потребность самостоятельного написания makefile в соответствие с указанными соглашениями из стандартов написания исходных кодов GNU.
Что и сделаем, как показано на примере написания makefile.real из поддиректории examples/libgnucalgthms проекта gasrunparts-0.9.
4. Пример написания Makefile
4.1 Поставим задачу, состоящую в том, что:
1) Проект gasrunparts версии 0.9 и выше должен поддерживать сборку некоторых исполняемых файлов, подобных sllist_mergesort, из исходных файлов, хранящихся глобально в /usr/local/share/gasrunparts/examples или локально в examples/libgnucalgthms дерева проекта и у которых сборка осуществляется вне зоны действия Automake.
2) Сборка программы sllist_mergesort должна производится в поддиректории build/gnucalgthms проекта, в которой будут сохраняться все «промежуточные» файлы, удовлетворяющие зависимости сборки, такие как объектные файлы и статические библиотеки, а так же сам целевой файл sllist_mergesort (далее по тексту — программа).
3) Сборка ПРОГРАММЫ и её зависимостей должна выполняться утилитой GNU Make в соответствие с требованиями стандартов написания исходных кодов GNU.
4.2 Решение задачи:
Для отладки процедуры сборки программы будем использовать цель sllist_mergesort корневого Makefile.am, рецепт создания которой приведен в статье "Как организовать дерево проекта в Automake с рекурсией и без неё" и обеспечивающей вызов make sllist_mergesort отдельно от Automake и его основных целей (таких как all, install и dist), как показано в листинге 4.2.1.
Листинг 5.4. Фрагмент корневого Makefile.am поддиректории libgnucalgthms из пакета gasrunparts-0.9
sllist_mergesort: cd examples/libgnucalgthms && $(MAKE) -f Makefile.real $(AM_MAKEFLAGS) sllist_mergesort clean-local: cd examples/libgnucalgthms && $(MAKE) -f Makefile.real $(AM_MAKEFLAGS) clean
Для очистки директории examples/libgnucalgthms будем использовать тоже самое проксирование make, как и для цели single_llist_mergesort, но в отличии от него — в связке с automake. Для этого нам нужно объявить в корневом Makefile.am цель ‘clean-local’ и ‘cd subdir && $(MAKE) $(AM_MAKEFLAGS) target’ , так как поддиректория examples/libgnucalgthms находится в глубине проекта.
Соответственно, когда будет вызван ‘make sllist_mergesort’, получим нечто подобное, показанное в дампе 4.2.1.1 .
Дамп 4.2.1.1 . Результат выполнения ‘make sllist_mergesort’
user@home: ~: make sllist_mergesort cd examples/libgnucalgthms && make -f Makefile.real sllist_mergesort make[1]: Entering directory '/home/arzhavskov/Projects/Gasrunparts/gasrunparts-0.9/examples/libgnucalgthms' make[1]: 'sllist_mergesort' is up to date. make[1]: Leaving directory '/home/arzhavskov/Projects/Gasrunparts/gasrunparts-0.9/examples/libgnucalgthms'
И после make clean — что-то подобное, как показано в дампе 4.2.1.2 .
Дампе 4.2.1.2. Фрагмент вывода на STDOUT отладочных сообщений `make'
user@home: ~: make clean ... Making clean in . make[1]: Entering directory '/home/arzhavskov/Projects/Gasrunparts/gasrunparts-0.9' cd examples/libgnucalgthms && make -f Makefile.real clean make[2]: Entering directory '/home/arzhavskov/Projects/Gasrunparts/gasrunparts-0.9/examples/libgnucalgthms' real[1] Clean directory make[2]: Leaving directory '/home/arzhavskov/Projects/Gasrunparts/gasrunparts-0.9/examples/libgnucalgthms' make[1]: Leaving directory '/home/arzhavskov/Projects/Gasrunparts/gasrunparts-0.9' ...
Соответственно, в первом случае (дамп 4.2.1.1) показывается отдельно выполнение цели, которая не связанна с рекурсивной сборкой проекта, а во втором случае (дамп 4.2.1.2) — в месте с ней.
4.2.2 Теперь, перейдем к содержанию реального файла makefile.real утилиты make в соответствие с требованиями стандартов написания исходных кодов GNU.
4.2.3 В первой строчке (или иной другой) нужно прописать переменную SHELL во избежание возникновения неприятностей, связанных с тем, что утилита make может её наследовать из переменной окружения. Хотя, GNU make в этом не был замечен, стандарт для написания makefile предписывает ЭТО ДЕЛАТЬ, как показано в листинге 4.2.3.1.
Листинг 4.2.3.1 фрагмент файла Makefile.real
1 SHELL = /bin/sh ...
4.2.4 Во избежание неразберихи и коллапса, связанных с использованием суффиксов различных файлов в соответствующем makefile, необходимо прописать только те, которые непосредственно используются, как показано в листинге 4.2.3.2.
Листинг 4.2.3.2. Фрагмент файла Makefile.real
... 3 .SUFFIXES: 4 .SUFFIXES: .c .o ... 16 .c.o: 17 $ (CC) -c $(CPPFLAGS) $(CPPFLAGS) $@ $<
1 SHELL = /bin/sh 2 3 srcdir= $(HOME)/project/src 4 buildir=build 5 6 VPATH= $(srcdir):$(builddir) 7 8 .SUFFIXES: 9 .SUFFIXES: .c .o 10 11 .c .o: 12 $(CC) -c $(CPPFLAGS) $(CPPFLAGS) $@ $< 13 14 program: program.o build.o 15 $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) 16Соответственно, как видно из строки 3 и 4 листинга 4.2.3.2.1, в директории `$(HOME)/projects' существует некая структура файлов исходного кода, которая показана в дампе 4.2.3.2.2. Дамп 4.2.3.2.2. Структура файлов исходного кода в `$(HOME)/projects'
$(HOME)/project: | +--> srcs | | | \-----> program.c: | 1 | 2 extern int print_message_on_stdout(const char *message); | 3 | 4 int main(void) | 5 { return print_message_on_stdout("Program running...");} | 6 /*eof*/ | 7 \--> build | +--> build.c: | 1 | 2 int print_message_on_stdout(const char *message) | 3 { return ( printf(message) > 0 ? 0 : 1 ); } | 4 /*eof*/ | 5 | \--> MakefileКак показано в дампе 4.2.3.2.3, для сборки исполняемого файла `program' достаточно выполнить `make -C ~/project/build program'. Дамп 4.2.3.2.3 .
user@home ~$ make -C ~/project/build make: Entering directory '/home/user/tmp/project/build' cc -c -o build.o build.c cc -c -o program.o /home/user/tmp/project/src/program.c cc build.o program.o -o program make: Leaving directory '/home/user/tmp/project/build'В результате, как показано в дампе 4.2.3.2.4, в директории `$(HOME)/project/build' будет собран целевой(исполняемый) файл `program' и необходимые ему объектные файлы `program.o' и `build.o'. При этом файл исходного кода `program.c' остается в своей директории `src'. Дамп 4.2.3.2.4.
user@home ~$ ls -lR ~/project/build home/user/tmp/project: total 8 drwxr-xr-x 2 user user 4096 Sep 7 10:45 build drwxr-xr-x 2 user user 4096 Sep 7 10:38 src /home/user/tmp/project/build: total 28 -rw-r--r-- 1 user user 291 Sep 7 10:32 Makefile -rw-r--r-- 1 user user 119 Sep 7 10:40 build.c -rw-r--r-- 1 user user 1384 Sep 7 10:45 build.o -rwxr-xr-x 1 user user 6864 Sep 7 10:45 program -rw-r--r-- 1 user user 1504 Sep 7 10:45 program.o /home/user/tmp/project/src: total 8 -rw-r--r-- 1 user user 146 Sep 7 10:38 program.c2. Суффиксальные правила (Suffix rules) поддерживаются не только из-за чистоты нравов, будучи более обобщенными и понятными, но и из-за сохранения обратной совместимости со старой формой записи makefile'ов и позволяя объявлять неявные правила (по сути — неявные цели), используемые для удовлетворения зависимостей при сборке целевого файла в соответствие с заданной целью.
В строках 3 и 4 чистим лист с суффиксами, а затем в нем же перечисляем все суффиксы, которые могут фигурировать в качестве неявных правил (или вернее целей) в makefile. В нашем случае — это суффиксы ‘.c’ и ‘.o’.
Для чего, в строке 16 и 17 определяем неявные правила в нотации сдвоенных суффиксов ‘.c.o’, где первым идет суффикс исходного файла ‘.c’, а вторым — целевого, объектного файла ‘.o’. Как не трудно догадаться в качестве суффиксов выступают расширения файлов.
При этом для удовлетворение условий задачи, в ‘VPATH’ прописываем локальный, относительный путь и абсолютный путь для поиска исходный файлов, замещаемые автоматической переменной ‘$<’, а переменная ‘$@’ будет автоматически подставлять имя объектного файла с расширение ‘.o’.
Правила записи относительного и абсолютного пути в ‘VPATH’ приведен в листинге 4.2.3.3 в прочем, как и флагов $(CPPFLAGS), $(CPPFLAGS) и $(LDFLAGS) — правила написания и принципы их использования.
Листинг 4.2.3.3. Фрагмент файла Makefile.real
... 6 ## дополнительные флаги для передачи компилятору С/С++ 7 CFLAGS = -g -march=native -mtune=native -O3 -Wall -pipe \ 8 -fomit-frame-pointer -fno-align-jumps \ 9 -fno-align-functions -fno-align-labels \ 10 -fno-align-loops -finline-functions 11 ## дополнительные флаги для передачи препроцессору С и используемые программами 12 CPPFLAGS = -I. -I/usr/local/include 13 ## дополнительные флаги, используемые в процессе линковки (подобных опции -L) 14 LDFLAGS = 15 ## и библиотеками (с такими как опция -l), которые добавляются в LDLIBS взамен LDFLAGS 16 LDLIBS = -lm libgnucalgthms.a 17 VPATH = examples/gnucalgthms:/usr/local/share/gasrunparts/examples/gnucalgthms ... 26 ## Эта цель будет найдена первой 27 all: sllist_mergesort 28 29 ## Сборка статической библиотеки 30 libgnucalgthms.a: gnucalgthms.o 31 $(AR) rcs $@ $? 32 33 ## Сборка целевого файла `sllist_mergesort' 34 sllist_mergesort: single_llist_mergesort.o libgnucalgthms.a 35 @echo real[1] Build program sllist_mergesort; \ 36 $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) $(LDLIBS) ...
user@home:~/projects/$ tar xzvf gasrunparts-0.9.tar.gz && cd gasrunparts-0.9 user@home:~/projects/gasrunparts-0.9/$ ./configure && make clean all user@home:~/projects/gasrunparts-0.9/$ make help Help on some targets are sllist_mergesort ____ Builds a program that execute sorting of singly linked list ____ by method Merge Sort makefile-update ____ copy current makefile.real from directory examples/libgnucalgthms ____ into directory build/gnucalgthms user@home:~/projects/gasrunparts-0.9/$ make sllist_mergesort cd examples/libgnucalgthms && make -f Makefile.real sllist_mergesort make[1]: Entering directory '/home/arzhavskov/Projects/Gasrunparts/gasrunparts-0.9/examples/libgnucalgthms' cc -g -march=native -mtune=native -O3 -Wall -pipe -fomit-frame-pointer -fno-align-jumps -fno-align-functions -fno-align-labels -fno-align-loops -finline-functions -I. -I/usr/local/share/gasrunparts/examples/gnucalgthms -c -o single_llist_mergesort.o single_llist_mergesort.c cc -g -march=native -mtune=native -O3 -Wall -pipe -fomit-frame-pointer -fno-align-jumps -fno-align-functions -fno-align-labels -fno-align-loops -finline-functions -I. -I/usr/local/share/gasrunparts/examples/gnucalgthms -c -o gnucalgthms.o gnucalgthms.c ar rcs libgnucalgthms.a gnucalgthms.o real[1] Build program sllist_mergesort make[1]: Leaving directory '/home/arzhavskov/Projects/Gasrunparts/gasrunparts-0.9/examples/libgnucalgthms'3. Для проверки сборки цели `sllist_mergesort` с абсолютном путем `/usr/local/share/gasrunparts/examples/gnucalgthms` в ‘VPATH’, выполнить действия прописанные в дампе 4.2.3.3.2 в дереве gasrunparts-0.9 после выполнения команды ‘make install’ . Дамп 4.2.3.3.2
user@home:~/projects/gasrunparts-0.9/$ sudo make install user@home:~/projects/gasrunparts-0.9/$ make -C build/gnucalgthms -f Makefile.real all make: Entering directory '/home/arzhavskov/Projects/Gasrunparts/gasrunparts-0.9/build/gnucalgthms' cc -g -march=native -mtune=native -O3 -Wall -pipe -fomit-frame-pointer -fno-align-jumps -fno-align-functions -fno-align-labels -fno-align-loops -finline-functions -I. -I/usr/local/share/gasrunparts/examples/gnucalgthms -c -o single_llist_mergesort.o /usr/local/share/gasrunparts/examples/gnucalgthms/single_llist_mergesort.c cc -g -march=native -mtune=native -O3 -Wall -pipe -fomit-frame-pointer -fno-align-jumps -fno-align-functions -fno-align-labels -fno-align-loops -finline-functions -I. -I/usr/local/share/gasrunparts/examples/gnucalgthms -c -o gnucalgthms.o /usr/local/share/gasrunparts/examples/gnucalgthms/gnucalgthms.c ar rcs libgnucalgthms.a gnucalgthms.o real[1] Build program sllist_mergesort make: Leaving directory '/home/arzhavskov/Projects/Gasrunparts/gasrunparts-0.9/build/gnucalgthms'4. В результате, в поддиректории `/usr/local/share/gasrunparts/examples/gnucalgthms` должны наблюдаться файлы, приведенные в дампе 4.2.3.3.3 . Дамп 4.2.3.3.3
user@home:~/projects/gasrunparts-0.9/$ ls -l build/gnucalgthms/ total 44 -rw-r--r-- 1 user user 1500 Sep 8 15:57 Makefile.real -rw-r--r-- 1 user user 5288 Sep 8 15:59 gnucalgthms.o -rw-r--r-- 1 user user 5420 Sep 8 15:59 libgnucalgthms.a -rw-r--r-- 1 user user 7472 Sep 8 15:59 single_llist_mergesort.o -rwxr-xr-x 1 user user 10448 Sep 8 15:59 sllist_mergesort
В строке 17 после знака равенства (=) указываем локальную директорию examples/gnucalgthms, а затем через знак двоеточия (:) глобальную директорию /usr/local/share/gasrunparts/examples/gnucalgthms.
В остальных строках c 26-й по 36-ю занимаемся непосредственно удовлетворением зависимостей для сборки указанных целей all, libgnucalgthms.a и sllist_mergesort.
5. Библиография
1) GNU Make
2) GNU Coding Standards. 7.2 Makefile Conventions
4) How to see which flags -march=native will activate?
5) Autotools Mythbuster. 6. Autoconf Building Blocks: Macros
6) Automake:Third-Party Makefiles
7) GNU Make: Old-Fashioned Suffix Rules
8) Mad-Scientist.Net: how not to use vpath
10) GNU Make: Implicit Variables
11) GNU Make: Automatic-Variables
12) GNU Make: General-Search.html
13) How to set environment variable in makefile
14) how to use ldflags in makefile
15) GNU Compiler Collections: x86 Options