MakeFile

Содержание
Введение
Для чего используются Makefiles
Формат
.PHONY:
Пример из C++
Docker из Makefile
Параметризация Make
BUILD_ID
Альтернативы

Для чего используются Makefiles

Make-файлы используются, чтобы помочь решить, какие части большой программы должны быть перекомпилированы.

В подавляющем большинстве случаев компилируются файлы C или C++ .

Другие языки обычно имеют свои собственные инструменты, которые служат той же цели, что и Make.

Его можно использовать и за пределами программ, когда вам нужна серия инструкций для запуска в зависимости от того, какие файлы изменились.

В этой статье вы узнаете про использование компиляции C/C++.

Вот пример графика зависимостей, который вы можете построить с помощью Make.

Если какие - либо зависимости файла изменятся, то файл будет перекомпилирован:

Граф зависимостей для компиляции изображение с сайта www.andreyolegovich.ru
Граф зависимостей
wikipedia.org

Формат

Makefile состоит из правил (rules). Первым указывается называние цели, затем зависимости (prerequisites) и действие (recipe - набор действий), которое нужно выполнить.

Зависимости нужны не всегда и указываются по необходимости. Для простоты на первом этапе можно думать о зависимостях как о файлах, которые нужно проверить: если ни один не измнелся - заново компилировать не нужно.

Отступы по умолчанию нужно ставить табуляцией. Если хотите поменять на другой символ - задайте .RECIPEPREFIX

target: prerequisites recipe

Типичное применение: какая-то зависимость изменилась → выполнятеся действие в результате которого создаётся таргет файл.

output: main.o message.o g++ main.o message.o -o output clean: rm *.o output

Как и в статье Configure, make, install в примере выше используются стандартные цели (target)

