Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Good practices of Makefile

Tags:

makefile

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.

like image 450
Paulius K. Avatar asked Dec 03 '12 17:12

Paulius K.


People also ask

What is makefile good for?

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.

What is $@ in makefile?

The variable $@ represents the name of the target and $< represents the first prerequisite required to create the output file.

What are rules in makefiles?

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.

What are typical targets in a makefile?

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.


1 Answers

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.

like image 96
Chris Desjardins Avatar answered Sep 21 '22 08:09

Chris Desjardins