Como exibir dependências fornecidas em um makefile como uma árvore?

18

Problema

Quero ver as dependências para um ou mais destinos de um makefile. Então, estou procurando um programa que possa analisar makefiles e, em seguida, representar as dependências em algum formato de árvore (recuo, ascii-art, ...) ou como um gráfico (ponto, ...).

Semelhante

Existem programas que fazem isso para outras situações:

  • pactree ou debtree pode exibir as dependências para pacotes de software no respectivo formato em uma árvore como formato ascii ou como dotgráfico,
  • gcc -M source_file.c exibe as dependências do arquivo de origem C como uma regra de criação,
  • pstree exibe uma representação ascii da árvore de processos.

Progresso

Pesquisando na web, encontrei pouca ajuda . Isso me levou a tentar

make --always-make --silent --dry-run some_target | \
  grep --extended-regexp 'Considering target file|Trying rule prerequisite'

mas parece que eu tenho que hackear um pouco mais de código de análise em perl ou python para representar isso como uma bela árvore / gráfico. E ainda não sei se realmente vou obter o gráfico completo e correto dessa maneira.

Exigências

Seria bom limitar o gráfico de algumas maneiras (nenhuma regra embutida, apenas um determinado destino, apenas um pouco de profundidade), mas na maioria das vezes estou apenas procurando uma ferramenta que me dê as dependências de alguma maneira "razoável", humana. formato visível (como os programas em "Similar").

Questões

  • Existem programas que podem fazer isso?
  • Receberei as informações completas e corretas make -dnq ...?
  • Existe uma maneira melhor de obter essas informações?
  • Já existem scripts / tentativas para analisar essas informações?
Lucas
fonte
11
O ponto crucial a entender aqui é: Dependências NÃO formam uma árvore. Eles formam um gráfico direcionado e (espero!) Acíclico - também conhecido como DAG . Tente esboçar o gráfico de dependência para o seguinte e você verá: A depende de B; A também depende de C; B depende de D; C depende de D.
Curinga
@ Wildcard eu sei, mas para o meu propósito, é suficiente representar as dependências como uma árvore. Eu estou bem com duplicação de subgráficos (e corte em círculos) para torná-lo uma árvore. Desculpe por não ser explícito. Para seu exemplo, eu ficaria bem com a saída de printf 'A\n B\n D\n C\n D\n'. (Quem disse que eu não posso colocar novas linhas nos comentários? :) #
1655 Lucas
Como isso seria distinguível de "A depende de B; B depende de D; D depende de C; A depende de D"? Você pode impor uma ordem total a qualquer DAG (já que qualquer DAG também representa uma ordem parcial), mas não é possível transformar um DAG em uma árvore. Esta é a teoria básica dos grafos. Gostaria de ver seu algoritmo para criar uma representação em árvore de um DAG que pode ser exibido. Sem esse algoritmo subjacente, qualquer ferramenta que tentasse exibir dependências como uma árvore seria necessariamente extremamente invasiva e propensa a erros.
Curinga
Talvez eu não tenha sido suficientemente explícito novamente, mas pensei que os exemplos que dou em Similar deixavam claro. Não estou interessado em teoria dos grafos (pelo menos nesta pergunta). Tudo o que quero é uma representação visual que se pareça com uma árvore (especialmente se for exibida em um terminal, pois os dotgráficos de pedidos são obviamente bons.) Atualizarei a pergunta um pouco para torná-la mais clara (espero).
Lucas
2
RANT: Sinceramente, estou um pouco frustrado que a marca não ofereça algo como esse pronto para o uso. Make é um dos sistemas de construção mais difundidos do mundo, e esse recurso seria tão imensamente útil que é difícil compreender que, durante o Deus, saiba quantas décadas existem, ninguém adicionou esse recurso. A saída dessas informações em um formato textual claramente definido seria totalmente suficiente. Entendo que make é open source e sempre posso adicionar esse recurso. E acredite, se make não fosse basicamente uma caixa preta para mim, eu faria! RANT acabou.
antred 27/07

Respostas:

10

Tente makefile2graph do mesmo autor, existe uma ferramenta similar MakeGraphDependencies escrita em javavez de c.

make -Bnd | make2graph | dot -Tsvg -o out.svg

Em seguida, use um editor de gráficos vetoriais para destacar as conexões necessárias.

