Основы Make

Содержание
Introduction
Установка
Проверить версию
Для чего используются Makefiles
Формат
.PHONY:
Посмотреть цели Make-файла
Пример из C++
Переменные
Docker из Makefile
Параметризация Make
BUILD_ID
USER_ID
Альтернативы
$$: Вызов bash команд (например whoami)
-: Игнорировать ошибки
Связанные статьи

Установить make

sudo apt install make

или для rpm

sudo yum install make

Так как make входит в состав build-essentials можно установить вместе с этим пакетом

sudo apt install build-essentials

Проверить версию make

/usr/bin/make --version

GNU Make 4.2.1 Built for x86_64-pc-linux-gnu Copyright (C) 1988-2016 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.

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

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

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

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

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

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

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

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

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

Формат

Makefile состоит из правил (rules). Первым указывается название цели (target), затем зависимости (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)

Про опции -o и -c читайте статью «Компиляция в C++

Опция Назначение
-c Указывает компилятору не делать линковку и создавать .o файлы для каждого исходника
-o filename Меняет название output файла со стадартного на указанный
-S Directs the compiler to produce an assembly source file but not to assemble the program.

Дополнительная информация (на английском ): 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 удобен тем, что можно перечислить все цели в самом начале файла.

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

Посмотреть цели Make-файла

Если вы создали Make-файл с большим количеством PHONY целей и забыли название нужно - не обязательно продираться через весь файл

Чтобы получить списко всех целей воспользуйтесь grep и выполните

cat GNUmakefile | grep 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++ -o output Main.cpp Functions.cpp

Эта команда сначала вызывает компиляцию, затем линковку

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

touch Makefile
vi Makefile

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

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

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

make output

Или просто

make

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

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

Если вам не понятно что происходит в этом файле - изучите статью «Компиляция в C++

.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

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

make

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

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

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++ -o output Main.o Functions.o

Выполните 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++ -o output Main.o Functions.o

Обратите внимание: Main.cpp не был перекомпилирован так как в нём нет изменений.

Таже посмотрите на время изменения файла output оно должно измениться.

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

make

make: 'output' is up to date.

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

Переменные

Подробнее про переменные в Makefile читайте в статье Работа с переменными в GNUmakefile

В этом примере вы можете увидеть как названия файлов сохранены в переменную для сокращения кода.

.PHONY: clean objects = Main.o Functions.o output: $(objects) g++ -o output $(objects) Main.o: Main.cpp g++ -c Main.cpp Functions.o: Functions.cpp g++ -c Functions.cpp clean: rm *.o output

Запустить Docker container из Makefile

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

«Introduction в 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

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

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

USER_ID

To получить ID пользователя запустившего GNUmakefile

USER_ID = $(shell id -u ${USER})

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

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

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

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

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

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

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

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

Что означает cc -c

cc это C compiler

Существует несколько общедоступных компиляторов C

В этой статье использовался gcc

-c это опция, которую разбирали здесь

whoami

В обычном Bash скрипте достаточно написать $(whoami) и это будет равносильно подстановке вывода whoami

В Make файле это может не получиться. Есть два варианта решить проблему

`whoami`

И

$$(whoami)

Игнорировать ошибки

Если какая-то команда выполнена с ошибкой выполнение сценария прерывается.

Рассмотрим пример

RPM_DIR=/home/$$(whoami)/rpms/ .PHONY: clean-repo clean-repo: @sudo rm $(RPM_DIR)release/* @sudo rm $(RPM_DIR)master/*

Если в …release/ пусто, то удалять в …master/ make уже не будет.

Вместо этого появится ошибка:

sudo rm /home/$(whoami)/rpms/release/* rm: cannot remove ‘/home/andrei/rpms/release/*’: No such file or directory make: *** [clean-repo] Error 1

Избежать этой проблемы можно поставив - перед командой

RPM_DIR=/home/$$(whoami)/rpms/ .PHONY: clean-repo clean-repo: @-sudo rm $(RPM_DIR)release/* @-sudo rm $(RPM_DIR)master/*

[andrei@localhost ~]$ make clean-repo

rm: cannot remove ‘/home/andrei/rpms/release/*’: No such file or directory
make: [clean-repo] Error 1 (ignored)

make жалуется, но переходит ко второй команде и чистит директорию.

Похожие статьи
make
Основы make
PHONY
CURDIR
shell
wget + make
Переменные в Make файлах
ifeq: Условные операторы
filter
-c: Компиляция
Linux
Bash
C
C++
C++ Header файлы
Configure make install
DevOps
Docker
OpenBSD
Errors make

Поиск по сайту

Подпишитесь на Telegram канал @aofeed чтобы следить за выходом новых статей и обновлением старых

Перейти на канал

@aofeed

Задать вопрос в Телеграм-группе

@aofeedchat

IT

Образование

Актуально сейчас

Разное

Поиск по сайту

Подпишитесь на Telegram канал @aofeed чтобы следить за выходом новых статей и обновлением старых

Перейти на канал

@aofeed

Задать вопрос в Телеграм-группе

@aofeedchat

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