Список целей / задач в GNU make, которые содержат переменные в своем определении

100

У меня есть довольно большой make-файл, который на лету создает несколько целей, вычисляя имена из переменных. (например, foo $ (VAR): $ (PREREQS)). Есть ли способ убедить gnu make выдать список целей после расширения этих переменных?

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

BitShifter
источник

Ответы:

79

Можете ли вы проанализировать вывод make -pn(т.е. make --print-data-base --dry-run)? Он распечатывает все переменные, правила, неявные правила и команды, которые будут выполняться, с кропотливой детализацией.

Джек Келли
источник
Этот ответ выглядит намного лучше, чем другой.
Мэтт Джойнер
Согласен, это намного приятнее. Еще больше работы, чем я надеялся, но я собираюсь отметить это как ответ, поскольку ничто другое не кажется разумным, а GNU make не имеет удобного флага для того, что я хочу.
BitShifter
6
Имейте в виду, что результат может зависеть от указанной вами цели, если ваш Makefile генерирует цели динамически. Вывод также может зависеть от значений переменных среды или переменных make, указанных с помощью команды make.
Reinierpost
6
Мне нравится краткость. +1 Добавлен мой собственный спин (описан в ответе ниже):make -pn | sed -rn '/^[^# \t\.%].*:[^=]?/p'
Джейми
Предложите make -pnr. -rУдаляет скрытые правила.
sjbx
114
make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'     

Взято из завершения make arg, которое работает как шарм.

Тодд Ходес
источник
2
Я попытался создать для этого псевдоним, заключив его в двойные кавычки, но это просто приводит к тому, что awk имеет синтаксическую ошибку в первой запятой ... какие-либо мысли о том, как правильно избежать этого для псевдонима оболочки (bash)?
drfrogsplat
3
Я думаю, это как-то связано с тем, где это определено. Я думаю, что если он определен как псевдоним, его рабочий каталог становится каталогом, в котором он определен, и, очевидно, часть awk не любит точку в скрытых каталогах. Определение функции работает.
Ибрагим
Я просто поместил его в сценарий оболочки, ~/binа не использовал псевдоним. Прекрасно работает; Спасибо!
mpontillo
3
Псевдоним, alias mkl="make -qp | awk -F':' '/^[a-zA-Z0-9][^\$#\/\t=]*:([^=]|\$)/ {split(\$1,A,/ /);for(i in A)print A[i]}' | sort"который сэкономит вам 2 минуты цитирования игр :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
| sort -uвыглядит полезнее :)
шерпя
14

Я не уверен, что это только gnu make, но это хорошо работает:

make help

