Как вы получаете список целей в make-файле? - PullRequest
164 голосов
/ 18 ноября 2010

Я немного использовал rake (программа Ruby make), и у него есть возможность получить список всех доступных целей, например

> rake --tasks
rake db:charset      # retrieve the charset for your data...
rake db:collation    # retrieve the collation for your da...
rake db:create       # Creates the databases defined in y...
rake db:drop         # Drops the database for your curren...
...

но, похоже, нет возможности сделать это в GNU make.

Видимо, код почти готов для этого, по состоянию на 2007 год - http://www.mail-archive.com/help-make@gnu.org/msg06434.html.

В любом случае, я немного взломал, чтобы извлечь цели из make-файла, который можно включить в make-файл.

list:
    @grep '^[^#[:space:]].*:' Makefile

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

> make list
list:
copy:
run:
plot:
turnin:

Ответы [ 13 ]

156 голосов
/ 15 мая 2014

В Bash (как минимум) это можно сделать автоматически с завершением табуляции:

make(space)(tab)(tab)
95 голосов
/ 13 октября 2014

Это попытка улучшить @ замечательный подход nobar следующим образом:

  • использует более надежную команду для извлечения целевых имен, которая, как мы надеемся, предотвращает любые ложные срабатывания (а также устраняет ненужные sh -c)
  • не всегда нацеливается на make-файл в каталоге current ; уважает make-файлы, явно указанные с -f <file>
  • исключает скрытые цели - по договоренности, это цели, имя которых не начинается ни с буквы, ни с цифры
  • справляется с одиночной фальшивой целью
  • префикс команды с @, чтобы предотвратить ее отображение перед выполнением

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

Поместите следующее правило в make-файл для GNU make, чтобы реализовать цель с именем list, которая просто перечисляет все имена целей в алфавитном порядке, т.е. вызывается как make list:

.PHONY: list
list:
    @$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$'

Важно : При вставке этого, убедитесь, что последняя строка имеет отступ ровно на 1 фактический символ табуляции. (пробелы делают не работают ) .

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

Объяснение правила :

  • . PHONY: list
    • объявляет список целей фальшивой целью, то есть один не , ссылающийся на файл , поэтому его рецепт должен вызываться безоговорочно
  • $(MAKE) -prRn -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null

    • Вызывает make снова, чтобы распечатать и проанализировать базу данных, полученную из make-файла:
      • -p печатает базу данных
      • -Rr подавляет включение встроенных правил и переменных
      • -q только проверяет текущее состояние цели (не переделывая ничего), но это само по себе не препятствует выполнению команд рецепта во всех случаях; следовательно:
      • -f $(lastword $(MAKEFILE_LIST)) гарантирует, что тот же make-файл нацелен на тот же, что и в исходном вызове, независимо от того, был ли он нацелен неявно или явно с помощью -f ....
        Предостережение : Это сломается, если ваш make-файл содержит директивы include; чтобы решить эту проблему, определите переменную THIS_FILE := $(lastword $(MAKEFILE_LIST)) перед любыми директивами include и используйте вместо нее -f $(THIS_FILE).
      • : является намеренно недопустимым целевым объектом , что означает , гарантирующее, что никакие команды не выполняются ; 2>/dev/null подавляет полученное сообщение об ошибке. Примечание. Тем не менее, это зависит от -p печати базы данных, что относится к GNU make 3.82. К сожалению, GNU make не предлагает прямой опции просто распечатать базу данных, без также выполнения задачи по умолчанию (или заданной); если вам не нужен целевой Makefile, вы можете использовать make -p -f/dev/null, как рекомендовано на странице man.
  • -v RS=

    • Это идиома awk, которая разбивает ввод на блоки смежных непустых строк.
  • /^# File/,/^# Finished Make data base/
    • Соответствует диапазону строк в выходных данных, которые содержат все цели (истинно для GNU make 3.82) - ограничивая анализ этим диапазоном, нет необходимости иметь дело с ложными срабатываниями из других выходных секций.
  • if ($$1 !~ "^[#.]")
    • Выборочно игнорирует блоки:
      • # ... игнорирует нецелевые объекты, чьи блоки начинаются с # Not a target:
      • . ... игнорирует специальные цели
    • Все остальные блоки должны начинаться со строки, содержащей только имя явно определенной цели, за которой следует :
  • egrep -v -e '^[^[:alnum:]]' -e '^$@$$' удаляет нежелательные цели из вывода:
    • '^[^[:alnum:]]' ... исключает скрытые цели, которые, по соглашению, являются целями, которые не начинаются ни с буквы, ни с цифры.
    • '^$@$$' ... исключая list саму цель

