Timur Fayzrakhmanov Timur Fayzrakhmanov - 2 months ago 13
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
src/ # dir of source code
obj/ #temporary dir for storing all .o files
lib1.c # start with #include "lib1.h"
lib2.c # the same

And the following makefile:

# dirs

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

# compiler

$(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
folders (not sure the changes might be under
). So now the
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
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...


* 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)


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.