Зденик
источник
Это довольно удобно, гораздо проще запомнить, чем настраиваемый псевдоним для одного из других методов.
Ибрагим
8
На самом деле не работает над GNU Make 3.81, вероятно, цель в ваших make-файлах: "make: *** Нет правила, чтобы сделать
цель`
8
Это просто вызывает цель "help" в Makefile. Если он существует, он что-то напечатает (не полные списки целей), если он не существует (типичная ситуация), он выйдет из строя.
pfalcon
2
Хорошая новость заключается в том, что cmake, похоже, генерирует приятную, легко читаемую цель «помощи».
Стефан
Это круто! :) Go cmake
Jeef
10

Несколько респондентов предложили использовать make -pn, который распечатает базу данных правил, но ничего не выполнит - более или менее. Проблема с этим подходом заключается в том, что -nон по-прежнему вызывает все рекурсивные make, и он по-прежнему выполняет гораздо больше работы, чем необходимо, потому что он распечатывает каждую команду, которую он вызвал бы в обычной сборке. Более эффективным решением было бы создание тривиального make-файла dummy.mk с таким содержимым:

__all_targets__: ; #no-op

Теперь вызываем make as make -p -f Makefile -f dummy.mk __all_targets__. При любой существенной сборке разница в объеме вывода, генерируемого make, значительна. Например:

$ gmake -pn | wc
 138985 2632330 69612711
$ gmake -f Makefile -f /tmp/dummy.mk -pn __all_targets__ | wc
  21673   93437  878985

Время выполнения также было значительно лучше - 2,063 с для первой версии, 0,059 с для второй.

Эрик Мельски
источник
1
Отличная идея, ее оценят все, кто работает с огромными системами сборки. Статистика для Android Gingerbread tree (не использует рекурсивный make, поэтому экономия небольшая, просто хорошо): " time make -pn | wc -l": 1338738 real 0m47.754s." time make -f Makefile -f dummy.mk -pn __all_targets__ | wc -l": 938621 real 0m40.219s.
pfalcon
Хорошая точка зрения. Справочная страница для, makeкажется, предлагает нечто похожее, но дружелюбное, а именноmake -p -f/dev/null
Davide
@Davide, который не совсем сработает: указание -f /dev/nullпредотвратит makeсинтаксический анализ обычных make-файлов, поэтому вы получите распечатку только встроенных правил. Вот почему мое решение указывает -f Makefile -f dummy.mk. Но и этого недостаточно, потому что with -n, makeпродолжит выполнение и также фактически начнет выполнять правила в make-файле. К сожалению, мне не известны какие-либо модификации, которые могут упростить решение в том виде, в котором оно было представлено изначально.
Эрик Мельски
Конечно ты прав. Однако на странице руководства make есть ошибка, потому что в ней говорится: «Чтобы распечатать базу данных, не пытаясь переделать какие-либо файлы, используйте make -p -f / dev / null»
Davide
Небольшая поправка к моему предыдущему комментарию: он должен читать «... потому что без -n ...»
Эрик Мельски
9

Проверьте завершение bash для make на GitHub.

js.
источник
Ссылка, которую вы предоставили, теперь не работает
Davide
Обновлена ​​ссылка на текущую версию.
js.
7

Изменить: к вашему сведению, репозиторий git завершения debian bash в другом ответе теперь включает расширенную версию этого сценария, адаптированную для случаев использования завершения bash.

#!/bin/bash

SCRIPT='
  /^# Make data base/,/^# Files/d             # skip until files section
  /^# Not a target/,+1          d             # following target isnt
  /^\.PHONY:/                   d             # special target
  /^\.SUFFIXES:/                d             # special target
  /^\.DEFAULT:/                 d             # special target
  /^\.PRECIOUS:/                d             # special target
  /^\.INTERMEDIATE:/            d             # special target
  /^\.SECONDARY:/               d             # special target
  /^\.SECONDEXPANSION/          d             # special target
  /^\.DELETE_ON_ERROR:/         d             # special target
  /^\.IGNORE:/                  d             # special target
  /^\.LOW_RESOLUTION_TIME:/     d             # special target
  /^\.SILENT:/                  d             # special target
  /^\.EXPORT_ALL_VARIABLES:/    d             # special target
  /^\.NOTPARALLEL:/             d             # special target
  /^\.ONESHELL:/                d             # special target
  /^\.POSIX:/                   d             # special target
  /^\.NOEXPORT:/                d             # special target
  /^\.MAKE:/                    d             # special target

# The stuff above here describes lines that are not
#  explicit targets or not targets other than special ones
# The stuff below here decides whether an explicit target
#  should be output.

  /^[^#\t:=%]+:([^=]|$)/ {                    # found target block
    h                                         # hold target
    d                                         # delete line
  }
  /^# File is an intermediate prerequisite/ { # nope
    s/^.*$//;x                                # unhold target
    d                                         # delete line
  }
  /^([^#]|$)/ {                               # end of target block
    s/^.*$//;x                                # unhold target
    s/:.*$//p                                 # write current target
    d                                         # hide any bugs
  }
'

make -npq .DEFAULT 2>/dev/null | sed -n -r "$SCRIPT" \
  | sort | uniq

Это гораздо более полный сценарий, чем сценарий завершения debian bash, потому что он предоставляет результаты для сгенерированных правил, о которых и спрашивает вопрос, а ответ, получивший наибольшее количество голосов (сценарий завершения debian bash на сервере debian git), не подходит.

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

кодшот
источник
Кстати, моды, если этот ответ не полностью соответствует всем политикам из-за нюансов его текста, пожалуйста, прокомментируйте перед удалением. Я внесу все необходимые законные изменения, чтобы на этот вопрос действительно был дан полный и обоснованный ответ.
codehot
В качестве быстрого теста я взял исходный код gcc и запустил ./configure && time ~ / makecomp.sh | wc -l, где мой ответ - ~ / makecomp.sh. ... настроить вывод ... config.status: создание Makefile 3312 real 0m0.401s user 0m0.412s sys 0m0.028s
codehot
В качестве еще одного теста я использовал демонстрационный make-файл по адресу codehot.blogspot.co.uk/2012/08/…, который генерирует псевдоправила для создания и проверки проходов модульного теста. В этом примере используется больше функций make, чем в типичном кроссплатформенном проекте autoconf, поэтому сценарий в этом ответе возвращает 14 реальных целей, в то время как завершение bash для make в ubuntu возвращает только два полезных правила и затем 5 исходных файлов.
codehot
4

Это код для псевдонима на основе решения Тодда Ходса.

alias mtargets='make -qp | awk -F":" "/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split(\$1,A,/ /);for(i in A)print A[i]}"'
Нгуен Минь Во
источник
3

Это даст хороший результат:

make -pn | perl -F: -ane 'print "$F[0]\n" if /^\w+\s*:/' | sort
Андор
источник
2

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

make -qp | grep -v '^# ' | grep -v '^[[:space:]]' | grep --only-matching '^.*:'

Это в основном работает, хотя может включать некоторые нецелевые вещи, такие как vpathдирективы. Если вы не зависите от makeвстроенных правил, вы можете использовать их make -qpRкак первую команду в конвейере.

DGrady
источник
Я пробовал это с freetz (.org). Перечисляет множество включенных make-файлов и не перечисляет все цели.
Роберт Симер 02
1

Рубиновый раствор:

`make -qp`.split("\n").select{|l| l =~ /^\w/ && l !~ /=/}.map{|l| l.sub(/:.*/,'')}
Gdub
источник
1

Я работаю над Solaris 10 и Turbo C. Данное решение не работает для моего проекта makefile. даже после изменения синтаксиса командной строки выше на tcsh. Однако я обнаружил, что вы можете получить простое решение, используя

remake --tasks | grep -v "clean some static output or just grep tabs on start of line" | awk ´{print $1}´

переделанная версия:

remake --version

является

GNU Make 3.82+dbg0.8
Built for sparc-sun-sparc2-9

и некоторые другие неважные данные о версии

Aviv
источник
0

Я искал тот же вопрос и придумал такой вариант:

make -pn | sed -rn '/^[^# \t\.%].*:/p'

Это удаляет все строки комментариев, правила шаблонов (строки, начинающиеся с табуляции), все встроенные функции (пример .c.oи %.o: %.cшаблоны).

Джейми
источник
не читал комментарий к принятому ответу: +1 за ответ Джека ... gist.github.com/777954 - pvandenberk 13 янв в 14:47
Джейми
0

Нашел это решение в другом потоке:

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

Вы также можете добавить его в свой Makefile:

list:
    sh -c "cat Makefile | egrep \"^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$\\""

И выполнить make list.

это
источник
-1

Конечно, но когда вы хотите, чтобы он их выплюнул?

Чтобы сообщить имя цели при выполнении правила, поместите строку в правило:

foo$(VAR): $(PREREQS)
    @echo now making the foo target: $@
    do_other_stuff...

Чтобы выплюнуть их всех сразу, вы можете создать отдельную цель PHONY:

.PHONY: show_vars
show_vars:
    @echo foo$(VAR)
    @echo bar$(PARAM) blah$(FLAG)
    # and so on

И это можно сделать предпосылкой для вашей цели по умолчанию:

all: show_vars
    ...

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

Чтобы сделать это точно и иметь возможность справляться со сложными make-файлами, например, с использованием правил, построенных с помощью evalоператоров, вам нужно будет написать что-то похожее на эмулятор Make. Непрактично.

Чтобы увидеть цели простых правил, вы можете написать make-файл, который будет действовать как сканер make-файла, работая с произвольным make-файлом:

  1. Получите все целевые имена из make-файла с помощью sed.
  2. `include` make-файл, чтобы использовать его для раскрытия переменных.
  3. Используйте `show_%:; echo $$ * `для печати всех целей

Это была бы впечатляющая работа. Вы уверены, что цель стоит затраченных усилий?

Бета
источник
Не совсем то, что я хочу. Я хотел бы иметь возможность заставить make выдавать список целей для произвольного make-файла, который у меня может быть. Я пытаюсь написать функцию завершения аргумента для своей оболочки, и я не могу полагаться на то, что make-файлы имеют какую-либо разумную помощь.
BitShifter,
-4
grep ^[a-z].*\:$ Makefile | sed s,:,,g
Виллиан Брага
источник
1
-1: этот подход дает ложные срабатывания даже для простых назначений переменных ( :=).
Thiton
Вы должны были сначала проверить это. Но хорошо, что ты попробовал.
Конрад Вильтерстен