Сборочные файлы в Linux: обзор - Ложные цели, макрос и специальные символы
ОГЛАВЛЕНИЕ
Ложные цели, макрос и специальные символы
Иногда target – это не файл, а действие, подлежащее выполнению. Цель, не относящаяся к файлу, называется ложной целью.
Например:
getobj:
mv obj/*.o . 2>/dev/null
Цель getobj перемещает все файлы с расширением .o из каталога obj в текущий каталог – ничего особенного. Но что если в obj нет файла? В этом случае команда mv вернет ошибку, которая передастся команде make.
Замечание: Стандартное поведение команды make – прервать обработку при обнаружении ошибки при выполнении команд в правилах.
Конечно, бывают случаи, когда каталог obj пустой. Как не допустить прерывания команды make при появлении ошибки?
Можно использовать специальный символ - (минус) перед командой mv. Итак:
getobj:
-mv obj/*.o . 2>/dev/null
- Приказывает make игнорировать ошибки. Есть еще один специальный символ: @ приказывает make не печатать команду в стандартный вывод перед выполнением. Можно объединять оба символа всегда перед командой:
getobj:
-@mv obj/*.o . 2>/dev/null
Есть специальная ложная цель по имени all(все), позволяющая сгруппировать несколько главных целей и ложных целей. Ложная цель all часто используется, чтобы направлять команду make при чтении сборочного файла.
Например:
all: getobj app install putobj
Команда make выполнит цели в последовательности: getobj, app, install и putobj. Также команда make поддерживает макросы в сборочных файлах. Можно определить макрос, написав:
MACRONAME=value
и обратиться к значению MACRONAME(макроимя), написав $(MACRONAME) или ${MACRONAME}.
Например:
EXECPATH=./bin
INCPATH=./include
OBJPATH=./obj
CC=cc
CFLAGS=-g -Wall -I$(INCPATH)
При выполнении make заменяет $(MACRONAME) на выделенное определение. Узнав, что такое ложные цели и макросы, переходим к следующему примеру.
Тестирование sample3
sample3 имеет чуть более сложный сборочный файл, использующий макросы, ложные цели и специальные символы. Также в каталоге sample3 есть 3 подкаталога:
• include – тут хранятся все файлы .h.
• obj – каталог, куда все объектные файлы перемещаются после компоновки, и откуда они перемещаются перед началом новой компоновки.
• bin – сюда копируются конечные исполнимые файлы.
Исходники .c хранятся вместе со сборочным файлом в корне sample3. Если имя сборочного файла - makefile, то не надо использовать параметр –f в команде make.
Файлы разделены по каталогам для повышения реалистичности примера. Листинг сборочного файла идет ниже:
Рисунок 4:Листинг сборочного файла sample3
Конечно, номеров строк нет в сборочном файле. Они используются здесь для облегчения чтения исходника.
Итак, имеется:
• Строки 7 - 13: определение некоторого макроса:
- INSTPATH, INCPATH и OBJPATH ссылаются на подкаталоги.
- CC и CFLAGS - компилятор и наиболее распространенные параметры компилятора соответственно. Если хотите, можете изменить CC, чтобы он указывал на компилятор gcc.
- COND1 и COND2 – выполняемые команды, затем обращается к макросу.
• Строка 17: ложная цель all как самая первая цель в сборочном файле. Цель all определяет порядок выполнения целей слева направо:
- За getobj первым идет app (main.o, mod_a.o и mod_b.o являются зависимостями app и будут вызваны при необходимости), затем make вызывает цель install, и в конце выполняется putobj.
• Строки 19-29: перечисляют цели, отвечающие за компоновку самого приложения. Обратите внимание на использование макросов CC и CFLAGS. Помните, макросы заменяются значениями. Так что $(CC) читается как cc, а CFLAGS читается как -g -Wall -I./include. Затем строка 20 интерпретируется как:
- -g -Wall -I./include -o app main.o mod_a.o mod_b.o
• Строки 31-34: перечисляют цели getobj и putobj. Это ложные цели, помогающие или организующие процесс компоновки:
- getobj указан в цели all как выполняемый первым. Он отвечает за извлечение объектных файлов из каталога obj ($(OBJPATH)) перед началом компоновки. Команда make может сравнить метки времени у объектов с метками времени у файлов исходного кода и заново скомпоновать или нет объектный файл согласно изменениям в исходных файлах.
- putobj делает противоположное. После успешной компоновки он перемещает все объектные файлы обратно в каталог obj.
• Строка 38: цель install – еще одна ложная цель, показывающая использование оператора командного процессора if(если). Программирование командного процессора не входит в данную статью. Если вам нужно больше информации, гуглите. Далее в статье объяснено, что делает цель install(установить).
• Строка 47: цель cleanall удаляет все файлы, чтобы заставить команду make скомпоновать все заново. Она не вызывается во время процесса компоновки. Ее можно вызвать путем передачи ее в качестве аргумента команде make:
make cleanall
Также обратите внимание на использование специальных символов (- и @) перед командами в getobj, putobj, install и cleanall. Как сказано выше, - приказывает make продолжить обработку даже при появлении ошибки, а @ приказывает make не печатать команду перед ее выполнением.
Замечание: В цели install каждая строка завершается "\", и символ "\" должен быть последним символом в строке (никаких пробелов после него), иначе make может выдать следующую ошибку:
строка xxx: синтаксическая ошибка: неожиданный конец файла
Где xxx – строка, считающаяся началом блока.
Всегда при группировке команд с помощью \ он должен быть последним символом в строке.
Выполняется следующая последовательность команд (рисунок 5):
Рисунок 5: Последовательность команд sample3.
1. Команда make вызывается без аргументов, так как имя сборочного файла - makefile.
- Если нет файлов в каталоге obj, getobj выдаст ошибку. Однако символ - (минус) перед командой mv (строка 32) не дает make прервать обработку.
- Это сообщение печатается, только если условие истинно (строка 39). Обратите внимание на символ @ перед оператором if. Оно запрещает печать всего блока. Условие сравнивает две строки, являющиеся результатом двух команд, определенных макросами COND1 и COND2 (строки 12-13). Условие использует сочетание команд командного процессора, чтобы проверить, различаются ли метки времени app и ./bin/myapp. Если истина – app копируется в ./bin с именем myapp, и его полномочия доступа к файлу меняются, чтобы разрешить доступ к нему только владельцу файла. Если никакого условия не было наложено, то цель install выполняется при каждом вызове make.
2. Команда make вызывается снова, но выполняются только getobj и putobj. Не происходит компоновка и, следовательно, не выполняется install.
3. Команда touch меняет метку времени для inc_a.h
4. Команда make вызывается, и перекомпоновываются только цели с зависимостью inc_a.h.
5. Перечисляется содержимое ./bin. Обратите внимание на полномочия myapp.
6. Пример использования цели cleanall.