I have a following directory structure in my project:
bin/
dist/
include/
├── module_a/
└── module_b/
Makefile
src/
├── module_a/
└── module_b/
Folder include/
contains *.hpp
's while *.cpp
's are in src/
. I would like to compile all sources to bin/
and then link them up together to dist/
. Seems a pretty reasonable wish for me.
I would like to know the best practices for a Makefile for this case. All I can find is %.o: %.cpp
target, but that doesn't really work, because of different source and binary folder.
I was trying to use something like this:
D_SRC = src
D_BIN=bin
F_CPP := $(shell find $(D_SRC) -iname '*.cpp' -type f)
F_OBJ := $(shell echo $(F_CPP) | sed s:\ :\\n:g | sed s:$(D_SRC):$(D_BIN): | sed 's:^\(.*\)\.cpp$$:\1\.o:')
$(F_OBJ): $(F_SRC)
$(foreach file, $(F_SRC), \
$(GXX) $(CXXFLAGS) -c $(file)\
)
This target doesn't work, because $(F_OBJ)
paths start with bin/
, while foreach
compiles sources to current working dir. I could make it compile to bin/
, but that would happen only with a few more sed
expressions and it's ugly enough as it is.
It's probably so difficult for me, because I don't know make
all that well, but I cannot be the only one with this project setup. In my opinion, it must be a pretty common one. I know I can write a Makefile
for each module separately, but is that really the best choice here?
EDIT: I was now wondering what would I achieve with several Makefiles. If one was at root and another one in src/module_a
, how would the latter know about the bin/
? If you'd execute it with make -f src/module_a/Makefile
, it would be the same as executing it from root directory, 'cause it's working directory would be root. Another way, I guess, would be to change directory before executing it, like so: make -C include/module_a
, but in that case, how would it find bin/
? I wouldn't want to have something like D_BIN = ../../bin
in a Makefile.
A makefile is useful because (if properly defined) allows recompiling only what is needed when you make a change. In a large project rebuilding the program can take some serious time because there will be many files to be compiled and linked and there will be documentation, tests, examples etc.
The variable $@ represents the name of the target and $< represents the first prerequisite required to create the output file.
A rule appears in the makefile and says when and how to remake certain files, called the rule's targets (most often only one per rule). It lists the other files that are the dependencies of the target, and commands to use to create or update the target.
By default, the goal is the first target in the makefile (not counting targets that start with a period). Therefore, makefiles are usually written so that the first target is for compiling the entire program or programs they describe.
What I normally do is have a Makefile in the src directory (which can be invoked from the top level Makefile if you like) and then use rules like this:
D_BIN = ../bin
$(D_BIN)/%.o: %.cpp
You could also experiment with just a makefile in the top level dir, and use rules that look like this:
D_BIN = bin
D_SRC = src
$(D_BIN)/%.o: $(D_SRC)/%.cpp
but I have not used such rules, so I don't know the pros/cons vs the way I normally do it. The way I normally do it works fine, I even have rules that build depends like so:
$(D_BIN)/%.d: %.cpp
and the link rule would be like:
../dist/outexe: $(F_OBJ)
Using a foreach is usually frowned upon because it does not make use of all the features built into normal makefile rules (i.e. there is no depends check on a per file basis, either you build everything or nothing), and as such foreach should only be used as a last resort, but in this case you will be able to get it to work without the foreach.
In addition to this there are much easier ways to build your file lists, you don't need to use the shell or sed.
F_CPP = $(wildcard *.cpp)
F_OBJ = $(F_CPP:.cpp=.o)
Update: This is how I normally issue recursive makes:
SUBDIRS = src
.PHONY: $(SUBDIRS)
all: $(SUBDIRS)
$(SUBDIRS):
@echo "Building $@..."
$(MAKE) -C $@ $(MFLAGS)
Then indeed in your submake, you would need to use ../bin for example.
However with a project as simple as yours, you might be better off just having one makefile at the root level and using rules like this:
D_BIN = bin
D_SRC = src
$(D_BIN)/%.o: $(D_SRC)/%.cpp
recursive makefiles are ok (ok but not great) if you have a really complex directory structure, where you will be adding/removing/modifying new dir trees as time goes on. But for a simple project where you just want to have separate directories for code and objs, it is probably overkill.
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