How Makefile Handles Header File Dependencies

How Makefile Handles Header File Dependencies

1. Background

When compiling C/C++ projects, you may sometimes see .d files, but it may not be clear how they are generated.

First, how does Makefile handle header file dependencies? Typically, in a Makefile, we need to specify which source files (.c or .cpp) and header files (.h) the target files (like .o) depend on. However, if the Makefile cannot automatically detect changes after modifying a header file, it may lead to incorrect compilation results because the source files that depend on that header file are not recompiled.

Therefore, to manage these dependencies correctly, the Makefile needs to know which header files are included by each source file. Manually maintaining these dependencies can be cumbersome, especially when the project is large and there are many header files. This is where automatically generating dependencies becomes crucial.

2. Compiler Options for Generating .d Files

At this point, the GCC compiler supports the functionality to generate dependency information. For example, the -M series options of gcc, such as -M, -MM, -MD, -MMD, etc. These options can generate dependency rules and include them in the Makefile. For instance, using the -MMD option, the compiler generates a .d file while compiling the source file, which records all the header files that the target file generated from that source file depends on.

<span>-MMD</span>: Generates dependency files, ignoring system header files (like <span><stdio.h></span>). • <span>-MD</span>: Generates dependency files, including all header files. • <span>-MP</span> (recommended): Generates a phony target for each header file to avoid errors when deleting header files.

Example Compilation Command:

gcc -MMD -MP -c main.c -o main.o

This will generate <span>main.d</span>, with the following content:

main.o: main.c utils.h
utils.h:  # Phony target to prevent errors when deleting header files

3. Complete Steps of a Makefile

1. Basic Makefile
CC = gcc
CFLAGS = -MMD -MP  # Enable dependency generation
SRCS = main.c utils.c
OBJS = $(SRCS:.c=.o)
TARGET = app

all: $(TARGET)

$(TARGET): $(OBJS)
	$(CC) $^ -o $@

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

clean:
	 rm -f $(OBJS) $(TARGET) *.d

# Include all .d files
-include $(SRCS:.c=.d)
2. Key Explanations

<span>-include $(SRCS:.c=.d)</span>: Automatically includes all <span>.d</span> files, where <span>-</span> indicates to ignore errors if the files do not exist (no <span>.d</span> files during the first compilation). • <span>CFLAGS</span> includes <span>-MMD -MP</span>: Generates <span>.d</span> files during compilation and adds phony targets.

4. Verification

  1. First Compilation: Execute <span>make</span> to generate <span>main.o</span>, <span>utils.o</span>, and <span>main.d</span>, <span>utils.d</span>.

    $ make
    gcc -MMD -MP -c main.c -o main.o
    gcc -MMD -MP -c utils.c -o utils.o
    gcc main.o utils.o -o app
    
  2. Modify Header File to Trigger Recompilation: Modify <span>utils.h</span> and execute <span>make</span> again, observing whether <span>main.o</span> and <span>utils.o</span> are recompiled.

    $ touch utils.h
    $ make
    gcc -MMD -MP -c main.c -o main.o   # main.o is recompiled due to dependency on utils.h
    gcc -MMD -MP -c utils.c -o utils.o
    gcc main.o utils.o -o app
    

5. Optimization and Common Issues

1. Handling Multi-level Directories

If project files are distributed across multiple directories, you can specify the output path for <span>.d</span> files:

OBJDIR = build
OBJS = $(addprefix $(OBJDIR)/, $(SRCS:.c=.o))
DEPFILES = $(addprefix $(OBJDIR)/, $(SRCS:.c=.d))

%.o: %.c | $(OBJDIR)
	$(CC) $(CFLAGS) -c $< -o $@

$(OBJDIR)/%.d: %.c | $(OBJDIR)
	 @touch $@

$(OBJDIR):
	 @mkdir -p $@

-include $(DEPFILES)
2. Performance Optimization

Quickly Locate <span>.d</span> Files: Use the <span>find</span> command instead of recursive functions:

DEPFILES := $(shell find $(OBJDIR) -name '*.d')
-include $(DEPFILES)
3. Common Issues

Issue: Errors when deleting header filesSolution: Add the <span>-MP</span> option to generate phony targets. • Issue: Errors during first compilation due to <span>.d</span> files not existingSolution: Use <span>-include</span> to ignore errors.

Finally

Some embedded learning materials have been collected, reply in the public account with 1024 to find the download link!

Recommended Articles  Click the blue text to jump
☞ Collection | Comprehensive Programming of Linux Applications
☞ Collection | Learn Some Networking Knowledge
☞ Collection | Handwritten C Language

☞ Collection | Handwritten C++ Language
☞ Collection | Experience Sharing
☞ Collection | From Microcontrollers to Linux
☞ Collection | Power Control Technology
☞ Collection | Essential Mathematics for Embedded Systems
☞ Collection | MCU Advanced Topics

☞ Collection | Advanced Embedded C Language

☞ Collection | Experience Sharing

Leave a Comment