Какова цель .PHONY в make-файле?


1739

Что .PHONYзначит в Makefile? Я прошел через это , но это слишком сложно.

Может кто-нибудь объяснить мне это простыми словами?

Ответы:


1947

По умолчанию цели Makefile являются «файловыми целями» - они используются для создания файлов из других файлов. Make предполагает, что его целью является файл, и это делает написание Makefiles относительно простым:

foo: bar
  create_one_from_the_other foo bar

Однако иногда вы хотите, чтобы ваш Makefile запускал команды, которые не представляют физические файлы в файловой системе. Хорошими примерами этого являются общие цели «чистый» и «все». Скорее всего, это не тот случай, но у вас может быть файл с именем cleanв вашем основном каталоге. В таком случае Make будет сбит с толку, потому что по умолчанию cleanцель будет связана с этим файлом, и Make будет запускать его только тогда, когда файл не обновлен по отношению к его зависимостям.

Эти специальные цели называются фальшивыми, и вы можете явно указать Make, что они не связаны с файлами, например:

.PHONY: clean
clean:
  rm -rf *.o

Теперь make cleanбудет работать, как ожидается, даже если у вас есть файл с именем clean.

В терминах Make поддельная цель - это просто цель, которая всегда устарела, поэтому всякий раз, когда вы спрашиваете make <phony_target>, она запускается независимо от состояния файловой системы. Некоторые общие makeцели, которые часто фальшиво являются: all, install, clean, distclean, TAGS, info, check.


49
@eSKay: «почему это называется« фальшивым »? - потому что это не настоящая цель. То есть имя цели не является файлом, который создается командами этой цели.
Бернард

97
@Lazer: я не знаю, являетесь ли вы носителем английского языка. Я не. Слово «фальшивый» не означает, как оно звучит. en.wiktionary.org/wiki/phony говорит: Мошенничество; не настоящие; иметь обманчивую внешность.
Bahbar

57
Этот ответ не является полным, хотя он может быть рассмотрен в связанном руководстве. .PHONY принудительно создает метку / файл в Makefile, если он является частью топологической сортировки того, что является вашей целью. То есть, если у вас есть метка 'cleanup:', которая установлена ​​как фальшивая, и ваша метка установки определена с помощью cleanup в качестве предварительного условия - то есть 'install: cleanup', то очистка всегда будет выполняться, когда Makefile пытается собрать «установить». Это полезно для шагов, которые вы всегда хотите сделать, независимо от того, успешны ли они - он будет игнорировать временные метки и просто заставлять их.
synthesizerpatel

9
Обратите внимание, что вам не нужно использовать .PHONY, если у вас нет файла с тем же именем, что и у задачи. Задача всегда будет выполняться в любом случае, и Makefile будет более читабельным.
Бернард

15
«фальшивая цель - это просто цель, которая всегда устарела» - отличное объяснение!
Даниэль

731

Предположим, у вас есть installцель, что очень часто встречается в make-файлах. Если вы не используете .PHONY, и файл с именем installсуществует в том же каталоге, что и Makefile, то make installничего не будет делать . Это потому, что Make интерпретирует правило как «выполнить такой-то рецепт для создания файла с именем install». Поскольку файл уже существует и его зависимости не изменились, ничего не будет сделано.

Однако, если вы installсоздадите целевой PHONY, он сообщит инструменту make, что цель вымышленная, и что make не должна ожидать, что он создаст фактический файл. Следовательно, он не будет проверять, installсуществует ли файл, а это означает, что: a) его поведение не будет изменено, если файл существует, и b) extra stat()не будет вызываться.

Обычно все цели в вашем Makefile, которые не создают выходной файл с тем же именем, что и имя цели, должны быть PHONY. Это , как правило , включает в себя all, install, clean, distclean, и так далее.


6
@PineappleUndertheSea Принятый ответ был значительно улучшен по сравнению с первоначальным уровнем бесполезности и теперь так же хорош, как и этот. Мне пришлось просмотреть историю изменений, чтобы понять ваш комментарий.
Марк Амери

2
Это кажется бессмысленным, поскольку в моей кодовой базе у меня никогда не будет файлов с именем 'install' или подобных. У большинства файлов будет расширение файла, а файлы без расширения обычно пишутся заглавными буквами, например «README». Опять же, если у вас есть bash-скрипт с именем «install» вместо «install.sh», у вас будут плохие времена.
ядерный тренд

6
@JasonTu Это не обязательно так. Соглашения Bash-сценариев просят вас не указывать расширение .shили .bashдля «программ», которые работают так, как будто они имеют основную функцию, и оставляют за собой добавление расширения для включаемых вами библиотек ( source mylib.sh). На самом деле, я добрался до этого ТАКОГО вопроса, потому что у меня был скрипт в той же директории, что и мой Makefile, под названиемinstall
Kyle

10
@ Кайл Да, я не уверен, что означало мое прошлое. В эти дни я использую .PHONYвсе время ...
Nutitide

2
@JasonTu Решение здесь простое: создайте машину времени и «замените» свое прошлое. Я рекомендую взять с собой лопату, чтобы никто не понял, что вы .PHONYверсия.
Матин Улхак

121

ПРИМЕЧАНИЕ . Инструмент make считывает make-файл и проверяет метки времени изменения файлов по обе стороны от символа «:» в правиле.

пример

В каталоге «test» присутствуют следующие файлы:

prerit@vvdn105:~/test$ ls
hello  hello.c  makefile

В makefile правило определяется следующим образом:

hello:hello.c
    cc hello.c -o hello

