44

Problem

I want to see the dependencies for one or more targets of a makefile. So I am looking for a program that can parse makefiles and then will represent the dependencies in some tree-like format (indentation, ascii-art, ...) or as a graph (dot, ...).

Similar

There are programs that do this for other situations:

  • pactree or debtree can display the dependencies for software packages in the respective format in a tree like ascii format or as a dot graph,
  • gcc -M source_file.c displays the dependencies of the C source file as a make rule,
  • pstree displays an ascii representation of the process tree.

Progress

Searching the web I found little help. That lead me to try

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

but it looks like I have to hack some more parsing code in perl or python in order to represent this as a nice tree/graph. And I do not yet know if I will really get the full and correct graph this way.

Requirements

It would be nice to limit the graph in some ways (no builtin rule, only a given target, only some depth) but for the most part I am just looking for a tool that will give me the dependencies in some "reasonable", human-viewable format (like the programs under "Similar" do).

Questions

  • Are there any programs that can do this?
  • Will I get the full and correct information from make -dnq ...?
  • Is there a better way to get this info?
  • Do scripts/attempts for parsing this info already exist?
5
  • 2
    The crucial thing to understand here is: Dependencies do NOT form a tree. They form a directed and (hopefully!) acyclic graph—also known as a DAG. Try sketching out the dependency graph for the following and you'll see it: A depends on B; A also depends on C; B depends on D; C depends on D.
    – Wildcard
    Commented May 16, 2016 at 19:24
  • 1
    @Wildcard I know but for my purpose it is enough to represent the dependencies as a tree. I am fine with duplicating subgraphs (and cutting at circles) to make it a tree. Sorry for not being explicit. For you example I would be fine with the output of printf 'A\n B\n D\n C\n D\n'. (Who said I can't put newlines in comments? :)
    – Lucas
    Commented May 16, 2016 at 19:56
  • How would that be distinguishable from "A depends on B; B depends on D; D depends on C; A depends on D"? You can impose a total ordering on any DAG (since any DAG also represents a partial ordering), but you can't turn a DAG into a tree. This is basic graph theory. I would be interested to see your algorithm for creating a tree representation of a DAG which could then be displayed. Without such an underlying algorithm, any tool which sought to display dependencies as a tree would of necessity be extremely hacky and error-prone.
    – Wildcard
    Commented May 16, 2016 at 20:02
  • Maybe I was not explicit enough again but I thought the examples I give under Similar made it clear. I am not interested in graph theory (in this question at least). All I want for this is a visual representation that looks similar to a tree (especially if it should be displayed on a terminal, for dot order graphs are obviously fine.) I will update the question a little to make it clearer (I hope).
    – Lucas
    Commented May 16, 2016 at 20:10
  • 6
    RANT: I'm honestly a little frustrated that make doesn't offer something like this out-of-the-box. Make is one of THE most wide-spread build systems in the world, and this feature would be so immensely useful that it is hard to grasp that during the God knows how many decades that make has existed no one added such a feature. Outputting this information in a clearly defined textual format would be totally sufficient. I understand that make is open source and I could always add this feature myself. And believe me, if make wasn't basically a black box to me, I would! RANT over.
    – antred
    Commented Jul 27, 2017 at 11:22

4 Answers 4

30

Try makefile2graph from the same author there is a a similar tool MakeGraphDependencies written in java instead of c.

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

Then use some vector graphics editor to highlight connections you need.

3
  • 1
    I've tried that tool. Doesn't even begin to work (at least not for any of the make incarnations I tried it with). Most of the time it just poops out with some memory access violation.
    – antred
    Commented Jul 27, 2017 at 10:56
  • Sadly, it could probably do with some care and attention - give the repo some build, test and perhaps even a container, to drop in people's toolchains where they want it. Commented Apr 29, 2022 at 14:52
  • 1
    This works for me. I like it. Very simple and really easy to use. My steps: 1, run: git clone github.com/lindenb/makefile2graph.git 2, switch to the directory makefile2graph and run: make to generate the executable file make2graph 3, get the full path of the make2graph 4, switch to the cmake project directory where makefile was located and run the following command to generate for example the png file. make -Bnd | %full_path_of_make2graph | dot -Tpng -o out.png , given that dot was installed already on your Linux machine
    – Cary
    Commented Jun 21, 2023 at 9:16
6

I used remake --profile (a drop-in replacement for make), it generated a dependency tree in a callgrind format.

Then gprof2dot can generate an image of the target tree.

3
  • Do I understand the documentation wrong or does remake --profile only output the dependency graph for the target it executes? Or can it somehow output the graph for all targets?
    – Lucas
    Commented Jan 22, 2019 at 22:27
  • Only the one it runs, I’m afraid. But you can run them all with —dry-run Commented Jan 22, 2019 at 22:28
  • Oh yea, something like remake --targets -r | grep -v %| grep -v '\t*\.'|xargs remake -n --profile -B looks promising.
    – Lucas
    Commented Jan 22, 2019 at 22:33
4

I have found a kind of hack to at least output clearly structured information about which target depends on which prerequisites. The downside is, it's quite intrusive. In other words, you need to change your makefile to wrap the build recipes of all your targets into a little conditional function. I'll post a brief example:

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 $@)

In this example, I'm using the hand-rolled getRecipe function to wrap each individual target's recipe and then decide whether to actually run that recipe or simply output which target is being built and which prerequisites it depends on. The latter happens only if the variable DEPENDENCY_GRAPH is set (e.g. as an environment variable). In the example, the build recipe is nothing more than an echo saying that the target is being built, but you could obviously replace this with a command of your choice.

With DEPENDENCY_GRAPH set to 1, this results in the output:

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"

which should be easy enough to parse and then convert into a dot-graph.

With DEPENDENCY_GRAPH not set at all or set to 0, the output is:

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

or, in other words, the normal build recipe is used instead. I haven't tested yet whether this works reliably with complicated recipes. One problem I've already run into is that it doesn't work at all with multi-line recipes.

For instance, in the last target's build recipe, if in addition to saying that the target is being built I actually wanted to touch the file:

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

make seems to think that the touch $@ part is merely part of the echo in the previous line:

Building target foobar.txt touch foobar.txt

If I leave off the trailing backslash in the previous line, make complains *** unterminated call to functioncall': missing )'. Stop. If anyone has an idea how to get make to play nice, I'm all ears. :)

EDIT: The other problem with this approach is that this will only work if no build results already exist, as makeobviously doesn't execute the build recipe of a target it considers up-to-date.

2
  • add ; after target $@ the touch command to works
    – mug896
    Commented Feb 22, 2019 at 2:57
  • 1
    for the second problem, use make -B option that unconditionally make all targets.
    – mug896
    Commented Feb 22, 2019 at 3:05
3

If you just need to list the dependencies for the specific target you can use the following command:

make -dn MAKE=: you_target | sed -rn "s/^ *Considering target file '(.*)'\.$/\1/p"

MAKE=: is for not running child makes.

Unfortunately included Makefiles are listed too. You can filter them out using following shell script (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"

Now you can list all dependencies for the target using this command:

list-deps MAKE=: your_target

If you need to list only the dependencies that need to be remade, see this answer.

Edit: For others who find this answer - you can preserve indentation by using

make -dn MAKE=: you_target | sed -rn "s/^(\s+)Considering target file '(.*)'\.$/\1\2/p"

You can also make this a target in the Makefile:

.PHONY: graph 
graph: 
    make -dn MAKE=: all | sed -rn "s/^(\s+)Considering target file '(.*)'\.$/\1\2/p" 
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .