Программы
Автоматизация работы с помощью make

Автоматизация работы с помощью make

Во время сборки приходится делать много рутинных операций. Давайте автоматизировать!

Когда-то давно люди решили автоматизировать операции, связанные со сборкой и поддержкой кода. Решением стала команда make. Она делала одну вещь и хорошо.

Со временем эта "одна вещь" стала больше, а make продолжал стараться делать её хорошо, но потерял простоту и низкий порог входа. Проще нажать на кнопочку в IDE, чем прочитать man make, который даже не даст вам ответа на вопрос "а что писать в Makefile".

Однако, полезно понимать, как происходит сборка, да и сам механизм makefile-ов может вам помочь ещё ни раз – даже в тех же сборках js-пакетов (внезапно увидел их и там). Ну и в целом – это часть той древней первозданной "магии", которую приятно понимать.

Для начала в общем виде:

цель: зависимости
[tab] команда

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

И пример из прошлой темы, где мы писали библиотеку для связных списков:

all: main.c linked_list.c
	clang linked_list.c main.c -o linked_list_example

Далее сама сборка с помощью make

→ make all

Или же просто

→ make

– цель all – цель по умолчанию.

Могли ли мы это же сделать в предыдущем случае (не разделять компиляцию и линковку)? Естественно, но зачем-то мы собирали отдельно? Если мы вносим изменения в код, нам, естественно, нужно будет перекомпилировать и слинковать. Но если мы разделяем компиляцию/трансляцию по файлам/библиотекам, то надо перекомпилировать только этот файл. Это ускоряет сборку, особенно если у нас большой проект. И да, это называется "инкрементальная компиляция".

Теперь напишем Makefile с использованием инкрементальной компиляции:

linked_list.o: linked_list.c
	clang -c linked_list.c -o linked_list.o
main.o: main.c
	clang -c main.c -o main.o
all: main.o linked_list.o
	clang linked_list.o main.o -o linked_list_example

– теперь будут компилироваться только изменённые файлы.

Также, возможно вы видели и цель install, когда устанавливали программы:

→ make
→ make install

а вот файла install там не было. Становится очевидно, что целями могут быть не только файлы, но и "фиктивные цели". Они определяются в "цели" .PHONY:

.PHONY: all clean install uninstall

linked_list.o: linked_list.c
	clang -c linked_list.c -o linked_list.o
main.o: main.c
	clang -c main.c -o main.o

all: main.o linked_list.o
	clang linked_list.o main.o -o linked_list_example
clean:
	rm -rf hello *.o
install:
	install ./linked_list_example /usr/local/bin
uninstall:
	rm -rf /usr/local/bin/linked_list_example

– clean, install и uninstall – частые там гости.

Можно, конечно, обойтись без .PHONY, но тогда make будет смотреть на существование файла/цели. Он ведь создавался для сборки, поэтому завязан на файлах: make linked_list.o – этот файл уже есть, ничего делать не надо. И т.д.

Также make позволяет использовать переменные:

СС=clang
CFLAGS=-Wall
LDFLAGS=

.PHONY: all clean install uninstall

linked_list.o: linked_list.c
	$(CC) $(CFLAGS) -c linked_list.c -o linked_list.o
main.o: main.c
	$(CC) $(CFLAGS) -c main.c -o main.o
all: main.o linked_list.o
	$(CC) $(LDFLAGS) linked_list.o main.o -o linked_list_example

clean:
	rm -rf hello *.o
install:
	install ./linked_list_example /usr/local/bin
uninstall:
	rm -rf /usr/local/bin/linked_list_example

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

ДЗ

Разобраться и дописать Makefile

all: $(SOURCES) $(EXECUTABLE)

$(EXECUTABLE): $(OBJECTS)
	$(CC) $(LDFLAGS) $(OBJECTS) -o $@

.c.o:
	$(CC) $(CFLAGS) $< -o $@
Также может быть вам интересно:

Сделаем свою небольшую библиотеку на Си

Делаем свою первую переиспользуемую библиотеку на Си

Читать »

Адаптация доклада для проектных менеджеров / владельцев продукта

Не так давно выступил с докладом "Если хозяина нет. Жизнь без начальника" – вот его текстовая адоптация.

Читать »
Фото Python: Встроенные типы данных (list, set, dict, etc)

Python: Встроенные типы данных (list, set, dict, etc)

В Python есть множество встроенных типов данных. Их использование значительно упрощает жизнь и ускоряет разработку программных продуктов.

Фото Python: типы данных, переменные, логическое ветвление и циклы

Python: типы данных, переменные, логическое ветвление и циклы

Первая часть заметок о Python. О базовых типах, переменных, ветвлении и циклах.

Фото Как сделать свою middleware в Django (с примерами)

Как сделать свою middleware в Django (с примерами)

Middleware или "промежуточное программное обеспечение" - элегантный способ установить общие правила обработки запросов и ответов приложения. Давайте напишем парочку middleware, чтобы понять, как они работают.

Фото Как настроить отправку почты из Django

Как настроить отправку почты из Django

Письма об ошибках, отчёты на почту, восстановление паролей - всё это полезно при работе с сайтом. Django предоставляет удобный способ это сделать с минимумом настроек!

Фото Добавляем постраничную пагинацию на Django сайт

Добавляем постраничную пагинацию на Django сайт

На сайтах часто встречаются многостраничные объекты: список товаров, список заметок и т.д. Поэтому важно уметь добавить навигацию по страницам на Django-проекте.

Фото Новый оператор match-case в Python

Новый оператор match-case в Python

В новой версии Python (3.10) появится новый оператор. Новый оператор сопоставления по шаблону (match-case).

Фото Нет слов, одни... однострочники

Нет слов, одни... однострочники

На днях вышел пост со списком полезных однострочников для JavaScript программистов. Памятуя Perl-овую молодость, заглянул туда.

Фото Добавляем переменные в контекст Django шаблонов (свой контекст-процессор)

Добавляем переменные в контекст Django шаблонов (свой контекст-процессор)

В Django вы можете передавать данные в шаблоны посредством контекстов. Контекст передаётся из контроллера (view в терминах Django), однако, если одни и те же данные нужны в разных местах, лучше сделать свой контекст-процессор.