Что такое фальшь в Makefile?
фальшивый в Makefile относится к конкретной цели, которая не связана с файлом. В основном он используется для определения задач или действий, которые необходимо выполнить, независимо от того, существует ли файл с таким же именем. «Фиктивные» цели обычно используются для организации и управления сложными процессами сборки при разработке программного обеспечения.
Фальшивые цели объявляются со специальной целью .PHONY в Makefile. Указывая цель как фальшивую, она сообщает Make, что цель не является файлом и всегда должна считаться устаревшей, вызывая выполнение связанных команд.
Как? Чтобы определить фиктивную цель в Makefile, вы можете использовать специальную цель .PHONY следующим образом:
«`makefile
.PHONY: имя_цели
«`
Здесь «target_name» следует заменить фактическим именем фальшивой цели, которую вы хотите определить. Несколько фиктивных целей можно указать с помощью пробелов или символов новой строки.
Например, предположим, что у нас есть Makefile с фиктивной целью под названием «clean». Мы хотим выполнять действие «очистка» каждый раз, независимо от существования файла с именем «очистка».
«`makefile
.PHONY: чистый
чистить:
rm -rf сборка/
«`
В этом примере «clean» объявлен как фиктивная цель, а соответствующая команда удаляет каталог «build/».
Почему?
Цель использования фиктивных целей в Makefile — определить задачи или действия, которые не представляют реальные файлы, а скорее используются для организации процесса сборки и выполнения таких операций, как очистка, тестирование или создание документации. Использование фиктивных целей позволяет разработчикам легко выполнять определенные действия, не беспокоясь о зависимостях файлов.
Ложные цели особенно полезны в сценариях, где одно и то же имя может использоваться для файла и цели, но они служат разным целям. Не объявляя цель как фальшивую, Make будет считать цель актуальной, если существует файл с таким же именем, что приведет к неверным результатам сборки.
Или?
В настоящее время фиктивные цели в Makefile широко используются при разработке программного обеспечения для таких задач, как очистка артефактов сборки, запуск тестов, создание документации и т. д. Они предоставляют удобный способ управления процессом сборки и обеспечивают выполнение определенных действий, когда это необходимо, независимо от зависимостей файлов.
Ложные цели можно использовать в сочетании с другими зависимостями для создания более сложных рабочих процессов сборки. Например, ложная цель «все» может зависеть от нескольких реальных целей для построения всего проекта. При запуске make all он выполнит все необходимые шаги сборки.
Кто?
В контексте Makefile использование фиктивных целей в основном осуществляется разработчиками программного обеспечения и инструментами автоматизации сборки. Разработчики определяют в Makefile фиктивные цели, чтобы указать действия, которые не связаны с реальными файлами, но необходимы для сборки и обслуживания проекта.
Другие соответствующие объекты или инструменты, которые могут быть связаны с фиктивными целями в Makefile, включают системы сборки, такие как GNU Make, CMake или любой другой инструмент, который использует Makefile для создания программных проектов. Эти системы сборки обеспечивают поддержку ложных целей для улучшения процесса сборки и позволяют разработчикам определять собственные действия.
об авторе
Я веб-предприниматель. Веб-мастер и редактор веб-сайтов, я специализируюсь на методах поиска информации в Интернете с целью сделать информацию более доступной для пользователей Интернета. Несмотря на то, что были приложены все усилия для обеспечения точности информации на этом сайте, мы не можем предложить никаких гарантий или нести ответственность за любые допущенные ошибки. Если вы заметили ошибку на этом сайте, мы будем признательны, если вы сообщите нам об этом, используя контакт: jmandii<>yahoo.fr (замените <> на @), и мы постараемся исправить ее как можно скорее. СПАСИБО
Net-Research, краткий обзор веб-страниц.
- Расчет бонуса за активность 2024
- Детская пара из Сахары
- Бонус 3000 евро 15-25 лет
Что делает .PHONY в Makefile?

