What is the difference between % and * in a makefile
Both %
and *
are ordinary characters in Make recipe lines; they are just passed to the shell.
%
denotes a file "stem" in pattern substitutions, as in $(patsubst %.o,%.c,$(OBJS))
. The pattern %.o
is applied to each element in $(OBJS)
, and %
captures the matching part. Then in the replacement pattern %.c
, the captured part is substituted for the %
, and a list of the substitutions emerges out of patsubst
as the return value.
*
is useful in the argument of the $(wildcard ...)
operator, where it resembles the action of the shell *
glob in matching some paths in the filesystem.
On the left hand side of a patsubst
, where %
denotes a match, it resembles *
in that it matches some characters. However, %
carries some restrictions, such as that it can only appear once! For instance whereas we can expand the wildcard */*.c
, of course, we cannot have a double stem pattern substitution like $(patsubst %/%.o,%/foo/%.c,...)
. This restriction could be lifted in some future version of GNU Make, but it currently holds as far as I know.
Also there is a subtle difference between %
and *
in that %
matches a nonempty sequence of characters. The wildcard pattern fo*o.c
matches foo.c
. The substitution pattern fo%o.c
does not match foo.c
, because then the stem %
would be empty which is not allowed.
The wildcard character *
is used to simply generate a list of matching files in the current directory. The pattern substitution character %
is a placeholder for a file which may or may not exist at the moment.
To expand on the Wildcard pitfall example from the manual which you had already discovered,
objects = *.o
The proper way to phrase that if there no '.o' files is something like
objects := $(patsubst %.c,%.o,$(wildcard *.c))
make
itself performs no wildcard expansion in this context, but of course, if you pass the literal value *.o
to the shell, that's when expansion happens (if there are matches) and so this can be slightly hard to debug. make
will perform wildcard expansion in the target of a rule, so you can say
foo: *.o
and have it work exactly like you intended (provided the required files are guaranteed to exist at the time this dependency is evaluated).
By contrast, you can have a rule with a pattern placeholder, which gets filled in with any matching name as make
tries to find a recipe which can be used to generate a required dependency. There are built-in rules like
%.o: %.c
$(CC) $(CCFLAGS) $^ -o $@
(approximating the real thing here) which say "given a file matching %.c
, the corresponding file %.o
can be generated as follows." Here, the %
is a placeholder which can be replaced by anything; so if it is applied against an existing file foo.c
it says how foo.o
can be generated.
You could rephrase it to say *
matches every matching file while %
matches any matching file.