× К оглавлению На главную Об авторе

Дата и время публикации:

Пример использования


1. Что такое GNU Make

Утилита , которая управляет в полуавтоматическом режиме генерацией программ из исходного кода в исполняемые файлы (executables) впрочем, как и данные к ним из не содержащих исходный код файлов(non-source files).

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

GNU 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. Не полагайтесь на то, что символ точки (.) является частью пути исполняемой команды. Поэтому позаботитесь о том, чтобы make использовал `./' или `$(srcdir)/', потому что в случае, если они отсутствуют, им используется текущая директория для поиска. Отличие между директорией `./' , где происходит сборка (build directory), и директорией `$(srcdir)/', где хранятся исходники (source directory), состоит в том, что последняя может быть задана с помощью опцией `--srcdir' скрипта `./configure' (Automake). В GNU make, можно задать непосредственно директорию откуда брать исходные файлы в правилах сборки, через переменную ‘VPATH’, в которой перечисляются такие директории. Рассмотрим вне контекста задачи пример использования переменной ‘VPATH’ на примере Makefile некоего проекта, приведенного в листинге 4.2.3.2.1 . Листинг 4.2.3.2.1 . Пример использования переменной ‘VPATH’ в Makefile
 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.c
2. Суффиксальные правила (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)
...
Примечания 1. Дополнительные флаги `-march=native -mtune=native -O3 ... ' используются для повышения производительности путем определения оптимального набора флагов для инструкций в соответствие с архитектурой процессора на машине, где осуществляется сборка. Внимание! Данные флаги нельзя использовать, если собираетесь компилировать вашу программу для разных CPU. 2.Для проверки сборки цели `sllist_mergesort` с использованием относительного пути ‘examples/gnucalgthms’ в ‘VPATH’, выполнить действия прописанные в дампе 4.2.3.3.1 в дереве gasrunparts-0.9 до выполнения команды ‘make install’. Дамп 4.2.3.3.1
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

3) Оптимизации GCC

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

9) Makefiles - VPATH and RCS

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

16) Example Makefile for Static Library

17) How to compile a static library in linux