xae
fonte
11
Eu tentei essa ferramenta. Nem sequer começa a funcionar (pelo menos não para nenhuma das encarnações de make que eu tentei). Na maioria das vezes, apenas aparece com alguma violação de acesso à memória.
antred 27/07
3

Eu encontrei um tipo de hack para, pelo menos, gerar informações claramente estruturadas sobre qual destino depende de quais pré-requisitos. A desvantagem é que é bastante intrusivo. Em outras palavras, você precisa alterar seu makefile para agrupar as receitas de compilação de todos os seus destinos em uma pequena função condicional. Vou postar um breve exemplo:

getRecipe = $(if $(DEPENDENCY_GRAPH),@echo Target $@ depends on prerequisites "$^",$(1))


VARIABLE_TARGET_NAME = foobar.txt

all : TopLevelTarget

TopLevelTarget : Target_A Target_D
    $(call getRecipe,\
        @echo Building target $@)

Target_A : Target_B
    $(call getRecipe,\
        @echo Building target $@)

Target_D : Target_C
    $(call getRecipe,\
        @echo Building target $@)

Target_B : $(VARIABLE_TARGET_NAME)
    $(call getRecipe,\
        @echo Building target $@)

Target_C :
    $(call getRecipe,\
        @echo Building target $@)

$(VARIABLE_TARGET_NAME) :
    $(call getRecipe,\
        @echo Building target $@)

Neste exemplo, estou usando a função getRecipe enrolada à mão para agrupar a receita de cada destino individual e, em seguida, decido se realmente executar essa receita ou simplesmente gerar qual destino está sendo construído e de quais pré-requisitos depende. O último acontece apenas se a variável DEPENDENCY_GRAPHestiver configurada (por exemplo, como uma variável de ambiente). No exemplo, a receita da compilação nada mais é do que um eco dizendo que o destino está sendo construído, mas você pode obviamente substituí-la por um comando de sua escolha.

Com DEPENDENCY_GRAPHdefinido como 1, isso resulta na saída:

Target foobar.txt depends on prerequisites ""
Target Target_B depends on prerequisites "foobar.txt"
Target Target_A depends on prerequisites "Target_B"
Target Target_C depends on prerequisites ""
Target Target_D depends on prerequisites "Target_C"
Target TopLevelTarget depends on prerequisites "Target_A Target_D"

o que deve ser fácil de analisar e depois converter em um gráfico de pontos.

Com DEPENDENCY_GRAPHnão definido ou definido como 0, a saída é:

Building target foobar.txt
Building target Target_B
Building target Target_A
Building target Target_C
Building target Target_D
Building target TopLevelTarget

ou, em outras palavras, a receita de compilação normal é usada. Ainda não testei se isso funciona de maneira confiável com receitas complicadas. Um problema em que eu já me deparei é que ele não funciona com receitas de várias linhas.

Por exemplo, na receita de construção do último destino, se além de dizer que o destino está sendo construído, eu realmente queria toucho arquivo:

$(VARIABLE_TARGET_NAME) :
    $(call getRecipe,\
        @echo Building target $@\
        touch $@)

makeparece pensar que a touch $@parte é apenas parte do eco na linha anterior:

Building target foobar.txt touch foobar.txt

Se eu deixar de fora a barra invertida na linha anterior, makereclama a *** unterminated call to functionchamada ': falta )'. Stop.Se alguém tiver uma idéia de como makejogar bem, eu sou todo ouvidos. :)

EDIT: O outro problema com essa abordagem é que isso funcionará apenas se não existirem resultados de compilação, pois makeobviamente não executa a receita de compilação de um destino que considera atualizado.

antred
fonte
adicionar ;após target $@o comando touch funcionar
mug896 22/02
para o segundo problema, use a make -Bopção que faz incondicionalmente todos os destinos.
mug896 22/02
2

Eu usei o remake --profile (um substituto para o drop-in make), que gerou uma árvore de dependência em um formato de callgrind.

Em seguida, o gprof2dot pode gerar uma imagem da árvore de destino.

Victor Sergienko
fonte
Entendo a documentação incorretamente ou remake --profileapenas produz o gráfico de dependência para o destino que ele executa? Ou pode, de alguma forma, gerar o gráfico para todos os destinos?
Lucas
Apenas o que ele roda, eu tenho medo. Mas você pode executá-los todos com —dry-run
Victor Sergienko
Oh sim, algo como remake --targets -r | grep -v %| grep -v '\t*\.'|xargs remake -n --profile -Bparece promissor.
Lucas