Liste metas / alvos no GNU make que contêm variáveis ​​em sua definição

100

Eu tenho um makefile bastante grande que cria vários destinos dinamicamente computando nomes de variáveis. (por exemplo, foo $ (VAR): $ (PREREQS)). Existe alguma maneira de o gnu make ser convencido a cuspir uma lista de alvos depois de expandir essas variáveis?

Eu gostaria de ser capaz de obter os alvos para um makefile arbitrário. Estou tentando escrever uma função de conclusão para meu shell.

BitShifter
fonte

Respostas:

79

Você pode analisar a saída de make -pn(ou seja make --print-data-base --dry-run)? Ele imprime todas as variáveis, regras, regras implícitas e quais comandos serão executados em detalhes trabalhosos.

Jack Kelly
fonte
Esta resposta parece muito melhor do que a outra.
Matt Joiner
Eu concordo que isso é muito melhor. Ainda mais trabalho do que eu esperava, mas vou marcá-lo como a resposta, já que nada mais parece ser razoável e o GNU make não tem um sinalizador conveniente para o que eu quero.
BitShifter
6
Esteja ciente de que a saída pode depender do destino que você especificar, se seu Makefile gerar destinos dinamicamente. A saída também pode depender dos valores das variáveis ​​de ambiente ou fazer variáveis ​​especificadas com o comando make.
reinierpost
6
Eu gosto da brevidade. +1 Adicionado meu próprio spin (descrito em uma resposta abaixo):make -pn | sed -rn '/^[^# \t\.%].*:[^=]?/p'
Jamie
Sugira make -pnr. O -rremove regras implícitas.
sjbx
114
make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'     

Retirado do complemento make arg, que funciona perfeitamente.

Todd Hodes
fonte
2
Eu tentei fazer um alias para isso, colocando-o entre aspas duplas, mas isso apenas resulta em awk tendo um erro de sintaxe na primeira vírgula ... alguma ideia sobre como escapar isso corretamente para um alias de shell (bash)?
drfrogsplat
3
Acho que tem algo a ver com onde está definido. Acho que se for definido como um alias, seu diretório de trabalho se torna o diretório onde está definido e, aparentemente, a parte do awk não gosta do ponto em diretórios ocultos. Definir uma função funciona.
Ibrahim
Acabei de colocá-lo em um script de shell em ~/binvez de usar um alias. Funciona bem; obrigado!
mpontillo de
3
O alias alias mkl="make -qp | awk -F':' '/^[a-zA-Z0-9][^\$#\/\t=]*:([^=]|\$)/ {split(\$1,A,/ /);for(i in A)print A[i]}' | sort"que vai economizar 2 minutos de jogos de citações :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
| sort -uparece mais útil :)
sherpya
14

Não tenho certeza se isso é apenas uma coisa gnu make, mas funciona bem:

make help

