Timur Fayzrakhmanov Timur Fayzrakhmanov - 1 month ago 7
C Question

How to properly use % or * (asterisk) symbols as a placeholder in makefile?

I have a problem with creating "context-free/filename-agnostic" makefile to build C project without specifying explicit file names each time I add new ones. I have the following project structure:

include/ #dir to store all header files
lib1.h
lib2.h
...
src/ # dir of source code
obj/ #temporary dir for storing all .o files
lib1.o
lib2.o
...
lib1.c # start with #include "lib1.h"
lib2.c # the same
...
main.c


And the following makefile:

# dirs
IDIR=include
SDIR=src
ODIR=src/obj

_HEADS=*.h #or maybe % percentage instead?
HEADS=$(patsubst %, $(IDIR)/%, $(_HEADS))
_OBJS=*.o
OBJS=$(patsubst %, $(ODIR)/%, $(_OBJS))
_CFILES=*.c
CFILES=$(patsubst %, $(SDIR)/%, $(_CFILES))

# compiler
GCC=gcc
CFLAGS=-I$(IDIR)

$(OBJS): $(CFILES) $(HEADS)
$(GCC) -c -o $@ $< $(CFLAGS)

main: $(OBJS)
$(GCC) -o $@ $^ $(CFLAGS)


What I'm trying to achieve is to build executable if any changes are occurred in *.c or *.h files under
src/
and
include/
folders (not sure the changes might be under
src/obj
). So now the
make
do not compile because it thinks the object files are "up-to-date". So I probably do something wrong, since I need to check .c and .h files first.

Also I'm not sure (even after reading several tutorials) how to use % properly and what the difference between using % and * asterisk. For example, I saw something like these:

%.o: %.c %.h
gcc -o foo %@ ...


How in the world the rule
%.o
is written as rule name (I suppose it's better if they are all plain names instead of some logical things)? Or it's make sense to do so only if you use placeholders like
%@
in the actual rule to avoid additional naming "overhead". Please, could anyone explain and help me to fix my makefile - I really messed up with these subtle things...

Answer

* is a wildcard which gets you a list and is "shell" relates.

% is a makefile internal wildcard which will serve as a placeholder in a for-each manner (I will explain below).

While *.c gets you a list of all c files in one variable you can do more with %

%.o: %.c %.h
    gcc -o foo %@

This for example is called a pattern-rule and means for each <placeholder>.c and <placeholder>.h generate the target <placeholder>.o with the according recipe below it.

With the pattern rule make would generate something like this

a.o : a.c a.h
    ...
b.o : b.c b.h
    ...
c.o : c.c c.h
    ...

What you are currently trying to do would result int

a.o b.o c.o : a.c b.c c.c a.h b.h c.h 
    gcc ....

While this may be valid you have unnecessary dependencies. a.o does not need to depend on b.c e.g.

Also _CFILES = *.c will result in "*.c" being the string in the variable not the actual files. If you want to expand the actual filenames you should use the wildcard function

_CFILES  := $(wildcard *.c)

now

CFILES=$(patsubst %, $(SDIR)/%, $(_CFILES))

also has this "for-each" like placeholer %

this means everytime there is % it will be replaced by $(SDIR)/%. Because you have no pattern for % to match though in the patsubst this will get strange. What do you want to achieve here? If you want to "cut of" src from the path it would be the other way round

CFILES=$(patsubst $(SRC)%, % , $(_CFILES))

But I doubt you want to do this. You should read a bit in the makefile documentation which is linked quite a few times here. I explains quite a lot.

Comments