Дополнительная информация (на английском : gnu.org: Rule-Introduction

Если файл вам не нужен, например, вы просто хотите выполнить какие-то команды - можно использовать .PHONY

.PHONY

PHONY это одна из Специальных встроенных целей (gnu.org - Special Built-in Target Names)

Рассмотрим следующий Makefile

.PHONY: site site: echo "HeiHei.ru"

Если теперь выполнить

make site

echo "HeiHei.ru"
HeiHei.ru

Удалите site из первой строки, а всё остальное не трогайте

make site

echo "HeiHei.ru"
HeiHei.ru

Вроде бы ничего не изменилось, но теперь создайте файл site рядом с Makefile

touch site
make site

make: 'site' is up to date.

Так как таргет теперь реальный make не нашёл изменений и ничего не сделал. Из-за такого простого совпадения имени цели (target) и какого-то файла в директории может перестать работать скрипт.

Для защиты от таких неприятностей и применяют PHONY

Также PHONY удобен тем, что перечисляет все цели в самом начале файла. Это улучшает читаемость кода. (если пользоваться правильно)

Пример из C++

Рассмотрим пример из статьи о заголовочных файлах .h

Есть три файла

ls

Functions.cpp Functions.h Main.cpp

Main.cpp

#include <iostream> #include "Functions.h" int main() { double b = add(1.3, 4.5); cout << "1.3 + 4.5 is " << b << "\n"; return 0; }

Functions.cpp

double add(double x, double y) { return x + y; }

Functions.h

#pragma once double add(double x, double y);

Если один из этих файлов изменился - нужно перекомпилировать проект. Для начала будем пользоваться командой

g++ Main.cpp Functions.cpp -o output

Создайте Makefile и откройте его в текстовом редакторе. Напримре, в Vim

touch Makefile
vi Makefile

Makefile будет выглядеть следующим образом

output: Main.cpp Functions.cpp Functions.h g++ main.cpp Functions.cpp -o output

Теперь для компиляции достаточно выполнить

make output

Или просто

make

В результате появится исполняемый файл output

В этот пример можно добавить ещё два шага - отдельно следить за компиляцией и убираться после работы

.PHONY: clean output: Main.o Functions.o g++ Main.o Functions.o -o output Main.o: Main.cpp g++ -c main.cpp Functions.o: Functions.cpp g++ -c Functions.cpp clean: rm *.o output

Чтобы запустить скрипт, достаточно выполнить

make

g++ -c Main.cpp
g++ -c Functions.cpp
g++ Main.o Functions.o -o output

Если нужно скомпилировать Main выполните

make Main.o

g++ -c Main.cpp

ls

Появится файл Main.o но не появятся остальные (Functions.o, output)

Functions.cpp Functions.h Main.cpp Main.o Makefile

На примере команды make Main.o можно понять почему в Make-файлах используется термин цели (target)

make Main.o говорит - создай файл Main.o а инструкция в Makefile определяет правило по которому это нужно сделать.

Если теперь выполнить make Main.o не будет перекомпилироваться. Будут выполнены только последние два шага.

g++ -c Functions.cpp
g++ Main.o Functions.o -o output

Выполните make если ещё не выполняли и не делайте после этого clean

Добавим ещё одну функцию в наш проект. Нужно указать её в файлах Functions.*

Вызывать пока не будет, поэтому Main.cpp остаётся без изменений

Functions.cpp

bool test(bool x) { return x; }

Functions.h

bool test(bool x);

make

g++ -c Functions.cpp g++ Main.o Functions.o -o output

Обратите внимание: Main.cpp не был перекомпилирован так как в нём нет изменений. Таже посмотрите на время изменения файла output оно должно измениться.

Не вносите никаких изменений в файлы и выполните

make

make: 'output' is up to date.

Перекомпиляция не нужна и поэтому не выполнена

Запустить Docker контейнер из Makefile

Читайте также статью

«Введение в Docker» Пример

.PHONY: docker docker: docker-compose -f docker/dev/docker-compose.yml build

Параметризация Make

?= позволяет переменным быть перезаписанными на существующие переменные окружения

:= перезаписывает значение переменной

PROJECT_NAME ?= myproject ORG_NAME ?= heihei REPO_NAME ?= myproject #Filenames DEV_COMPOSE_FILE := docker/dev/docker-compose.yml REL_COMPOSE_FILE := docker/release/docker-compose.yml .PHONY: test release test: docker-compose -f $(DEV_COMPOSE_FILE) build docker-compose -f $(DEV_COMPOSE_FILE) up agent docker-compose -f $(DEV_COMPOSE_FILE) up test release: docker-compose -f $(REL_COMPOSE_FILE) build docker-compose -f $(REL_COMPOSE_FILE) up agent docker-compose -f $(REL_COMPOSE_FILE) run --rm app manage.py collectstatic --noinput docker-compose -f $(REL_COMPOSE_FILE) run --rm app manage.py migrate --noinput docker-compose -f $(REL_COMPOSE_FILE) up test clean: docker-compose -f $(DEV_COMPOSE_FILE) kill docker-compose -f $(DEV_COMPOSE_FILE) rm -f docker-compose -f $(REL_COMPOSE_FILE) kill docker-compose -f $(DEV_COMPOSE_FILE) rm -f

BUILD_ID

Чтобы добавить переменным уникальности используют BUILD_ID

# Docker Compose Project Names REL_PROJECT := $(PROJECT_NAME)$(BUILD_ID) DEV_PROJECT := $(REL_PROJECT)dev

Какие альтернативы Make существуют

Популярными альтернативными системами сборки C/C++ являются SCons, CMake, Bazel и Ninja. Некоторые редакторы кода, такие как Microsoft Visual Studio , имеют свои собственные встроенные инструменты сборки.

Для Java есть Ant, Maven и Gradle.

Другие языки, такие как Go и Rust, имеют свои собственные инструменты сборки.

Интерпретируемые языки, такие как Python , Ruby и JavaScript , не требуют аналога для создания файлов.

Цель Makefile состоит в том, чтобы скомпилировать любые файлы, которые должны быть скомпилированы, основываясь на том, какие файлы изменились.

Но когда файлы на интерпретируемых языках меняются, ничего не нужно перекомпилировать.

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

Ошибки Make

make: *** No rule to make target 'main.cpp', needed by 'main.o'. Stop.

Похожие статьи
Связанные темы
C
C++
C++ Header файлы
Linux
Bash
DevOps
Configure make install
Docker
Контакты и сотрудничество:
Рекомендую наш хостинг beget.ru
Пишите на info@urn.su если Вы:
1. Хотите написать статью для нашего сайта или перевести статью на свой родной язык.
2. Хотите разместить на сайте рекламу, подходящуюю по тематике.
3. Реклама на моём сайте имеет максимальный уровень цензуры. Если Вы увидели рекламный блок недопустимый для просмотра детьми школьного возраста, вызывающий шок или вводящий в заблуждение - пожалуйста свяжитесь с нами по электронной почте
4. Нашли на сайте ошибку, неточности, баг и т.д. ... .......
5. Статьи можно расшарить в соцсетях, нажав на иконку сети: