---
title: Makefiles для чайников.
description: Автоматизируем по старинке.
position: 1
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` будет присутствовать в директории цель не будет выполняться.
