
Problema
Quero ver as dependências de um ou mais alvos de um makefile. Então, estou procurando um programa que possa analisar makefiles e então representar as dependências em algum formato semelhante a uma árvore (recuo, arte ascii, ...) ou como um gráfico (ponto, ...).
Semelhante
Existem programas que fazem isso para outras situações:
- pactreeouárvore de dívidapode exibir as dependências dos pacotes de software no respectivo formato em uma árvore como formato ascii ou como um
dot
gráfico, gcc -M source_file.c
exibe as dependências do arquivo de origem C como uma regra make,- pstreeexibe uma representação ascii da árvore de processos.
Progresso
Pesquisando na web eu encontreipequena 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 preciso hackear mais algum 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 conseguirei o gráfico completo e correto desta forma.
Requisitos
Seria bom limitar o gráfico de algumas maneiras (sem regra interna, apenas um determinado alvo, apenas alguma profundidade), mas na maioria das vezes estou apenas procurando uma ferramenta que me forneça as dependências de alguma forma humana "razoável". -formato visualizável (como fazem os programas em "Similar").
Questões
- Existem programas que podem fazer isso?
- Receberei as informações completas e corretas de
make -dnq ...
? - Existe uma maneira melhor de obter essas informações?
- Já existem scripts/tentativas de analisar essas informações?
Responder1
Tentarmakefile2graphdo mesmo autor existe uma ferramenta semelhanteMakeGraphDependenciesescrito em java
vez de c
.
make -Bnd | make2graph | dot -Tsvg -o out.svg
Em seguida, use algum editor de gráficos vetoriais para destacar as conexões necessárias.
Responder2
eu useirefazer --profile(um substituto imediato para make
), gerou uma árvore de dependências em formato callgrind.
Entãogprof2dotpode gerar uma imagem da árvore de destino.
Responder3
Eu encontrei uma espécie de hack para, pelo menos, produzir informações claramente estruturadas sobre qual alvo depende de quais pré-requisitos. A desvantagem é que é bastante intrusivo. Em outras palavras, você precisa alterar seu makefile para agrupar as receitas de construção de todos os seus alvos 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 manualmente para agrupar a receita de cada destino individual e, em seguida, decidir se realmente executarei essa receita ou simplesmente exibirei qual destino está sendo construído e de quais pré-requisitos ele depende. O último acontece apenas se a variável DEPENDENCY_GRAPH
estiver definida (por exemplo, como uma variável de ambiente). No exemplo, a receita de construção nada mais é do que um eco dizendo que o alvo está sendo construído, mas você poderia obviamente substituir isso por um comando de sua escolha.
Com DEPENDENCY_GRAPH
definido 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"
que deve ser fácil de analisar e depois converter em um gráfico de pontos.
Com DEPENDENCY_GRAPH
nã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 construção normal é usada. Ainda não testei se isso funciona de maneira confiável com receitas complicadas. Um problema que já encontrei é que não funciona com receitas de várias linhas.
Por exemplo, na receita de construção do último alvo, se além de dizer que o alvo está sendo construído eu realmente quisesse touch
o arquivo:
$(VARIABLE_TARGET_NAME) :
$(call getRecipe,\
@echo Building target $@\
touch $@)
make
parece pensar que a touch $@
parte é apenas parte do eco da linha anterior:
Building target foobar.txt touch foobar.txt
Se eu deixar de fora a barra invertida na linha anterior, make
reclama, *** unterminated call to function
chame': faltando )'. Stop.
Se alguém tiver uma idéia de como make
jogar bem, sou todo ouvidos. :)
EDIT: O outro problema com esta abordagem é que isso só funcionará se ainda não existirem resultados de construção, pois make
obviamente não executa a receita de construção de um destino que considera atualizado.
Responder4
Se você precisar apenas listar as dependências do destino específico, poderá usar o seguinte comando:
make -dn MAKE=: you_target | sed -rn "s/^ *Considering target file '(.*)'\.$/\1/p"
MAKE=:
é para não correr criançafaz.
Infelizmente, os Makefiles incluídos também estão listados. Você podefiltrá-losusando o seguinte script de shell ( list-deps
):
#!/bin/sh -e
dbg="`make -dn "$@"`"
all="`echo -n "$dbg" | sed -rn "s/^ *Considering target file '(.+)'\.$/\1/p"`"
mks="`echo -n "$dbg" | sed -rn "s/^ *Reading makefile '([^']+)'.*$/\1/p"`"
echo -n "$all" | grep -vxF "$mks"
Agora você pode listar todas as dependências do destino usando este comando:
list-deps MAKE=: your_target
Se você precisar listar apenas as dependências que precisam ser refeitas, consulteesta resposta.
Editar: para outras pessoas que encontrarem esta resposta - você pode preservar o recuo usando
make -dn MAKE=: you_target | sed -rn "s/^(\s+)Considering target file '(.*)'\.$/\1\2/p"
Você também pode tornar isso um alvo no Makefile:
.PHONY: graph
graph:
make -dn MAKE=: all | sed -rn "s/^(\s+)Considering target file '(.*)'\.$/\1\2/p"