.PHONY используется, чтобы пометить цель как фальшивую. Это означает цель, которая не принимает во внимание для выполнения любой файл, имя которого совпадает с его именем.
Допустим, у нас есть цель с именем clear-cache , которая удаляет кеш приложения
clear-cache: rm -rf cache/*
В результате получается следующая команда:

Команда cli для удаления кэша в большинстве случаев будет работать нормально, но давайте проверим, что произойдет, если мы добавим файл с тем же именем, что и у цели, на том же уровне, что и Makefile

Теперь при повторном запуске make clear-cache получаем:

Makefile в основном говорит, что у него уже есть целевой файл и его не нужно выполнять.
Это поведение по умолчанию не соответствует нашим ожиданиям в данном случае, и мы хотим переопределить его. В этом случае на помощь приходит директива .PHONY.
Давайте обновим наш пример, чтобы использовать его:
.PHONY: clear-cache clear-cache: rm -rf cache/*
После пометки цели как фальшивой команда ведет себя так, как ожидалось

Совет для профессионалов: еще один способ пометить цели как фальшивые — разместить их все в списке, а не над каждой из них, например:
clear-cache: rm -rf cache/* clear-build: rm -rf build/* .PHONY: clear-cache clear-build
Заключение
Чтобы переопределить поведение по умолчанию в Makefile и использовать некоторые цели, такие как средства выполнения команд, вы можете использовать .PHONY.
Ссылки:
Основы Make
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 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.
Если какие-либо зависимости файла изменятся, то файл будет перекомпилирован:

Формат
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++
Если файл вам не нужен, например, вы просто хотите выполнить какие-то команды — можно использовать .PHONY
.PHONY
Рассмотрим следующий Makefile
.PHONY: site site: echo «HeiHei.ru»
Если теперь выполнить
Удалите site из первой строки, а всё остальное не трогайте
Вроде бы ничего не изменилось, но теперь создайте файл 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++
Functions.cpp Functions.h Main.cpp
#include #include «Functions.h» int main()
Functions.cpp
double add( double x, double y)
#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
Теперь для компиляции достаточно выполнить
В результате появится исполняемый файл 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 запустить скрипт, достаточно выполнить
g++ -c Main.cpp
g++ -c Functions.cpp
g++ -o output Main.o Functions.o
Если нужно скомпилировать Main execute
Появится файл 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)
bool test(bool x);
g++ -c Functions.cpp g++ -o output Main.o Functions.o
Обратите внимание: Main.cpp не был перекомпилирован так как в нём нет изменений.
Таже посмотрите на время изменения файла output оно должно измениться.
Не вносите никаких изменений в файлы и execute
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
Читайте также статью
.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
USER_ID
To получить ID пользователя запустившего GNUmakefile
USER_ID = $ ( shell id -u $)
Какие альтернативы 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 файле это может не получиться. Есть два варианта решить проблему
Игнорировать ошибки
Если какая-то команда выполнена с ошибкой выполнение сценария прерывается.
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 жалуется, но переходит ко второй команде и чистит директорию.
Цель из других целей
Если нужно запустить несколько целей сразу, можно вызывать из новой цели
all-targets: target1 target2 target3

Несколько make-файлов в одной директории
Если в одной директории находится два и более make-файлов с совпадающими целями, вызывать из нужного файла помогает опция -f
make ├── GNUmakefile.beget └── GNUmakefile.heihei
# GNUmakefile.beget .PHONY: url url: echo «https://beget.com»
# GNUmakefile.heihei .PHONY: url url: echo «https://heihei.ru»
make -f GNUmakefile.beget url
make -f GNUmakefile.heihei url

Подпишитесь на Telegram канал @aofeed чтобы следить за выходом новых статей и обновлением старых
4 уровня владения Makefile
Я фанат Makefile и активно задействую его в большинстве своих текущих проектов. Возможно, Makefile попадался вам на GitHub в различных проектах с открытым ПО (пример). Наверняка вы задавались вопросом, что это за инструмент и что он делает.
По Makefile создано немалое количество обучающих материалов. Цель данного руководства — разжечь интерес к Makefile и научить им пользоваться буквально за 5 минут. В результате вы приобретаете навык работы с этим инструментом и можете продолжать самостоятельно его изучать.
При желании пропускайте вступительную часть и сразу переходите к Уровню 1 и Уровню 2.
Определение Make и Makefile
Если кратко, то Makefile — это файл специального формата с инструкциями для утилиты GNU Make, т.е. make , по выполнению команд, работающих в системах *nix . Как правило, Make применяется для компиляции, сборки и установки ПО.
Хотя Makefile обычно используется для компиляции C и C++, он НЕ ограничен каким-либо конкретным языком программирования. Makefile решает разные задачи:
- выполнение цепочки команд для настройки среды разработки;
- автоматическая сборка;
- запуск наборов тестов;
- развертывание.
Аргументы в пользу Makefile
Компиляция исходного кода может стать проблематичной, особенно при необходимости включать множество исходных файлов.
По сути, Makefile представляет собой утилиту для написания и выполнения набора инструкций командной строки для таких процедур, как компиляция кода, его тестирование, форматирование, запуск и т.д.
Он помогает автоматизировать рабочие процессы разработки в виде простых команд ( make build , make test , make format и make run ).
- make предустановлен на большинстве существующих систем *nix ;
- make не зависит от языка программирования/фреймворка.
От слов переходим к делу!
Краткое руководство по Makefile
При последующем разборе уровней обучения рекомендую создать Makefile и самим выполнять практические задания.
Примечание. Файл всегда должен носить имя Makefile .
Уровень 1. “Расскажи все, что нужно знать”
На этом уровне изучаем основы Makefile . Скорее всего, этих знаний хватит для эффективной работы с ним.
Допустим, у нас есть проект, использующий Docker. Для многократной сборки и запуска приложения с помощью Docker-контейнера вы обычно совершаете следующие действия.
- Выполняете команду docker build .
- Убеждаетесь в отсутствии работающих контейнеров.
- Выполняете команду docker run .
- Повторяете процедуру.
Вот как мы это делаем самостоятельно:
docker build -t image-name . --build-arg ENV_VAR=foo
docker stop container-name || true && docker rm container-name || true
docker run -d -e ANOTHER_VAR=bar--name container-name image-name
Довольно напряжно! Столько всего нужно запомнить и напечатать. Кроме того, возрастает вероятность допустить глупые ошибки.
Конечно, можно просто выполнять все три команды при каждом внесении изменений. Такой прием сработает, но об эффективности можно забыть. Вместо этого напишем Makefile следующим образом:
all: build stop run # build -> stop -> run
build:
@docker build -t image-name . --build-arg ENV_VAR=foo
stop:
@docker stop container-name || true && docker rm container-name || true
run:
@docker run -d -e ANOTHER_VAR=bar--name container-name image-name
Теперь для сборки и запуска нового Docker-образа потребуется всего лишь одна команда make all . В вышеуказанном случае можно просто вызвать make , поскольку all является первым правилом. Обратите внимание, что первое правило выбирается по умолчанию.
Правило в Makefile обычно выглядит так:
# запуск `make ` для выполнения данного правила
: # перед блоком комментариев ставится префикс "#"
Ключевые понятия уровня 1:
- (цель) — любое имя файла.
- — команды/шаги *nix для выполнения . Они должны начинаться с символа табуляции TAB .
- (пререквизиты) — необязательная часть. Указывает make на то, что перед запуском команд все пререквизиты должны быть в наличии. Исходя из этого, они выполняются в порядке от 1 до N, как показано в примере выше.
- Назначение синтаксиса @ . Если командная строка начинается с @ , вывод самой команды в консоль подавляется (ссылка).
- Первая цель выбирается по умолчанию при запуске make .
Переходим на следующий уровень и рассмотрим Makefile в действии!
Уровень 2. “Круто, но хочется большего”
Подстановка переменных довольно часто встречается во всех аспектах программирования. В этом плане Makefile — не исключение.
Так как же применять переменные среды (по умолчанию)?
Допустим, нужно организовать сборку приложения с разными аргументами, например ENV_VAR . В этом случае Makefile принимает следующий вид:
NAME := my-app
DOCKER := $(shell command -v docker 2> /dev/null)
ENV_VAR := $(shell echo $$) # Примечание: двойной символ $ для экранирования
.PHONY: build
build: ## сборка Docker-образа на основе shell ENV_VAR
@if [ -z $(DOCKER) ]; then echo "docker is missing."; exit 2; fi # tip
@docker build -t $(NAME) . --build-arg ENV_VAR=$(ENV_VAR)
Ключевые понятия уровня 2:
- .PHONY . По умолчанию make принимает цели за файлы. Если же они таковыми не являются, то пометьте их с помощью директивы .PHONY , особенно при совпадении имени файла и цели. Более подробная информация по ссылке.
- Объявление переменной Makefile с помощью синтаксиса = или := (ссылка).
- := означает однократное выполнение инструкции.
- = указывает на выполнение инструкции в каждом случае. Например, при необходимости в новом значении date при каждом вызове функции.
- С помощью переменной среды можно проверить факт существования команды, как показано в инструкции if .
Дополнительная информация:
- echo $$ устанавливает ENV_VAR на основе текущей среды shell со значением по умолчанию для development .
- Как вариант, make позволяет передавать переменные и переменные среды из командной строки, например ENV_VAR=development make build .
Теперь вы знаете достаточно, чтобы создавать Makefile для небольших и средних проектов.
Двигаемся дальше и расширяем наши возможности с Makefile !
Уровень 3. “Покажи, что-нибудь особенное”
Мы подошли к самой излюбленной части. Думаю, что всем по нраву содержательное сообщение help .
Создадим полезную цель help на тот случай, если пользователям потребуется помощь по использованию Make в проекте:
.DEFAULT_GOAL := help
.PHONY: help
help:
@echo "Welcome to $(NAME)!"
@echo "Use 'make ' where is one of:"
@echo ""
@echo " all run build -> stop -> run"
@echo " build build docker image based on shell ENV_VAR"
@echo " stop stop docker container"
@echo " run run docker container"
@echo ""
@echo "Go forth and make something great!"
all: build stop run
.PHONY: build
build:
@docker build -t image-name . --build-arg ENV_VAR=foo
.PHONY: stop
stop:
@docker stop container-name || true && docker rm container-name || true
.PHONY: run
run:
@docker run -d -e ANOTHER_VAR=bar--name container-name image-name
- .DEFAULT_GOAL . Ранее упоминалось о том, что при запуске make первое правило выбирается по умолчанию. Инструкция .DEFAULT_GOAL позволяет отменить это действие и явно указать цель.
- С помощью .DEFAULT_GOAL вы просто запускаете make для отображения сообщения help каждый новый раз.
Однако каждая новая цель в Makefile должна добавляться новой строкой echo .
Как вам идея о самодокументирующемся сообщении help ?
Меняем цель help соответствующим образом:
.DEFAULT_GOAL := help
.PHONY: help
help: ## отображение данного сообщения help
@awk 'BEGIN \\033[0m\\n\\nTargets:\\n"> /^[a-zA-Z_-]+:.*?##/ < printf " \\033[36m%-10s\\033[0m %s\\n", $$1, $$2 >' $(MAKEFILE_LIST)
Далее добавляем комментарии с помощью тега ## , чтобы вывести их как часть сообщения help :
all: build stop run ## run build -> stop -> run (запуск build -> stop -> run)
.PHONY: build
build: ## build docker image (сборка docker-образа)
@docker build -t image-name . --build-arg ENV_VAR=foo
.PHONY: stop
stop: ## stop running container (остановка запущенного контейнера)
@docker stop container-name || true && docker rm container-name || true
.PHONY: run
run: ## run docker container (запуск docker-контейнера)
@docker run -d -e ANOTHER_VAR=bar--name container-name image-name
В результате получаем отличное самодокументирующееся сообщение help :
$ makeUsage:
make Targets:
help display this help
all run build -> stop -> run
build build docker image
stop stop running container
run run docker container
Уровень 4. “Статья не включает детальный разбор темы!”
Этот уровень предполагает самостоятельное погружение в тему Makefile и расширение знаний на основе качественных обучающих руководств.
Заключение
Даже если вы пишите отличную документацию, Makefile наверняка окажется намного более надежным инструментом. Его преимущество заключается в строгом принципе документирования выполняемых действий.
Если вы участвуете в проектах с открытым ПО, то задействуйте Makefile для определения рабочих процессов разработки.
Послесловие. Текущее состояние Makefile
Makefile появился в 1976 году и с тех пор прошел через взлеты и падения.
Чаще всего Make критикуют за сложный синтаксис. Как следствие, программисты разделились на поклонников и противников Makefile .
Думаю, тут дело вкуса. У каждого есть свои предпочтения. Я вот не вижу смысла загружать еще одну новомодную программу/инструмент/фреймворк/библиотеку по многим другим причинам.
Ведь Makefile “работает”.
- Как написать красивый и информативный README.md
- Слабо решить эти задачи по программированию?
- С этими советами у junior-разработчиков не возникнет проблем на их первой работе
Читайте нас в Telegram, VK и Дзен