I have the following project structure:
common
|-- foo.cpp
|-- foo.h
exercise_1
|-- main.cpp
|-- bar_1.cpp
|-- bar_1.h
exercise_2
|-- main.cpp
|-- bar_2.cpp
|-- bar_2.h
...
How can one organize Makefile
to build such project from the main directory e.g.:
make exercise_10
So that this command would build object files in common
directory, in exercise_10
folder and link them all to executable in exercise_10
. I started with the following:
COMPILER = g
INCLUDE = -I common/
DEPS = common/*.o
OBJECTS := $(patsubst common/%.cpp, common/%.o, $(wildcard common/*.cpp))
common: $(OBJECTS)
exercise_%:
$(COMPILER) $@/main.cpp $(INCLUDE) -o $@/main $(DEPS)
But it's not working and I don't know what to do next.
Thanks!
CodePudding user response:
If you use GNU make you could define a macro to build any of your exercises. Something like the following:
EXERCISES := $(wildcard exercise_*)
MAINS := $(addsuffix /main,$(EXERCISES))
.PHONY: all
all: $(MAINS)
common-objs := $(patsubst %.cpp,%.o,$(wildcard common/*.cpp))
common-headers := $(wildcard common/*.h)
%.o: %.cpp $(common-headers)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -Icommon -c $< -o $@
# $(1): exercise directory
define BUILD_EXERCISE
.PHONY: $(1)
$(1): $(1)/main
$(1)-objs := $$(patsubst %.cpp,%.o,$$(wildcard $(1)/*.cpp))
OBJS = $$($(1)-objs)
$(1)-headers := $$(wildcard $(1)/*.h)
$$($(1)-objs): $$($(1)-headers)
$(1)/main: $$($(1)-objs) $$(common-objs)
$$(CXX) $$(CXXFLAGS) $$(LDFLAGS) -o $$@ $$^ $$(LDLIBS)
endef
$(foreach e,$(EXERCISES),$(eval $(call BUILD_EXERCISE,$(e))))
.PHONY: clean
clean:
rm -f $(MAINS) $(OBJS) $(common-objs)
It looks a bit complicated but it's not. The only trick is the $$
in the BUILD_EXERCISE
macro. It is needed because the macro is expanded twice by make. Everything else is straightforward:
CXX
,CPPFLAGS
,CXXFLAGS
,LDFLAGS
andLDLIBS
are Variables Used by Implicit Rules.$@
,$<
and$^
are Automatic Variables.wildcard
,addsuffix
,patsubst
,foreach
,eval
andcall
are make functions.- Phony targets are declared as prerequisites of the
.PHONY
special target. - The
foreach-eval-call
construct is a way to programmatically instantiate make statements. %.o: %.cpp...
is a pattern rule.