Makefile ToC

К большому сожалению, несмотря на наличие продвинутых систем сборок, нам всё ещё приходится использовать утилиту make и писать Makefile-⁠ы руками.

Передо мной встала задача по составлению справки по целям сборки в текущем Makefile проекта. Из коробки древняя утилита не позволяет получить список целей сборки и их описание, которого не предполагалось.

Поискав готовые решения и убедившись, что большая часть из них не сработает на macOS из-⁠за её родства с BSD-⁠системами, я принял решение подумать над собственным вариантом генерации оглавления.

Итак, перед нам две задачи:

Первая задача решается кросс-⁠системно и довольно легко. Достаточно указать переменную .DEFAULT_GOAL в самом начале Makefile:

.DEFAULT_GOAL := help

Теперь вызов команды make без аргументов будет вызывать цель help по умолчанию:

.DEFAULT_GOAL := help

.PHONY: help
help:
	@echo This is help target

Вторая задача оказалась чуть более сложной. Она заключается в том, чтобы собрать имена целей и их описание, затем вывести их в две ровные колонки.

Посмотрев на примеры, я принял решение использовать следующий формат объявления целей сборки:

.PHONY: foo
foo: ## do some foo
	@echo foo

Здесь, после двух знаков ## следует комментарий, который будет выводиться как справочная информация. Имя цели — описание цели, всё рядом и достаточно удобно.

Теперь необходимо “оживить” команду make help, чтобы справочная информация генерировалась динамически.

Вот моё решение:

.PHONY: help
help: ## shows this help message
	@grep --fixed-strings --no-filename "##" $(MAKEFILE_LIST) \
		| grep -v 'grep --fixed-strings' \
		| sed -e 's/:/	/' \
		| awk 'BEGIN { FS="#"; } { print $$1 $$3 }' \
		| column -t -s $$'\t'

Что здесь происходит? Всё достаточно просто:

Ознакомьтесь с демонстрационным примером такого Makefile:

.DEFAULT_GOAL := help

.PHONY: help
help: ## shows this help message
	@grep --fixed-strings --no-filename "##" $(MAKEFILE_LIST) \
		| grep -v 'grep --fixed-strings' \
		| sed -e 's/:/	/' \
		| awk 'BEGIN { FS="#"; } { print $$1 $$3 }' \
		| column -t -s $$'\t'

.PHONY: foo
foo: ## do some foo
	@echo foo

.PHONY: bar
bar: ## do some bar
	@echo bar

.PHONY: some-long-named-makefile-target
some-long-named-makefile-target: ## this is some-long-named-makefile-target
	@echo some-long-named-makefile-target

.PHONY: ignored
ignored:
	@echo ignored

А вот вывод команды make без аргументов:

$ make
help                               shows this help message
foo                                do some foo
bar                                do some bar
some-long-named-makefile-target    this is some-long-named-makefile-target

Аккуратно, не правда ли?

Тем не менее, допускаю, что решение получилось далеко не самым переносимым между разными версиями make и операционными системами. Поэтому, если у вас есть возможность, старайтесь избегать устаревших систем сборок.