Запуск make list затем печатает все цели, каждая на своей линии; вы можете направить на xargs вместо списка, разделенного пробелами.

24 голосов
/ 25 февраля 2013

Я объединил эти два ответа: https://stackoverflow.com/a/9524878/86967 и https://stackoverflow.com/a/7390874/86967 и немного сбежал, чтобы это можно было использовать из make-файла.

.PHONY: no_targets__ list
no_targets__:
list:
    sh -c "$(MAKE) -p no_targets__ | awk -F':' '/^[a-zA-Z0-9][^\$$#\/\\t=]*:([^=]|$$)/ {split(\$$1,A,/ /);for(i in A)print A[i]}' | grep -v '__\$$' | sort"

.

$ make -s list
build
clean
default
distclean
doc
fresh
install
list
makefile ## this is kind of extraneous, but whatever...
run
19 голосов
/ 05 января 2017

Это, очевидно, не будет работать во многих случаях, но если ваш Makefile был создан CMake, вы можете запустить make help.

$ make help
The following are some of the valid targets for this Makefile:
... all (the default if no target is provided)
... clean
... depend
... install
etc
13 голосов
/ 27 июня 2014

Если у вас установлено завершение bash для make, сценарий завершения определит функцию _make_target_extract_script.Эта функция предназначена для создания сценария sed, который можно использовать для получения целей в виде списка.

Используйте его так:

# Make sure bash completion is enabled
source /etc/bash_completion 

# List targets from Makefile
sed -nrf <(_make_target_extract_script --) Makefile
9 голосов
/ 23 августа 2017

Когда mklement0 указывает на , в GNU-make отсутствует функция для перечисления всех целей Makefile, и его ответ и другие приводят способы сделать это.

Однако в оригинальном посте также упоминается rake , чей переключатель tasks делает что-то немного другое, чем просто перечисление всех задач в rakefile. Rake предоставит вам только список задач, с которыми связаны описания. Задачи без описания не будут перечислены . Это дает автору возможность как предоставлять настраиваемые описания справки, так и опускать справку для определенных целей.

Если вы хотите эмулировать поведение рейка, где вы предоставляете описания для каждой цели , для этого есть простой метод: вставлять описания в комментарии для каждой цели, которую вы хотите перечислить.

Вы можете поместить описание рядом с целью или, как я часто это делаю, рядом со спецификацией PHONY над целью, например:

.PHONY: target1 # Target 1 help text
target1: deps
    [... target 1 build commands]

.PHONY: target2 # Target 2 help text
target2:
    [... target 2 build commands]

...                                                                                                         

.PHONY: help # Generate list of targets with descriptions                                                                
help:                                                                                                                    
    @grep '^.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/\1 \2/' | expand -t20

Который даст

$ make help
target1             Target 1 help text
target2             Target 2 help text

...
help                Generate list of targets with descriptions

Вы также можете найти пример короткого кода в этой сущности и здесь тоже.

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

Однако, если вы пишете Makefile и хотите получить способ создания справочного текста согласованным, самодокументированным способом, этот метод может быть полезен.

4 голосов
/ 13 октября 2014

@ nobar's answer полезно показать, как использовать завершение табуляции для вывода списка целей make-файла .

  • Это прекрасно работает для платформ, которые предоставляют эту функциональность по умолчанию (например, Debian, Fedora ).

  • На других платформах (например, Ubuntu ) вы должны явно загрузить эту функциональность, как подразумевается в ответе @ hek2mgl :

    • . /etc/bash_completion устанавливает несколько функций завершения табуляции, в том числе для make
    • В качестве альтернативы, для установки только завершение вкладки для make:
      • . /usr/share/bash-completion/completions/make
  • Для платформ, которые не предоставляют этой функциональности вообще , таких как OSX , вы можете получить следующие команды (адаптировано из здесь ) для его реализации:
