I have source in a bunch of subdirectories like:
src/widgets/apple.cpp src/widgets/knob.cpp src/tests/blend.cpp src/ui/flash.cpp
In the root of the project I want to generate a single Makefile using a rule like:
%.o: %.cpp $(CC) -c $< build/test.exe: build/widgets/apple.o build/widgets/knob.o build/tests/blend.o src/ui/flash.o $(LD) build/widgets/apple.o .... build/ui/flash.o -o build/test.exe
When I try this it does not find a rule for build/widgets/apple.o. Can I change something so that the %.o: %.cpp is used when it needs to make build/widgets/apple.o ?
# Register all subdirectories in the project's root directory. SUBDIRS := $(wildcard */.) # Recurse `make` into each subdirectory. $(SUBDIRS): FORCE $(MAKE) -C $@ # A target without prerequisites and a recipe, and there is no file named `FORCE`.
$@ is the name of the target being generated, and $< the first prerequisite (usually a source file). You can find a list of all these special variables in the GNU Make manual.
Yes, a Makefile can have a directory as target. Your problem could be that the cd doesn't do what you want: it does cd and the git clone is carried out in the original directory (the one you cd ed from, not the one you cd ed to). This is because for every command in the Makefile an extra shell is created.
CFLAGS and CXXFLAGS are either the name of environment variables or of Makefile variables that can be set to specify additional switches to be passed to a compiler in the process of building computer software.
The reason is that your rule
%.o: %.cpp ...
expects the .cpp file to reside in the same directory as the .o your building. Since test.exe in your case depends on build/widgets/apple.o (etc), make is expecting apple.cpp to be build/widgets/apple.cpp.
You can use VPATH to resolve this:
VPATH = src/widgets BUILDDIR = build/widgets $(BUILDDIR)/%.o: %.cpp ...
When attempting to build "build/widgets/apple.o", make will search for apple.cpp in VPATH. Note that the build rule has to use special variables in order to access the actual filename make finds:
$(BUILDDIR)/%.o: %.cpp $(CC) $< -o $@
Where "$<" expands to the path where make located the first dependency.
Also note that this will build all the .o files in build/widgets. If you want to build the binaries in different directories, you can do something like
build/widgets/%.o: %.cpp .... build/ui/%.o: %.cpp .... build/tests/%.o: %.cpp ....
I would recommend that you use "canned command sequences" in order to avoid repeating the actual compiler build rule:
define cc-command $(CC) $(CFLAGS) $< -o $@ endef
You can then have multiple rules like this:
build1/foo.o build1/bar.o: %.o: %.cpp $(cc-command) build2/frotz.o build2/fie.o: %.o: %.cpp $(cc-command)
This does the trick:
CC := g++ LD := g++ MODULES := widgets test ui SRC_DIR := $(addprefix src/,$(MODULES)) BUILD_DIR := $(addprefix build/,$(MODULES)) SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.cpp)) OBJ := $(patsubst src/%.cpp,build/%.o,$(SRC)) INCLUDES := $(addprefix -I,$(SRC_DIR)) vpath %.cpp $(SRC_DIR) define make-goal $1/%.o: %.cpp $(CC) $(INCLUDES) -c $$< -o $$@ endef .PHONY: all checkdirs clean all: checkdirs build/test.exe build/test.exe: $(OBJ) $(LD) $^ -o $@ checkdirs: $(BUILD_DIR) $(BUILD_DIR): @mkdir -p $@ clean: @rm -rf $(BUILD_DIR) $(foreach bdir,$(BUILD_DIR),$(eval $(call make-goal,$(bdir))))
This Makefile assumes you have your include files in the source directories. Also it checks if the build directories exist, and creates them if they do not exist.
The last line is the most important. It creates the implicit rules for each build using the function make-goal
, and it is not necessary write them one by one
You can also add automatic dependency generation, using Tromey's way
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With