Common reasons why 'Linux make' always build target although not needed
Deepak Kumar
Propelling AI To Reinvent The Future ||Author|| 150+ Mentorship|| Leader || Innovator || Machine learning Specialist || Distributed architecture | IoT | Cloud Computing
Introduction
Layman explanation
If you are Linux programmer and you have used make command, you might have great respect for make command. You think that make command will ensure to avoid duplicate compilation effort automatically. Suddenly, if you see that make is compiling a file always even though there is no edit in this file, you might be surprised. And it is not uncommon to witness such surprises. This document helps to understand why it could be happening.
Technical explanation
gnu make tool empower user to automate compilation. To execute make in the right way, Makefile must be written correctly. make tool uses Makefile to create dependency graph. Makefile provides input to make tool which target should be built and when it should be built. If make is building target always although not needed, then it can be error in Makefile programming.
Finding reasons in Makefile
Check if the specified target is created
If target file is not created(symbolic target), then make falsely assumes that target recipe must be executed.
Example where target is built always due to non-existent target file
Before:
CC=gcc
CFLAGS=-Wall -I. -c
EXEC_FILE=program1
all: program
hellofunc.o: hellofunc.c
hellofunc.c: hellofunc.h
hellomake.o: hellomake.c
hellomake.c: hellomake.h
objects: hellofunc.c hellomake.c
$(CC) hellofunc.c hellomake.c $(CFLAGS)
program: hellofunc.o hellomake.o
$(CC) hellofunc.o hellomake.o -o $(EXEC_FILE)
=================================
[root@ubuntu /personal/testcode/makefile_learning]# make -f Makefile_1
gcc hellofunc.o hellomake.o -o program1
After:
CC=gcc
CFLAGS=-Wall -I. -c
EXEC_FILE=program1
all: $(EXEC_FILE)
hellofunc.o: hellofunc.c
hellofunc.c: hellofunc.h
hellomake.o: hellomake.c
hellomake.c: hellomake.h
objects: hellofunc.c hellomake.c
$(CC) hellofunc.c hellomake.c $(CFLAGS)
$(EXEC_FILE): hellofunc.o hellomake.o
$(CC) hellofunc.o hellomake.o -o $(EXEC_FILE)
=============================================
[root@ubuntu /personal/testcode/makefile_learning]# make -f Makefile_2
make: Nothing to be done for `all'.
Check if all dependencies have corresponding specified target created
If any dependency doesn't create target(symbolic target), then make falsely assumes that dependency is out-dated and all targets associated to this dependency must be re-executed.
Example where a dependency is not creating target
Before:
convert: devel/bar
touch convert
init: devel/foo
echo 'init'
devel/foo:
mkdir -p devel
touch devel/foo
devel/bar: init
touch devel/bar
==============================
[root@ubuntu /personal/testcode/makefile_learning]# make -f Makefile_1
mkdir -p devel
touch devel/foo
echo 'init'
init
touch devel/bar
touch convert
[root@ubuntu /personal/testcode/makefile_learning]# make -f Makefile_1
echo 'init'
init
touch devel/bar
touch convert
After:
convert: devel/bar
touch convert
init: devel/foo
echo 'init'
touch init
devel/foo:
mkdir -p devel
touch devel/foo
devel/bar: init
touch devel/bar
==========================
[root@ubuntu /personal/testcode/makefile_learning]# make -f Makefile_2
echo 'init'
init
touch init
touch devel/bar
touch convert
[root@ubuntu /personal/testcode/makefile_learning]# make -f Makefile_2
make: `convert' is up to date.
Check if any dependency of target is marked 'always build'
.PHONY makefile option ensures that target is always built. All targets dependent on this target will be rebuilt everytime.
Example where a dependency is marked as .PHONY
Before:
.PHONY: init
convert: devel/bar
touch convert
init: devel/foo
echo 'init'
touch init
devel/foo:
mkdir -p devel
touch devel/foo
devel/bar: init
touch devel/bar
==========================================
[root@ubuntu /personal/testcode/makefile_learning]# make -f Makefile_1
echo 'init'
init
touch init
touch devel/bar
touch convert
[root@ubuntu /personal/testcode/makefile_learning]# make -f Makefile_1
echo 'init'
init
touch init
touch devel/bar
touch convert
After:
.PHONY: init
convert: devel/bar
touch convert
init: devel/foo
echo 'init'
touch init
devel/foo:
mkdir -p devel
touch devel/foo
devel/bar: init
touch devel/bar
=============================
[root@ubuntu /personal/testcode/makefile_learning]# make -f Makefile_2
echo 'init'
init
touch init
touch devel/bar
touch convert
[root@ubuntu /personal/testcode/makefile_learning]# make -f Makefile_2
make: `convert' is up to date.
See if directory timestamp change is falsely triggering rebuild
Consider an example where your targets are to be placed in a separate directory, and that directory might not exist before make is run. In this situation, you want the directory to be created before any targets are placed into it but, because the timestamps on directories change whenever a file is added, removed, or renamed, we certainly don’t want to rebuild all the targets whenever the directory’s timestamp changes. One way to manage this is with order-only prerequisites: make the directory an order-only prerequisite on all the targets:
OBJDIR := objdir OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o) $(OBJDIR)/%.o : %.c $(COMPILE.c) $(OUTPUT_OPTION) $< all: $(OBJS) $(OBJS): | $(OBJDIR) $(OBJDIR): mkdir $(OBJDIR)
Now the rule to create the objdir directory will be run, if needed, before any ‘.o’ is built, but no ‘.o’ will be built because the objdir directory timestamp changed.
What if none of above method works for me
If none of above method helps you, then you may need to use make debugging tool. make provides -d option to get debugging capability. It gives detail output about why a target is builfd (for example, reason related to pre-requisite and dependency detail) . If you found any new reason, please share with me in this comment section. All the very best.