Zdenik
fonte
Isso é muito útil, muito mais fácil de lembrar do que um alias personalizado para um dos outros métodos.
Ibrahim
8
Não está realmente funcionando no GNU Make 3.81, provavelmente um alvo em seus makefiles: "make: *** Nenhuma regra para tornar o
alvo`
8
Isso apenas invoca o alvo "help" no Makefile. Se existir, irá imprimir algo (não as listas completas de alvos), se não existir (situação típica), irá resgatar.
falcão
2
A boa notícia é que o cmake parece gerar um alvo de "ajuda" de fácil leitura.
Stéphane,
Isso é incrível! :) Vá cmake
Jeef
10

Vários respondentes sugeriram o uso de make -pn, que imprimirá o banco de dados de regras, mas não executará nada - mais ou menos. O problema com essa abordagem é que -nainda invoca todas as marcas recursivas e ainda faz muito mais trabalho do que o necessário, porque imprime todos os comandos que teria invocado em uma compilação regular. Uma solução mais eficiente seria criar um makefile trivial, dummy.mk, com estes conteúdos:

__all_targets__: ; #no-op

Agora invoque make as make -p -f Makefile -f dummy.mk __all_targets__. Em qualquer construção substancial, a diferença na quantidade de produção gerada pelo make é significativa. Por exemplo:

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

O tempo de execução também foi dramaticamente melhor - 2.063s para a primeira versão, 0.059s para a segunda.

Eric Melski
fonte
1
Ótima ideia, todos que trabalham com sistemas de construção enormes vão gostar. Estatísticas para Android Gingerbread tree (não usa make recursivo, então a economia não é enorme, apenas boa): " 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.
falcão
Bom ponto. A página de manual de makeparece sugerir algo semelhante, mas amigável, a sabermake -p -f/dev/null
Davide
@Davide isso não funcionará muito bem: a especificação -f /dev/nullimpedirá a makeanálise dos makefiles regulares, então você obterá uma impressão apenas das regras embutidas. É por isso que minha solução especifica -f Makefile -f dummy.mk. Mas isso também não é suficiente, porque com -n, makeirá em frente e começará a executar as regras no makefile também. Infelizmente, não estou ciente de nenhuma modificação que possa simplificar a solução apresentada originalmente.
Eric Melski
Claro que voce esta certo. No entanto, a página do manual do make tem um bug, pois afirma: "Para imprimir o banco de dados sem tentar refazer nenhum arquivo, use make -p -f / dev / null"
Davide
Ligeira correção ao meu comentário anterior: deveria ser "... porque sem -n ..."
Eric Melski
9

Confira a conclusão do bash para make no GitHub.

js.
fonte
O link que você forneceu está quebrado
Davide
Link atualizado para a versão atual.
js.
7

Edit: FYI, o repositório git de conclusão debian bash em outra resposta agora incorpora uma versão aprimorada deste script adaptado para casos de uso de conclusão 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

Este é um script muito mais completo do que o script de conclusão debian bash porque fornece resultados para regras geradas que é o que a pergunta pede e a resposta mais votada (script de conclusão debian bash no servidor git debian) não vai longe o suficiente.

Este não é o script original ao qual vinculei, mas é muito mais simples e um pouco mais rápido.

codeshot
fonte
BTW, mods, se esta resposta não estiver totalmente de acordo com todas as políticas devido à nuance de seu texto, por favor, comente antes de excluir. Farei todos os ajustes legais necessários para que esta pergunta realmente obtenha uma resposta completa e válida.
codeshot
Como um teste rápido, peguei o código-fonte do gcc e executei ./configure && time ~ / makecomp.sh | wc -l onde ~ / makecomp.sh é minha resposta. ... configurar saída ... config.status: criando Makefile 3312 real 0m0.401s usuário 0m0.412s sys 0m0.028s
codeshot
Como outro teste, usei o makefile de demonstração em codeshot.blogspot.co.uk/2012/08/… que gera pseudo-regras para construir e verificar as aprovações no teste de unidade. Este exemplo usa mais recursos do make do que um projeto autoconf de plataforma cruzada típico, então o script nesta resposta retorna 14 alvos reais, enquanto a conclusão do bash para make no ubuntu retorna apenas duas regras úteis e 5 arquivos de origem.
codeshot
4

Este é o código para o alias baseado na solução Todd Hodes

alias mtargets='make -qp | awk -F":" "/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split(\$1,A,/ /);for(i in A)print A[i]}"'
Nguyễn Minh Vũ
fonte
3

Isso dará uma boa saída:

make -pn | perl -F: -ane 'print "$F[0]\n" if /^\w+\s*:/' | sort
Andor
fonte
2

Acho que estou um pouco atrasado para esta festa, mas se você está procurando um comando específico, pode tentar

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

Isso geralmente funciona, embora ainda possa incluir algumas coisas não-alvo, como uma vpathdiretiva. Se você não depende makedas regras integradas de, você pode usar make -qpRcomo o primeiro comando no pipeline.

DGrady
fonte
Experimentei com freetz (.org). Lista muitos makefiles incluídos e não lista todos os destinos.
Robert Siemer
1

Solução Ruby:

`make -qp`.split("\n").select{|l| l =~ /^\w/ && l !~ /=/}.map{|l| l.sub(/:.*/,'')}
gdub
fonte
1

Estou trabalhando em solaris 10 e turbo C shell. A solução fornecida não funciona para meu projeto makefile. mesmo depois de alterar a linha de comando acima para a sintaxe tcsh. No entanto, descobri que você pode obter uma solução fácil usando

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

versão remake:

remake --version

é

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

e alguns outros dados de versão sem importância

Aviv
fonte
0

Procurei a mesma pergunta e encontrei este giro:

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

Isso remove todas as linhas de comentário, regras de padrão (linhas que começam com tabulações), todos os intrínsecos (exemplo .c.oe %.o: %.cpadrões).

Jamie
fonte
não leu o comentário da resposta aceita: +1 para a resposta de Jack ... gist.github.com/777954 - pvandenberk 13 de janeiro às 14:47
Jamie
0

Encontrei esta solução em outro tópico:

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

Você também pode adicioná-lo ao seu Makefile:

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

E execute make list.

thisismydesign
fonte
-1

Claro, mas quando você quer que ele os cuspa?

Para relatar o nome do alvo quando ele executa a regra, coloque uma linha na regra:

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

Para cuspi-los todos de uma vez, você pode criar um alvo PHONY separado:

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

E isso pode ser um pré-requisito de seu destino padrão:

all: show_vars
    ...

EDITAR:
Você quer uma maneira de mostrar todos os alvos possíveis de um makefile arbitrário, o que eu suponho que significa não intrusivamente. Bem...

Para fazer isso exatamente, e ser capaz de lidar com makefiles sofisticados, por exemplo, envolvendo regras construídas por evalinstruções, você teria que escrever algo próximo a um emulador Make. Impraticável.

Para ver os alvos das regras simples, você poderia escrever um makefile que atuaria como um scanner makefile, operando em um makefile arbitrário:

  1. Pegue todos os nomes de destino do makefile usando sed.
  2. `inclua` o makefile para usá-lo para expandir variáveis.
  3. Use `show_%:; echo $$ * `para imprimir todos os alvos

Este seria um trabalho impressionante. Tem certeza de que o objetivo vale o esforço?

Beta
fonte
Não é bem o que eu quero. Eu gostaria de ser capaz de fazer make out uma lista de alvos para um makefile arbitrário que eu possa ter. Estou tentando escrever uma função de conclusão de argumento para meu shell e não posso contar com os makefiles tendo nenhuma ajuda sensata.
BitShifter de
-4
grep ^[a-z].*\:$ Makefile | sed s,:,,g
Willian Braga
fonte
1
-1: Esta abordagem produz falsos positivos mesmo para atribuições de variáveis ​​simples ( :=).
thiton de
Você deveria ter testado primeiro. É bom que você tente, no entanto.
Konrad Viltersten