_complete_make() { COMPREPLY=($(compgen -W "$(make -pRrq : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($1 !~ "^[#.]") {print $1}}' | egrep -v '^[^[:alnum:]]' | sort | xargs)" -- "${COMP_WORDS[$COMP_CWORD]}")); }
complete -F _complete_make make
  • Примечание: это не так сложно, как функциональность дополнения табуляции, которая поставляется с дистрибутивами Linux: наиболее примечательно, что неизменно нацелен на make-файл в текущем каталоге , даже если командная строка предназначается для другого make-файла с -f <file>.
1 голос
/ 28 августа 2018

Это модификация очень полезного ответа jsp (https://stackoverflow.com/a/45843594/814145). Мне нравится идея получить не только список целей, но и их описания. jsp * 1006 Makefile * помещает описание в качестве комментария, который, как я обнаружил, часто повторяется в команде echo для описания цели, поэтому вместо этого я извлекаю описание из команды echo для каждой цели.

Пример Makefile:

.PHONY: all
all: build
        : "same as 'make build'"

.PHONY: build
build:
        @echo "Build the project"

.PHONY: clean
clean:
        @echo "Clean the project"

.PHONY: help
help:
        @echo -n "Common make targets"
        @echo ":"
        @cat Makefile | sed -n '/^\.PHONY: / h; /\(^\t@*echo\|^\t:\)/ {H; x; /PHONY/ s/.PHONY: \(.*\)\n.*"\(.*\)"/    make \1\t\2/p; d; x}'| sort -k2,2 |expand -t 20

Выход make help:

$ make help
Common make targets:
    make all        same as 'make build'
    make build      Build the project
    make clean      Clean the project
    make help       Common make targets

Примечания:

  • То же, что и ответ jsp , могут быть указаны только цели PHONY, которые могут или не могут работать в вашем случае
  • Кроме того, он перечисляет только те цели PHONY, которые имеют команду echo или : в качестве первой команды рецепта. : означает «ничего не делать». Я использую его здесь для тех целей, которые не нуждаются в эхосигнале, например, all цель выше.
  • Для цели help есть дополнительный трюк для добавления ":" в вывод make help.
1 голос
/ 19 апреля 2018

Здесь много работоспособных решений, но, как мне нравится говорить, «если это стоит сделать один раз, то стоит сделать снова».Я высказал предположение о предложении использования (вкладка) (вкладка), но, как некоторые отмечали, у вас может не быть поддержки завершения, или, если у вас много включаемых файлов, вы можете захотеть более простой способ узнать, где определена цель.

Я не проверял нижеприведенное с подмодами ... Я думаю, что это не сработает.Как известно, рекурсивный делает вредным.

.PHONY: list ls
ls list :
    @# search all include files for targets.
    @# ... excluding special targets, and output dynamic rule definitions unresolved.
    @for inc in $(MAKEFILE_LIST); do \
    echo ' =' $$inc '= '; \
    grep -Eo '^[^\.#[:blank:]]+.*:.*' $$inc | grep -v ':=' | \
    cut -f 1 | sort | sed 's/.*/  &/' | sed -n 's/:.*$$//p' | \
    tr $$ \\\ | tr $(open_paren) % | tr $(close_paren) % \
; done

# to get around escaping limitations:
open_paren := \(
close_paren := \)

Что мне нравится, потому что:

  • выводит список целей по включаемому файлу.
  • выводит необработанные динамические определения целевых объектов (заменяет переменные-разделители по модулю)
  • выводит каждую цель на новой строке
  • кажется более понятным (субъективное мнение)

Объяснение:

  • foreach-файл в MAKEFILE_LIST
  • выводит имя файла
  • строки grep, содержащие двоеточие, которые не имеют отступов, не являются комментариями и не начинаются с точки
  • исключают выражения непосредственного присваивания (: =)
  • зависимости правил вырезания, сортировки, отступа и нарезки (после двоеточия)
  • разделители переменных munge для предотвращения расширения

Пример вывода:

 = Makefile = 
  includes
  ls list
 = util/kiss/snapshots.mk = 
  rotate-db-snapshots
  rotate-file-snapshots
  snap-db
  snap-files
  snapshot
 = util/kiss/main.mk = 
  dirs
  install
   %MK_DIR_PREFIX%env-config.php
   %MK_DIR_PREFIX%../srdb
1 голос
/ 19 сентября 2017

Это далеко не чисто, но сделал работу, для меня.

make -p 2&>/dev/null | grep -A 100000 "# Files" | grep -v "^$" | grep -v "^\(\s\|#\|\.\)" | grep -v "Makefile:" | cut -d ":" -f 1

Я использую make -p, который создает дамп внутренней базы данных, ditch stderr, использует быстрый и грязный grep -A 100000, чтобы сохранитьнижняя часть вывода.Затем я очищаю вывод парой grep -v и, наконец, использую cut, чтобы получить то, что находится перед двоеточием, а именно цели.

Этого достаточно для моих вспомогательных сценариев на большинстве моих Makefile.

РЕДАКТИРОВАТЬ: добавлено grep -v Makefile, что является внутренним правилом

...