140 lines
5.4 KiB
Markdown
140 lines
5.4 KiB
Markdown
---
|
||
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` будет присутствовать в директории цель не будет выполняться.
|
||
|
||
<br>
|
||
|
||
До новых встреч.
|