2022-12-18

Tools: redo (part 2) Automatic Recording of Dependencies on Header Files

#software

Content

Part 0: Intro

Part 1: Hello, world!

Part 2: Automatic Recording of Dependencies on Header Files

Part 3: CFLAGS and friends, config.sh, compile.do

Part 4: CFLAGS and friends, env/VAR, default.run.do

Part 5: Auto-update BUILDDATE in version.h

Part 6: The yacc/bison problem: one call produces two artifacts

Part 7: Test: Generator for N source files

My code featured in this series can be found at

https://git.sr.ht/~ew/ew.redo/

Part 2: Automatic Recording of Dependencies on Header Files

The dependency tree produced in Part 1 does not record the fact, that hello.o must be rebuild if resources.h has changed. Of course we could add such a dependency in hello.do, however, we are lazy and we want the computer to take care of this. One way of doing this, is to nicely ask the compiler to produce this information. The compiler, or more precicely the preprocessor needs to evaluate all '#include' pragmas anyway, it is in the position to record, what it encountered along its course. Fortunately there are flags to instruct gcc to list the include dependencies:

shell$ gcc -MMD -MF hello.d -c -o hello.o hello.c
shell$ cat hello.d
hello.o: hello.c resources.h

The output rightfully states, that hello.o needs to be rebuild, if either hello.c or resources.h have changed. The output is formatted in makefile syntax, it can be included as is into a Makefile. In order to make this information known to redo, we are reading the content of the file back into our .do file and call "redo-ifchange" on the part after the colon ':'. The change occurs in "default.o.do" only, and I use shell variable editing to remove the part up to and including the colon.

# default.o.do
redo-ifchange $2.c
gcc  -MMD -MF $2.d  -o $3 -c $2.c
read DEPS <$2.d
redo-ifchange ${DEPS#*:}

With this change in place, the build proceeds including the new commands. The explicit dependency is also visible in the output of redo-dot.

shell$ redo clean
redo clean (0.005s)
shell$ redo -xx all
+ redo-ifchange hello
+ OBJS=hello.o resources.o
+ redo-ifchange hello.o resources.o
+ redo-ifchange hello.c
+ redo-ifchange resources.c
+ gcc -MMD -MF hello.d -o .redo.hello.o.3418330711.3 -c hello.c
+ read DEPS
+ redo-ifchange hello.c resources.h
+ gcc -MMD -MF resources.d -o .redo.resources.o.1173191576.3 -c resources.c
+ read DEPS
+ redo-ifchange resources.c
redo . . hello.o (default.o.do) (0.086s)
redo . . resources.o (default.o.do) (0.099s)
+ gcc -o .redo.hello.1878974086.3 hello.o resources.o
redo . hello (0.159s)
redo all (0.206s)
shell$ redo-dot hello
digraph d {
        rankdir=LR
        ranksep=2
        splines=false // splines=ortho
        node[shape=rectangle]

        "hello" -> "hello.o"
        "hello.o" -> "hello.o.do" [style=dotted]
        "hello.o" -> "default.o.do"
        "resources.o" -> "resources.o.do" [style=dotted]
        "hello" -> "hello.do"
        "hello.o" -> "resources.h"
        "hello" -> "resources.o"
        "resources.o" -> "default.o.do"
        "resources.o" -> "resources.c"
        "hello.o" -> "hello.c"
}
shell$ LANG=C ls -al
total 76
drwxr-xr-x 3 ew ew  4096 Oct 18 17:50 .
drwxr-xr-x 5 ew ew  4096 Oct 18 16:16 ..
-rw-r--r-- 1 ew ew    29 Oct 18 17:35 .gitignore
drwxr-xr-x 2 ew ew  4096 Oct 18 17:50 .redo
-rw-r--r-- 1 ew ew    48 Oct 17 20:36 all.do
-rw-r--r-- 1 ew ew    58 Oct 18 17:23 clean.do
-rw-r--r-- 1 ew ew   592 Oct 18 17:12 default.o.do
-rwxr-xr-x 1 ew ew 16032 Oct 18 17:50 hello
-rw-r--r-- 1 ew ew   108 Oct 17 16:19 hello.c
-rw-r--r-- 1 ew ew    48 Oct 18 17:50 hello.d
-rw-r--r-- 1 ew ew   150 Oct 18 16:19 hello.do
-rw-r--r-- 1 ew ew  1304 Oct 18 17:50 hello.o
-rw-r--r-- 1 ew ew    33 Oct 17 16:19 resources.c
-rw-r--r-- 1 ew ew    44 Oct 18 17:50 resources.d
-rw-r--r-- 1 ew ew    22 Oct 17 16:20 resources.h
-rw-r--r-- 1 ew ew  1136 Oct 18 17:50 resources.o

In order to explicitly record the dependency to system include files like /usr/include/stdio.h, the flag "-MMD" can be changed to "-MD", if so desired.

Do not forget, to add '*.d' in .gitignore and clean.do.

Home