--- title: Makefiles для чайников. description: Автоматизируем по старинке. position: 3 category: 'DevOps' --- # Makefiles для чайников Что такое мейкфайлы? Вы наверняка их встречали, если собирали проекты из ихсодных файлов. Если описывать в двух словах, то это просто описание команд для упрощения работы с проектом. Изначально делались для того, чтобы удобно компилировать всякие проекты на любых языках. ## Как они работают? Достаточно просто. Вот пример `Makefile`: ```makefile{}[Makefile] # Сгенерируем простой файл test.gen: @echo 'echo "Hi!"' > "test.gen" # Команда для запуска файла test.gen .PHONY: run run: test.gen sh "test.gen" ``` До двоеточий обазначены команды (tagets). Например тут это: `test.gen` и `run`. Данные таргеты можно запускать через `make ${target}`. Например, если ввести `make run` в папке с `Makefile`, то мы получим следующее: ```console $ sh "test.gen" Hi! ``` Как видно из выхлопа данной команды, у нас успешно запустился файл `test.gen`, хотя мы не запускали команду `make test.gen`. Что произошло? Давайте разбираться. ## Зависимости таргетов На строчке объявления таргета `run` видно, что объявлен `test.gen`. Это зависимость данного таргета и она будет вызвана до того, как выполнится скрипт описываемого таргета. Таких зависимостей может быть много, перечисляются они через пробел. Например: ```makefile{}[Makefile] .PHONY: target1 target1: echo "1" .PHONY: target2 target2: target1 echo "2" .PHONY: target3 target3: target1 target2 echo "memes" ``` При вызове `make target3` будет выведено: ```console $ make target3 echo "1" 1 echo "2" 2 echo "memes" memes ``` Как можно видеть, он построил граф зависимостей и не выполнил `target1` дважды. ## Сокрытие вывода команд В предыдущем примере можно заметить, что он написал все команды в терминал. Для того, чтобы этого избежать следует добавить "@" в начало команды и она не будет напечатана. ```makefile{}[Makefile] .PHONY: target1 target1: @echo "1" .PHONY: target2 target2: target1 @echo "2" .PHONY: target3 target3: target1 target2 @echo "memes" ``` Теперь при вызое `make target3` будет показано следующее: ```cosole $ make target3 1 2 memes ``` ## Валидация сгенерированных файлов Зачастую `Makefile` используют для компиляции С и зачастую требуется собрать какую-либо часть проекта в файл и пропустить сборку этого файла, если он уже собран. Раскрою секрет, в Makefile это базовый функционал. Давайте немного поменяем первый Makefile и запустим дважды. ```makefile{}[Makefile] # Сгенерируем простой файл test.gen: echo 'echo "Hi!"' > "test.gen" # Команда для запуска файла test.gen .PHONY: run run: test.gen sh "test.gen" ``` Теперь вызываемая команда таргета `test.gen` выводится на экран. Позапускаем. ```console $ make run echo 'echo "Hi!"' > "test.gen" # Наша командабыла вызвана. sh "test.gen" Hi! $ make run sh "test.gen" # Наша команда не вызвана. Hi! ``` Дело в том, что названия таргетов - названия файлов, которые должны сгенерировать эти самые таргеты. То есть, в данном случае таргет `test.gen` должен сгенерировать файл `test.gen` по окончании выполнения. Если этот файл уже присутствует, то команда выполнена не будет. Именно поэтому у нас она не запустилась второй раз, так как в первый запуск был создан треубемый файл и его никто не удалял между запусками. А что если я вот не хочу чтобы команда создавала и проверяла файлы? Для этого пишут `.PHONY: ${target}`. Например у нас так объявлен таргет `run` и, даже если файл с названием `run` будет присутствовать в директории цель не будет выполняться.