Reputation: 694
In the following Makefile, I'm modifying the content of $(SRC) based on whether the target rule is 'test'. This rule is supposed to be for unit testing.
My objects are built with the implicit rule
OBJ = $(SRC:.cpp=.o)
but when building the object, even if the SRC variable changes, it always uses its default value. Here is the Makefile :
CC = g++
SRC_DIR = src
SRC_TEST_DIR = tests/src
INC_DIR = include/
test: INC_DIR += tests/include/
SRC_MAIN = $(SRC_DIR)/main.cpp
test: SRC_MAIN = $(SRC_TEST_DIR)/main.cpp
SRC = $(SRC_MAIN) \ # <=== The value that changes in SRC
$(SRC_DIR)/minicalc.cpp \
$(SRC_DIR)/io.cpp
OBJ = $(SRC:.cpp=.o)
CPPFLAGS += -I$(INC_DIR) -g -Wall -Wextra
test: LDFLAGS += -lcppunit
OUT = minicalc
test: OUT = tests/tests
all: $(OBJ)
# DEBUG :
@echo SRC: '$(SRC)'
@echo OBJ: '$(OBJ)'
$(CC) $(OBJ) -o $(OUT) $(LDFLAGS)
test: all
ls $(OUT) && ./$(OUT)
Here are the outputs :
make
gives expected output with default value of SRC :
g++ -Iinclude/ -g -Wall -Wextra -c -o src/main.o src/main.cpp
g++ -Iinclude/ -g -Wall -Wextra -c -o src/minicalc.o src/minicalc.cpp
g++ -Iinclude/ -g -Wall -Wextra -c -o src/io.o src/io.cpp
SRC: src/main.cpp src/minicalc.cpp src/io.cpp
OBJ src/main.o src/minicalc.o src/io.o
g++ src/main.o src/minicalc.o src/io.o -o minicalc
But make test
doesn't build test/src/main.o even if it is in SRC :
g++ -Iinclude/ -g -Wall -Wextra -c -o src/main.o src/main.cpp
g++ -Iinclude/ -g -Wall -Wextra -c -o src/minicalc.o src/minicalc.cpp
g++ -Iinclude/ -g -Wall -Wextra -c -o src/io.o src/io.cpp
SRC: tests/src/main.cpp src/minicalc.cpp src/io.cpp
OBJ: tests/src/main.o src/minicalc.o src/io.o
g++ tests/src/main.o src/minicalc.o src/io.o -o tests/tests -lcppunit
g++: error: tests/src/main.o: No such file or directory
make: *** [Makefile:41: all] Error 1
all
requires $(OBJ)
, but the object created are not corresponding to the content of the $(SRC)
variable
Not using the implicit .cpp.o rules by doing it manually WORKS :
Same Makefile but replaced $(OBJ)
by the rule create_obj
CC = g++
SRC_DIR = src
SRC_TEST_DIR = tests/src
INC_DIR = include/
test: INC_DIR += tests/include/
SRC_MAIN = $(SRC_DIR)/main.cpp
test: SRC_MAIN = $(SRC_TEST_DIR)/main.cpp
SRC = $(SRC_MAIN) \
$(SRC_DIR)/minicalc.cpp \
$(SRC_DIR)/io.cpp
OBJ = $(SRC:.cpp=.o)
CPPFLAGS += -I$(INC_DIR) -g -Wall -Wextra
test: LDFLAGS += -lcppunit
OUT = minicalc
test: OUT = tests/tests
all: create_obj
# DEBUG :
@echo SRC: '$(SRC)'
@echo obj: '$(OBJ)'
$(CC) $(OBJ) -o $(OUT) $(LDFLAGS)
create_obj:
@echo Crafting Files:
@$(foreach file, $(SRC), \
echo $(file),; \
$(CC) -c $(file) -o $(file:.cpp=.o) $(CPPFLAGS) $(LDFLAGS); \
)
test: all
ls $(OUT) && ./$(OUT)
Here are the outputs :
make
:
Crafting Files:
src/main.cpp,
src/minicalc.cpp,
src/io.cpp,
SRC: src/main.cpp src/minicalc.cpp src/io.cpp
OBJ: src/main.o src/minicalc.o src/io.o
g++ src/main.o src/minicalc.o src/io.o -o minicalc
make test
Crafting Files:
tests/src/main.cpp,
src/minicalc.cpp,
src/io.cpp,
SRC: tests/src/main.cpp src/minicalc.cpp src/io.cpp
OBJ tests/src/main.o src/minicalc.o src/io.o
g++ tests/src/main.o src/minicalc.o src/io.o -o tests/tests -lcppunit
ls tests/tests && ./tests/tests
tests/tests
OK (0)
Why does the implicit building of my objects not change when changing my sources, while doing it manually works ?
Upvotes: 0
Views: 152
Reputation: 2298
At the risk of overkill, here's how I would have written it. Personal guidelines:
%.o: %.cpp; ...
) until forced not to.make -C${PLATFORM}/${CONFIG} ...
all : minicalc ;@: # ... or ".PHONY: all" test : mctests ; ls $^ && ./$^ minicalc : minicalc.o io.o main.o # minicalc.o is subtly redundant here. mctests : minicalc.o io.o mctests.o # ... likewise mctests.o is redundant. mctests.o : tests/src/main.cpp # Name difference forces an explicit action below. vpath %.cpp src CC = ${CXX} # Roundabout fix for MAKE using ${CC} for linking # ... but cc/gcc cannot handle C++ object files. CXXFLAGS += -Iinclude -g -Wall -Wextra mctests.o : CXXFLAGS += -Itests/include mctests : LDLIBS += -lcpptest # More accurately LDLIBS not LDFLAGS mctests.o :; ${COMPILE.cpp} -o $@ $^
Upvotes: 1
Reputation: 2298
In order for make to initially find all dep relations, it must expand vars in whatever state they are at that point, in the initial scan. So all : $(OBJ)
is evaluated with the global value, and not reevaluated in the context of test : all
. Sorry. Make's lazy eval does not play well with context-dependent var assignment.
Upvotes: 1