When make
parses a Makefile it will expand all the variables upon building a dependency tree for targets that are not implicit targets. So when you call make debug
it tries to build myprog
, which in turn tries to build obj/main.o
, not obj/debug/main.o
, since the $(OBJFILES)
has already been expanded when the rule for myprog
was parsed. That's why $@
expands to obj/main.o
even though the variable is correct. See for yourself:
$ make -dr debug
...
Considering target file 'debug'.
File 'debug' does not exist.
Looking for an implicit rule for 'debug'.
No implicit rule found for 'debug'.
Considering target file 'all'.
File 'all' does not exist.
Looking for an implicit rule for 'all'.
No implicit rule found for 'all'.
Considering target file 'myprog'.
File 'myprog' does not exist.
Considering target file 'obj/main.o'.
File 'obj/main.o' does not exist.
Looking for an implicit rule for 'obj/main.o'.
Trying pattern rule with stem 'main'.
Trying implicit prerequisite 'src/main.c'.
Found an implicit rule for 'obj/main.o'.
Considering target file 'src/main.c'.
Looking for an implicit rule for 'src/main.c'.
No implicit rule found for 'src/main.c'.
Finished prerequisites of target file 'src/main.c'.
No need to remake target 'src/main.c'.
Finished prerequisites of target file 'obj/main.o'.
Must remake target 'obj/main.o'.
mkdir -p ./obj/debug/
...
It is possible to make it work though. First of all, you would need to use second expansion to resolve dependencies at execution time. Second, you would need to make your $(NAME)
target an implicit rule somehow to let make
reevaluate the expression. Last, since your object location will differ (include debug
or not), it will also propagate to the source location, which should generally be the same. I was able to make it work like this:
$ cat Makefile
SRCFILES=./src/main.c ./src/file.c
# Get the filenames of all the sources
SRCNAMES=$(notdir $(SRCFILES))
OBJDIR=./obj/
# Replace .c to .o in all source names
OBJNAMES=$(SRCNAMES:.c=.o)
# Add the obj/ repository as prefix
OBJFILES=$(addprefix $(OBJDIR), $(OBJNAMES))
NAME=myprog
.SECONDEXPANSION:
all: $(CURDIR)/$(NAME)
# Implicit rule due to %
%/$(NAME): $$(OBJFILES)
gcc $(OBJFILES) -o $(NAME)
%.o: ./src/$$(notdir $$*).c
mkdir -p $(OBJDIR)
gcc -c $< -o $@
# If the make debug rule is called, I want my objects to be build into ./obj/debug/
debug: OBJDIR = ./obj/debug/
debug: all
Output:
$ make
mkdir -p ./obj/
gcc -c src/main.c -o obj/main.o
mkdir -p ./obj/
gcc -c src/file.c -o obj/file.o
gcc ./obj/main.o ./obj/file.o -o myprog
rm obj/main.o obj/file.o
$ rm -rf obj myprog
$ make debug
mkdir -p ./obj/debug/
gcc -c src/main.c -o obj/debug/main.o
mkdir -p ./obj/debug/
gcc -c src/file.c -o obj/debug/file.o
gcc ./obj/debug/main.o ./obj/debug/file.o -o myprog
rm obj/debug/main.o obj/debug/file.o
Personally I would also consider building final binaries with different name or location, otherwise you will never be sure what has been built.
OBJFILES=$(addprefix $(OBJDIR), $(OBJNAMES))
appear to be set set before you updatedebug: OBJDIR = ./obj/debug/
. You will need to make sure you have which directory to use set before you fill your...FILES
variables.OBJFILES
variable is properly updated as you can see line 5:gcc ./obj/debug/main.o ./obj/debug/file.o -o myprog
./obj/debug/main.o ./obj/debug/file.o
do not exist. They were compiled tocc -c src/main.c -o obj/main.o
above.-o $@
should be./obj/debug/main.o
since it come from$(OBJDIR)%.o
andOBJDIR
is redefined bydebug
before the call toall
. I can't really know in advance if the Makefile will be called withdebug
rule.make
, e.g.make with=debug
and then checkifeq ($(with), debug)
then update the directory locations. Otherwise ifmake debug
is supposed to handle that for you automatically -- I'd have to go back to the documentation and figure it out.