Теперь предположим, что файл 'hello' представляет собой текстовый файл, содержащий некоторые данные, которые были созданы после файла 'hello.c'. Таким образом, отметка времени модификации (или создания) 'hello' будет новее, чем у 'hello.c'. Поэтому, когда мы вызовем команду 'make hello' из командной строки, она напечатает как:

make: `hello' is up to date.

Теперь откройте файл hello.c и вставьте в него пробелы, которые не влияют на синтаксис или логику кода, затем сохраните и закройте. Теперь отметка времени модификации hello.c новее, чем у 'hello'. Теперь, если вы вызовете 'make hello', он выполнит команды следующим образом:

cc hello.c -o hello

И файл 'hello' (текстовый файл) будет перезаписан новым двоичным файлом 'hello' (результат вышеуказанной команды компиляции).

Если мы используем .PHONY в make-файле следующим образом:

.PHONY:hello

hello:hello.c
    cc hello.c -o hello

и затем вызывает 'make hello', он игнорирует любой файл, присутствующий в pwd 'test', и выполняет команду каждый раз.

Теперь предположим, что у цели 'hello' нет объявленных зависимостей:

hello:
    cc hello.c -o hello

и файл 'hello' уже присутствует в pwd 'test', тогда 'make hello' всегда будет отображаться как:

make: `hello' is up to date.

3
Это не только делает понятными команды, которые я запускаю, но и, наконец, makeделает смысл в целом, это все о файлах! Спасибо за этот ответ.
Kzqai

Вот что простое правило , выглядит следующим образом : `` `целевая: зависимость ... команда ...` `` Ref: gnu.org/software/make
VicX

80
.PHONY: install
  • означает, что слово «установить» не представляет имя файла в этом Makefile;
  • означает, что Makefile не имеет ничего общего с файлом «install» в том же каталоге.

45

Это цель сборки, которая не является именем файла.


33

Лучшим объяснением является само руководство по GNU make: 4.6 раздел Phony Targets .

.PHONYявляется одним из специальных встроенных целевых имен make . Есть и другие цели, которые могут вас заинтересовать, поэтому стоит просмотреть эти ссылки.

Когда пришло время рассмотреть цель .PHONY, make запустит свой рецепт безоговорочно, независимо от того, существует ли файл с таким именем или каково его время последней модификации.

Вы также можете быть заинтересованы в стандартных целях марки, таких как allи clean.


11

Есть также один важный хитрый прием «.PHONY» - когда физическая цель зависит от фальшивой цели, которая зависит от другой физической цели:

TARGET1 -> PHONY_FORWARDER1 -> PHONY_FORWARDER2 -> TARGET2

Вы просто ожидаете, что если вы обновили TARGET2, то TARGET1 следует считать устаревшим по сравнению с TARGET1, поэтому TARGET1 следует перестроить. И это действительно работает таким образом .

Самое сложное, когда TARGET2 не устарел против TARGET1 - в этом случае следует ожидать, что TARGET1 не следует перестраивать.

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

Рассматривать:

all: fileall

fileall: file2 filefwd
    echo file2 file1 >fileall


file2: file2.src
    echo file2.src >file2

file1: file1.src
    echo file1.src >file1
    echo file1.src >>file1

.PHONY: filefwd
.PHONY: filefwd2

filefwd: filefwd2

filefwd2: file1
    @echo "Produced target file1"


prepare:
    echo "Some text 1" >> file1.src
    echo "Some text 2" >> file2.src

Вы можете поиграть с этим:

  • сначала сделайте 'make prepare', чтобы подготовить "исходные файлы"
  • поиграйте с этим, коснувшись определенных файлов, чтобы увидеть их обновленными

Вы можете видеть, что fileall зависит от file1 косвенно через фальшивую цель - но он всегда перестраивается из-за этой зависимости. Если вы измените зависимость в fileallс filefwdна file, то теперь fileallне будет перестраиваться каждый раз, а только тогда, когда какая-либо из зависимых целей устарела как файл.


5

Специальная цель .PHONY:позволяет объявлять фиктивные цели, поэтому makeони не будут проверяться как настоящие имена файлов: она будет работать постоянно, даже если такие файлы еще существуют.

Вы можете поместить несколько .PHONY:в свой Makefile:

.PHONY: all

all : prog1 prog2

...

.PHONY: clean distclean

clean :
    ...
distclean :
    ...

Есть еще один способ объявить фиктивные цели: просто введите '::'

all :: prog1 prog2

...

clean ::
    ...
distclean ::
    ...

'::' имеет особое значение: цели являются фальшивыми, и они могут появляться несколько раз:

clean ::
    rm file1

...


clean ::
    rm file2

Командные блоки будут вызываться один за другим.


3

Я часто использую их, чтобы сказать, что цель по умолчанию не стрелять.

superclean: clean andsomethingelse

blah: superclean

clean:
   @echo clean

%:
   @echo catcher $@

.PHONY: superclean

Без фальшиво, make supercleanбудет стрелять clean, andsomethingelseи catcher superclean; но с PHONY, make supercleanне уволитcatcher superclean .

Нам не нужно беспокоиться о том, чтобы сказать, что cleanцель - это ФОНИ, потому что она не совсем фальшивая. Хотя он никогда не производит чистый файл, у него есть команды для запуска, поэтому make будет думать, что это конечная цель.

Тем не менее, supercleanцель на самом деле является фальшивой, поэтому make попытается объединить ее с чем-то еще, что обеспечивает supercleanцели для цели - это включает в себя другие supercleanцели и %цель.

Обратите внимание, что мы ничего не говорим о andsomethingelseили blah, поэтому они явно идут в ловушку.

Вывод выглядит примерно так:

$ make clean
clean

$ make superclean
clean
catcher andsomethingelse

$ make blah 
clean
catcher andsomethingelse
catcher blah
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.