Как вы получаете список целей в make файле?

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

> 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/[email protected]/msg06434.html.

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

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

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

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

Ответ 1

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

  • использует более надежную команду для извлечения целевых имен, которая, как мы надеемся, предотвращает любые ложные срабатывания (а также устраняет ненужные sh -c)
  • не всегда нацеливается на make файл в текущем каталоге; уважает make- -f <file> явно указанные в -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 '^[email protected]$$'

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

Обратите внимание, что сортировка результирующего списка целей является наилучшим вариантом, поскольку не сортировка не приводит к полезному упорядочению в том порядке, в котором порядок появления целей в make файле не сохраняется.
Кроме того, подцели правила, содержащего несколько целей, неизменно выводятся по отдельности и, следовательно, вследствие сортировки обычно не появляются рядом друг с другом; Например, правило, начинающееся с az: не будет иметь цели 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, которая разбивает ввод на блоки смежных не -e строк mpty.
  • /^# File/,/^# Finished Make data base/
    • Соответствует диапазону строк в выводе, который содержит все цели (истинно на GNU make 3.82) - ограничивая анализ этим диапазоном, нет необходимости иметь дело с ложными срабатываниями из других выходных секций.
  • if ($$1 !~ "^[#.]")
    • Выборочно игнорирует блоки:
      • #... игнорирует не цели, чьи блоки начинаются с # Not a target:
      • .... игнорирует специальные цели
    • Все остальные блоки должны начинаться со строки, содержащей только имя явно определенной цели, за которой следует :
  • egrep -v -e '^[^[:alnum:]]' -e '^[email protected]$$' удаляет нежелательные цели из вывода:
    • '^[^[:alnum:]]'... исключает скрытые цели, которые - по договоренности - являются целями, которые не начинаются ни с буквы, ни с цифры.
    • '^[email protected]$$'... исключает саму цель list

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

Ответ 2

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

make space tab tab

Ответ 3

Я объединил эти два ответа: fooobar.com/questions/52739/... и fooobar.com/questions/52739/... и сделал некоторое экранирование, чтобы это можно было использовать изнутри 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

Ответ 4

Это, очевидно, не будет работать во многих случаях, но если ваш 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

Ответ 5

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

Используйте его следующим образом:

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

# List targets from Makefile
sed -nrf <(_make_target_extract_script --) Makefile

Ответ 6

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

Однако в оригинальном посте также упоминается rake, чей переключатель задач делает что-то немного другое, чем просто перечисление всех задач в файле 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 и вам нужен способ генерировать текст справки согласованным, самодокументируемым способом, этот метод может оказаться полезным.

Ответ 7

@nobar answer показывает, как использовать вкладку завершения для отображения целей makefile.

  • Это отлично работает для платформ, которые предоставляют эту функциональность по умолчанию (например, 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 файл в текущем каталоге, даже если командная строка нацелена на другую makefile с -f <file>.

Ответ 8

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

cat Makefile | egrep "^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$"

Ответ 9

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

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, чтобы получить то, что перед двоеточием, а именно цели.

Этого достаточно для моих вспомогательных скриптов на большинстве моих Make файлов.

EDIT: добавлено grep -v Makefile, это внутреннее правило

Ответ 10

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

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

.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

Ответ 11

Это модификация очень полезного ответа jsp (fooobar.com/questions/52738/...). Мне нравится идея получить не только список целей, но и их описание. jsp Makefile помещает описание в качестве комментария, который, как я обнаружил, часто повторяется в команде echo target description. Поэтому вместо этого я извлекаю описание из команды 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; /\(^\[email protected]*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.

Ответ 12

Мой любимый ответ на этот вопрос был опубликован Крисом Дауном в Unix & Linux Stack Exchange. Я процитирую.

Вот как модуль завершения bash для make получает свой список:

make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'

Распечатывает список целей, разделенных новой строкой, без подкачки.

Пользователь Brainstone предлагает подключиться к sort -u, чтобы удалить повторяющиеся записи:

make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}' | sort -u

Источник: Как перечислить все цели в make? (Unix & Linux SE)

Ответ 13

Еще один дополнительный ответ на выше.

протестировано на MacOSX с использованием только cat и awk на терминале

cat Makefile | awk '!/SHELL/ && /^[A-z]/ {print $1}' | awk '{print substr($0, 1, length($0)-1)}'

будет вывод файла make, как показано ниже:

target1

target2

target3

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

объяснение

кот - выплевывает содержимое

| - труба анализирует вывод на следующий awk

awk - запускает регулярное выражение, исключая "shell" и принимая только строки "Az", затем выводит первый столбец за $ 1

awk - еще раз удаляет последний символ ":" из списка

это грубый вывод, и вы можете делать более интересные вещи с помощью только AWK. Старайтесь избегать sed, поскольку он не так согласован в вариантах BSD, т.е. некоторые из них работают на * nix, но не работают на BSD, таких как MacOSX.

Больше

Вы должны иметь возможность добавить это (с изменениями) в файл для make, в папку завершения bash по умолчанию /usr/local/etc/bash-completion.d/, что означает, что когда вы "сделаете вкладку с вкладкой "... она завершит цели, основанные на одном сценарии лайнера.

Ответ 14

Я обычно делаю:

grep install_targets Makefile

Это вернулось бы с чем-то вроде:

install_targets = install-xxx1 install-xxx2 ... etc

Я надеюсь, что это помогает

Ответ 15

не уверен, почему предыдущий ответ был настолько сложным:

list:
    cat Makefile | grep "^[A-z]" | awk '{print $$1}' | sed "s/://g"