Сборочные файлы в Linux: обзор - Базовый синтаксис сборочных файлов
ОГЛАВЛЕНИЕ
Базовый синтаксис сборочных файлов
Рисунок 1: Основной синтаксис сборочного файла
Сборочный файл состоит из набора целей, зависимостей и правил. Цель большей частью является файлом, подлежащим созданию или обновлению. Цель зависит от набора исходных файлов или даже от других целей, описанных в списке зависимостей. Правила – необходимые команды для создания целевого файла с помощью списка зависимостей.
Как видно на рисунке 1, каждая команда в части Правила должна быть в строках, начинающихся с символа табуляции. Пробел дает ошибки. Также пробел в конце строки правила может заставить make выдать сообщение об ошибке.
Сборочный файл читается командой make, определяющей целевые файлы, подлежащие компоновке, путем сравнения дат и времен (метка времени) исходных файлов в списке зависимостей. Если у какой-либо зависимости изменилась метка времени с момента последней компоновки, команда make выполнит правило, связанное с целью.
Тестирование sample1
Пора сделать простой тест. Исходный код содержит каталог по имени sample1. Есть четыре файла:
• app.c и inc_a.h: простое приложение на C.
• mkfile.r: Правильный сборочный файл (расширение .r означает правильный).
• mkfile.w: Неполный или плохо написанный сборочный файл (расширение .w означает неверный). Это не значит, что есть ошибки синтаксиса.
Итак, имеется:
mkfile.r
# Это очень простой сборочный файл
app: app.c inc_a.h
cc -o app app.c
и
mkfile.w
# Это очень простой сборочный файл
app: app.c
cc -o app app.c
Видимо, разница между ними кажется неважной, но в ряде случаев она важна. Сначала проверяется часть сборочного файла:
• Символ # применяется для вставки комментариев в сборочные файлы. Весь текст от символа комментария до конца строки игнорируется.
• app - цель, исполнимый файл, который должен быть скомпонован.
• app.c и inc_a.h являются зависимостями целевого app (inc_a.h присутствует только в mkfile.r).
• cc -o app app.c – правило, используемое для компоновки цели, учитывающее любые изменения файлов в списке зависимостей.
Следующая последовательность команд демонстрирует их работу (Рисунок 2):
Рисунок 2: Последовательность команд sample1
1. Команда make вызывается для обработки mkfile.r, и правило выполняется для создания исполнимого файла app.
2. app удаляется, чтобы заставить команду make снова скомпоновать app.
3. Теперь команда make вызывается для обработки mkfile.w, и получается такой же результат, как в пункте 1.
4. Команда make вновь вызывается с использованием mkfile.r, но ничего не выполняется, потому что цель новейшая.
5. Такой же результат, как в пункте 4, на этот раз обрабатывается mkfile.w.
6. Команда touch используется для изменения метки времени для inc_a.h, имитируя изменение, сделанное в файле.
7. mkfile.w не узнал изменение в модуле inc_a.h.
8. mkfile.r обрабатывается, как ожидалось.
9. Команда touch вызывается для изменения времени доступа для app.c, имитируя изменение, сделанное в файле.
10. mkfile.w обрабатывается, как ожидалось.
11. Команда touch вызывается для изменения времени доступа для app.c, имитируя изменение, сделанное в файле.
12. mkfile.r обрабатывается, как ожидалось.
Теперь ясно, почему mkfile.w считается плохим или неполным сборочным файлом. Он не учитывает модуль inc_a.h, потому что он не описан в списке зависимостей цели app.
Тестирование sample2
sample2 – еще один пример простого сборочного файла, но на этот раз есть несколько целей. Опять имеются 2 сборочных файла: mkfile.r и mkfile.w, чтобы показать верных и неверный способ написания сборочного файла.
Конечный исполнимый файл (цель app) формируется тремя объектными файлами: main.o, mod_a.o и mod_b.o. Каждый является целью со своими исходными файлами, представляющими его список зависимостей.
Цель app – главная цель, или цель, которая породит главный исполнимый файл. Обратите внимание на список зависимостей app. Они являются именами других целей.
Оба сборочных файла полные. Главное отличие – порядок помещения целей в сборочный файл.
Итак, имеется:
mkfile.r
app: main.o mod_a.o mod_b.o
cc -o app main.o mod_a.o mod_b.o
main.o: main.c inc_a.h inc_b.h
cc -c main.c
mod_a.o: mod_a.c inc_a.h
cc -c mod_a.c
mod_b.o: mod_b.c inc_b.h
cc -c mod_b.c
и
mkfile.w
main.o: main.c inc_a.h inc_b.h
cc -c main.c
mod_a.o: mod_a.c inc_a.h
cc -c mod_a.c
mod_b.o: mod_b.c inc_b.h
cc -c mod_b.c
app: main.o mod_a.o mod_b.o
cc -o app main.o mod_a.o mod_b.o
Выполняется следующая последовательность команд (Рисунок 3):
Рисунок 3:Последовательность команд sample2
1. Команда make вызывается для обработки mkfile.w, и выполняется только первое правило.
2. Все объектные файлы, полученные в результате предыдущих компоновок, были удалены, чтобы заставить команду make выполнить полную компоновку.
3. Команда make вызывается для обработки mkfile.r, и все модули правильно создаются.
4. app выполняется.
5. Все объекты и исполнимые файлы были удалены, чтобы заставить команду make выполнить полную компоновку.
6. Команда make вновь вызывается для обработки mkfile.w. Но на этот раз цель app передается как аргумент, и все модули правильно создаются.
Что неправильно в mkfile.w? Технически – ничего, если информируется главная цель (рисунок 3 - пункт 6). Но если не информировать цель, команда make прочитает сборочный файл с начала, чтобы найти первую цель для обработки. В случае mkfile.w этой целью является main.o. Цель main.o только говорит make скомпоновать main.o из main.c, inc_a.h и inc_b.h – не надо делать больше ничего связанного. Make не прочитает следующую цель.
Замечание: Прочтение первой цели определяет, как make должна интерпретировать все остальные цели и какого порядка она должна придерживаться в ходе процесса компоновки. Итак, первая цель должна быть главной целью, и она может быть связана с одной и более вторичными целями, чтобы выполнить компоновку.
Цель app размещена в разных строках в обоих файлах компоновки, но их синтаксис идентичен. Пункт 3 и пункт 6 рисунка 3 дадут одинаковый результат:
• цель app говорит команде make, что ей надо сначала обработать три зависимости: main.o, mod_a.o и mob_b.o перед компоновкой конечного исполнимого файла (app).
• Затем make начинает искать цель main.o и обрабатывает ее.
• Затем она находит и обрабатывает mod_a.o.
• В конце обрабатывается mod_b.o.
• Когда все три цели скомпонованы, обрабатывается правило цели app, и создается исполнимый файл app.