Призначення, історія, варіанти
Утиліта make призначена для автоматизації збирання проектів. Якщо будь-які файли проекту можуть бути згенеровані з інших файлів, утиліта дозволяє виконати процес побудови найбільш оптимальним способом, по можливості мінімізуючи кількість оброблюваних файлів.
Історично утиліта призначалася для збірки проектів на мові C в операційній системі Unix, проте може бути використовуватися для роботи з будь-якими проектами. Перша версія системи була створена в 1977 році.
На сьогоднішній день найбільш поширені три варіанти утиліти, об'єднані спільними принципами роботи, але відрізняються синтаксисом мови і можливостями:
GNU make - найпоширеніший і функціональний варіант
BSD make (pmake) - використовується в проектах BSD, по функціональності приблизно відповідає GNU make
nmake (Microsoft make) - працює під Windows, малофункціонален, тільки базові принципи make.
Ми працюємо з GNU make. На BSD системах (зокрема, FreeBSD, він може бути доступний як gmake, на Linux - просто make).
Основні принципи
Утиліта make працює за правилами (rules). записаним в спеціальному файлі конфігурації. Правила визначають цілі (targets). завімості між цілями і набір команд для виконання кожної мети.
Цілі можуть відповідати певним файлам. Крім того, цілі можуть не відповідати жодному файлу і використовуватися для угруповання інших цілей або певної послідовності команд. Такі цілі називаються phony targets.
Кожна мета може залежати від виконання інших цілей. Виконання мети вимагає попереднього виконання інших цілей, від яких вона залежить.
У разі залежності між цілями, відповідними файлів, мета виконується тільки в тому випадку, якщо файли, від яких вона залежить, новіше, ніж файл, відповідний мети. Це дозволяє перегенеріровать тільки файли, що залежать від змінених файлів, і не виконувати потенційно довгий процес пересборки всіх файлів проекту.
Таким чином, makefile визначає граф залежностей, за яким утиліта make виконує ту чи іншу мету, по можливості мінімізуючи кількість операцій складання.
запуск make
Незважаючи на те, що для make можна вказати довільний файл правил, як правило використовують стандартне ім'я Makefile. Підтримуються також кілька альтернативних імен за замовчуванням, але має сенс використовувати найбільш поширене.
буде використовувати файл Makefile. що знаходиться в поточному каталозі.
При запуску make можна вказати мету, яка буде виконана. Якщо мета не вказана, використовується мета за замовчуванням, яка або вказана в файлі правил явно, або неявно використовується перша певна мета.
Явна вказівку мети виконується інструкцією DEFAULT_GOAL в Makefile.
викличе обробку мети clean файлу Makefile. що знаходиться в поточному каталозі.
Можна вказати відразу кілька цілей.
Виконання цілей може бути налаштоване з використанням змінних (про які нижче). При запуску make можна вказати значення змінних:
Значення змінної PREFIX буде доступно в правилах Makefile і може бути використано при складанні.
Команда підтримує також ряд додаткових опцій, з яких найбільш важливі наступні:
-f - дозволяє явно вказати файл правил
-C - переходить в зазначений каталог перед виконанням, може бути, наприклад, використана для запуску make з зовнішнього каталогу по відношенню до каталогу проекту
-B - відключає перевірку часу залежних цілей і примусово виконує їх повністю
Базовий синтаксис make
Основна конструкція, яка використовується в файлах make. виглядає наступним чином:
dep1. dep2 - цілі, від яких залежить мета target
command1. command2 - команди, що виконуються для досягнення мети target
Цей фрагмент визначає, що файл style.css залежить від файлу src / less / app.less і для його складання необхідно виконати команду lessc src / less / app.less> style.css. Перегенерація файлу style.css буде виконуватися тільки в разі, якщо файл src / less / app.less новіше, ніж файл style.css (до тих пір, поки при запуску make не буде зазначений ключ -B).
Перед кожною командою всередині опису мети повинен бути присутнім символ табуляції. В принципі, це налаштовується, але краще використовувати загальноприйняті угоди. Якщо замість табуляції використовуються прогалини, make працювати не буде.
Як команд обробки цілей використовуються команди shell. Текст команди виводиться, для того, щоб він не виводився, команду необхідно почати з символу @.
Кожна команда запускається в окремому інтерпретатор shell, таким чином, команди не пов'язані один з одним. Інакше кажучи, один рядок команди - один shell. Це поведінка може бути перевизначити за допомогою спеціальної мети .ONESHELL.
Якщо команду (або список залежностей) необхідно записати в кілька рядків, використовують символ перенесення \.
PHONY targets
Цілі, які не відповідають файлам, і призначені для виконання набору команд або угруповання завімостей, декларуються в такий спосіб:
Декларацій .PHONY може бути кілька, зазвичай визначають одну і прописують туди всі відповідні цілі.
У нашому прикладі виклик make clean призведе до виконання мети clean. яка безумовно виконає видалення тимчасових файлів.
У разі, якщо у phony target є залежність у вигляді іншої phony target, то залежність виконується перед залежить метою. Таким чином, ми отримуємо механізм, що нагадує підпрограми. Наприклад, ми можемо визначити мету all. збирає всі файли проекту, і окремі цілі css. js і php. збирають окремої css-файли, js-файли і обробні php файли.
Відповідно, в Makefile ми можемо написати:
В результаті ми можемо використовувати make all для пересборки всіх файлів і, скажімо, make css для пересборки тільки CSS-файлів.
змінні
У make-файлі можна використовувати змінні, хоча правильніше сказати, що можна використовувати макроси.
Змінні визначаються привласненням в makefile або можуть бути передані ззовні.
Змінні - це макроозначення, причому обчислення змінної завжди виконується в самий останній момент перед підстановкою. Макроси можуть використовувати всюди в тексті makefile.
Підстановка виконується конструкцією $ (VAR) на відміну від shell, де використовується $ VAR.
Якщо в shell команді використовується shell-змінна, необхідно квоти знак $. дублюючи його, наприклад:
Крім макропеременних існують і більш традиційні, в яких значення встановлюється відразу. Для роботи з ними використовується оператор: =. У наших умовах досить використовувати звичайні змінні.
Часто потрібно визначити змінну тільки в тому випадку, якщо вона ще не була визначена. Для цього використовується оператор? =.
Відповідно, якщо ми викличемо
буде використана кодування UTF8. а в разі
буде використана CP1251.
Якщо змінна містить кілька рядків, можна використовувати синтаксис define.
автоматичні змінні
Make підтримує набір автоматичних змінних, що полегшують написання правил. Наприклад, змінна $ @ відповідаю поточної мети (то, що зліва від.), А змінна $ ^ - списку залежностей (то, що праворуч від.). Таким чином, наприклад, можна написати:
В результаті www / js / script.js буде результатом об'єднання трьох js-файлів.
Повний список таких змінних наведено в документації, для нас найбільш цікаві:
$<— имя первой зависимости
$? - імена всіх залежностей, які новіше ніж мета
$ ^ - імена всіх залежностей мети
З повним списком можна ознайомитися в документації: Automatic Variables.
умовне виконання
У Makefile можна використовувати умовні вирази. Знову ж таки, ми говоримо про макрообработке make, відповідно, умовні вирази працюють на рівні makefile, а не на рівні команд. Зазвичай умовні вирази використовуються для визначення тих чи інших цілей в залежності від значення змінних. наприклад:
В якості умов можна перевіряти визначеність змінної, а також її значення:
Повністю з можливостями умовних виразів можна ознайомитися в документації: Conditional syntax.
шаблонні правила
Шаблонні правила (pattern rules) дозволяють вказати правило перетворення одних файлів в інші на підставі залежностей між їхніми іменами. Наприклад, ми можемо вказати правило для отримання об'єктного файлу з файлу на мові C:
Зверніть увагу на використання змінної%<. которая в таких правилах используется для получения имени исходного файла.
Шаблони не зобов'язані обмежуватися розширеннями файлів. Якщо вихідні і вихідні файли відповідають один одному і в їхніх іменах є якась залежність, можна використовувати pattern rule.
Включення інших файлів make
Файл make може підключити інші файли make оператором include.
Таким чином, з файлів можна будувати модульну систему, часто має сенс виконувати include всередині умовного оператора.
Make визначає великий набір функцій, які можуть бути використані в змінних (макросах). Виклик функції виконується конструкцією:
Функції дозволяють обробляти рядки, імена файлів, організовувати цикли по набору значень, організовувати умовний виклик інших функцій.
Кілька прикладів з hive. Отримуємо поточний час (зверніть увагу на використання: =.
Включення файлу container.mk тільки в разі, якщо він існує:
Формування імені MySQL бази по імені проекту:
Додавання префіксів і суфіксів до імен файлів
власні функції
Можна створювати власні параметризрвані функції шляхом визначення змінних, що містять спеціальні змінні $ 1. $ 2. відповідні переданим аргументів. Виклик для користувача функції проводиться спеціальним макросом call.
Дуже тупий приклад:
Тепер можна написати:
рекурсивний make
Крім включення іншого файлу make, Makefile може виконати інший файл make у вигляді окремого make-процесу.
Це досить часта ситуація в разі модульних проектів. Наприклад, для кожної з компонент проекту (сервер, клієнт, бібліотеки) передбачений свій Makefile. при цьому є основною Makefile. задає набір загальних налаштувань і викликає Makefile кожного підпроекту.
Виклик make з Makefile часто називають submake. Для виклику використовується змінна $ (MAKE).
Значення цієї змінної відповідає шляху до програми make. обробної поточний файл.
Для передачі змінних в викликається таким чином файл їх необхідно явно експортувати:
Значення змінної PREFIX буде доступно в subsystem / Makefile.
паралельний make
Make вміє распараллеливать виконання правил. Для цього використовується опція -j. що дозволяє вказати кількість використовуваних паралельних процесів. Крім прискорення процесу складання ця особливість іноді дозволяє реалізовувати на коліні дуже прості сценарії обробки, що мають на увазі багатозадачність. Наприклад, можна реалізувати простий, але працездатний менеджер черг на make, використовуючи файли для зберігання завдань і каталоги для зберігання файлів завдань на різних стадіях (очікування, виконання, результат).
Паралельне виконання може бути заборонено за допомогою спеціальної мети .NOTPARALLEL.
спеціальні цілі
Make підтримує набір цілей, які можуть бути використані як модифікатори для наступних цілей або як службові інструкції. Ось деякі з таких спеціальних цілей:
PHONY - визначає набір phony targets;
DEFAULT - визначає мету, яка буде викликатися, якщо не знайдена залежна мета, необхідна для виконання іншої мети;
IGNORE - вказує цілі, для яких необхідно ігнорувати помилки при виконанні мети;
SILENT - визначає набір цілей, для яких необхідно придушувати висновок команд, що виконують мета;
NOTPARALLEL - забороняє паралельне виконання makefile;
ONESHELL - виконує набір команд мети в одному процесі shell.
варіанти використання
Найчастіше про make кажуть в контексті збірки програм на C / C ++, в кінці кінців, для цього він спочатку призначався. Однак, make - набагато більш універсальний інструмент. Записуючи makefile, ми декларативно описуємо певний стан відносин між файлами, яке кожен запуск make буде намагатися підтримувати. Декларативний характер визначення стану дуже зручний, в разі використання будь-якого імперативного мови (наприклад, shell) нам доводилося б виконувати велику кількість різних перевірок, отримуючи на виході складний і заплутаний код.
Крім того, використання залежностей між phony targets, позволяюща, по суті, декларативно описувати якийсь (обмежений) кінцевий автомат, може бути корисно для написання різних адміністративних сценаріїв. Використовуючи make як каркас для виконання різних shell-команд, ми отримуємо по суті якийсь базовий framework для shell з готовим призначеним для користувача інтерфейсом (виклик make + передача змінних), вбудованими засобами відстеження залежностей, паралельного виконання, Макроозначення і т.д.
Тому базове знання make дозволяє в ряді випадків вирішити проблему нехай і не найкрасивішим, але зате швидким і досить надійним способом.