Jump to content

My standard Makefile ;-)


blizz

Recommended Posts

This is what I basically use for my C++ stuff on linux. I'm using this one for some SDL, GL, boost gadgetry, so just replace it with the includes paths/libraries you need in your build.

Why am I writing my own Makefile? I hate GNU auto*! It messes things up. But maybe I'll have to write my own GNU-like configure script later, anyway.

I will add debugging build capabilities and a switch for binary/library building later, also I want to include windows support and add support for other languages (C at least. Of course, you just gotta replace the g++ in the CXX variable, but i want to do it more elegant with traditional CC and CCFLAGS). I will also add an EXTENSION variable then for the file endings.

Place it in the root directory of your sources and it will compile any of your .cpp files to objects and link them together to one big binary. It will of course detect changes to all your source (.cpp) and header files (.hpp, but works with any of them, just include them somewhere).

If you want to build a shared library you should just leave out any main function, add -fPIC -shared to the LDFLAGS and call your target libsomething.so. Also adjust the architecture flag -march to fit yours ;-)

If you do not want to compile all the objects in the root directory, just add a second parameter to the collect call, this will only collect cpp files recursively from that directory.

Oh and tribute goes to Volkard Henkel who wrote the kinda sick collect macro ;-)

CXX = g++

CXXFLAGS = -g -W -Wall -Werror -pipe -O2 -march=athlon64



INCLUDE_PATH = -I/usr/include/GL -I/usr/include/SDL



LIBRARIES = GL GLU SDL boost_signals

LIBRARY_PATH = -L/usr/X11R6/lib

LDFLAGS = $(LIBRARY_PATH) $(addprefix -l,$(LIBRARIES))



SOURCES = $(call collect,%.cpp)

OBJECTS = $(patsubst %.cpp,%.o,$(SOURCES))

DEPS    = $(patsubst %.cpp,%.dep,$(SOURCES))



collect = $(foreach n,$(wildcard $(2)*),$(filter $(1),$(n)) $(call collect,$(1),$(n)/))



TARGET = happycat



all: $(TARGET)



$(TARGET): $(OBJECTS) $(DEPS)

    $(CXX) $(LDFLAGS) -o $@ $(OBJECTS)



-include $(DEPS)



%.o: %.cpp

    $(CXX) $(CXXFLAGS) $(INCLUDE_PATH) -o $@ -c $<



%.dep: %.cpp

    $(CXX) $(CXXFLAGS) -MM $< -MT $(<:.cpp=.o) > $@ 



clean:

    rm -rf $(OBJECTS) $(DEPS) $(TARGET)



.PHONY: all clean

I wonder if anyone is also interested in how Makefiles work? It's basically a very old but cool system to create incremental builds. FYI, incremental means that the whole thing doesn't have to compile everything again and again, it will just compile the parts that changed and link them back together.

Link to comment
Share on other sites

CXXFLAGS = -g -W -Wall -Werror -pipe [b]-O2 -march=athlon64[/b]

That stuff isn't something you should put in your Makefile. Let the user decide if he wants to use that. Just append or prepent the current value of CXXFLAGS with those other parameters.

LDFLAGS = $(LIBRARY_PATH) $(addprefix -l,$(LIBRARIES))



SOURCES = $(call collect,%.cpp)

OBJECTS = $(patsubst %.cpp,%.o,$(SOURCES))

DEPS    = $(patsubst %.cpp,%.dep,$(SOURCES))



collect = $(foreach n,$(wildcard $(2)*),$(filter $(1),$(n)) $(call collect,$(1),$(n)/))

I have *NO* idea what this 'call collect' and 'patsubst' stuff is supposed to do. Looking over it I'm starting to see a _little_ bit of logic in their use, but it's still mystifying to me.

To be completely honest, I prefer Ant's build.xml scripts. *FAR* more readable, and doesn't mind the indentation.

So, in closing, a few comments in that file that explains what's going on might be prudent.

Link to comment
Share on other sites

CXXFLAGS = -g -W -Wall -Werror -pipe [b]-O2 -march=athlon64[/b]

That stuff isn't something you should put in your Makefile. Let the user decide if he wants to use that. Just append or prepent the current value of CXXFLAGS with those other parameters.

I agree, that is also something I wanted to add. That's what configure usually does.

LDFLAGS = $(LIBRARY_PATH) $(addprefix -l,$(LIBRARIES))



SOURCES = $(call collect,%.cpp)

OBJECTS = $(patsubst %.cpp,%.o,$(SOURCES))

DEPS    = $(patsubst %.cpp,%.dep,$(SOURCES))



collect = $(foreach n,$(wildcard $(2)*),$(filter $(1),$(n)) $(call collect,$(1),$(n)/))

I have *NO* idea what this 'call collect' and 'patsubst' stuff is supposed to do. Looking over it I'm starting to see a _little_ bit of logic in their use, but it's still mystifying to me.

To be completely honest, I prefer Ant's build.xml scripts. *FAR* more readable, and doesn't mind the indentation.

So, in closing, a few comments in that file that explains what's going on might be prudent.

I thought I'd leave out any comments, but yes I originally wanted to explain my Makefile, too, or better explain how make works in general. Let me write that up.

Link to comment
Share on other sites

Soo, let me explain the whole thing:

(Lots of stuff kinda copied from manuals, too!)

CXX = g++

CXXFLAGS = -g -W -Wall -Werror -pipe -O2 -march=athlon64

Two compiler specific variables have been assigned here:

  • CXX The C++ compiler we want to use. g++ is the GNU project's C++ compiler.
  • CXXFLAGS these are flags which will be passed to the compiler above. -g generates debugging information which can be used by gdb (maybe also by other debuggers, I don't know). -W -Wall -Werror enables all kinds of errors and warnings during the compile process. -pipe should decrease the time needed for compiling, as pipes should be faster than temp files for communication between the different stages of compilation. -O2 will enable certain speed optimizations. -march=athlon64 will enable all architecture specific features an Athlon 64 architecture is able to understand (SSE2 for example).

INCLUDE_PATH = -I/usr/include/GL -I/usr/include/SDL

Here we defined the INCLUDE_PATH, so the compiler will know where it can look for headers which have been included in our files. (e.g.: #include <SDL.h>)

LIBRARIES = GL GLU SDL boost_signals

LIBRARY_PATH = -L/usr/X11R6/lib

LDFLAGS = $(LIBRARY_PATH) $(addprefix -l,$(LIBRARIES))

This are some informations for the linker. We basically have a LIBRARY_PATH which is quite similar to the include path - the only difference here is, that g++ will look for libraries in those directories.

LIBRARIES is simply a list of words which we need later for LDFLAGS.

LDFLAGS finally concatenates the LIBRARY_PATH and it does *something* with LIBRARIES. The addprefix macro will take a list of words (LIBRARIES here) and add prefixes. So GL GLU SDL will be expanded to -lGL -lGLU -lSDL! -l means that the linker should take those libraries into concideration when linking together objects. The resulting binary/library will carry that information and be able to use it.

SOURCES = $(call collect,%.cpp)

OBJECTS = $(patsubst %.cpp,%.o,$(SOURCES))

DEPS    = $(patsubst %.cpp,%.dep,$(SOURCES))



collect = $(foreach n,$(wildcard $(2)*),$(filter $(1),$(n)) $(call collect,$(1),$(n)/))

Yeah, that one's a bitch. First of all, SOURCES will call the collect macro. What does it do? Well, it will search in a certain directory for files with certain extensions. You can define both the root directory of its search AND the files it should find. I'll explain later.

For now, imagine that SOURCES is defined as "main.cpp foo.cpp bar.cpp".

OBJECTS merely takes SOURCES and replaces the .cpp with .o. So how will OBJECTS look like? Right, "main.o foo.o bar.o".

DEPS is the same, except it will be "main.dep foo.dep bar.dep".

Soo, how does collect work? First of all, it will search for all files and directories in the current dir (or the one you passed to it as second parameter). Then it will iterate over that list and do something with every element. If the element contains $(1), which is our file extension .cpp, it will add it to the result. Regardless of what it is, call collect on it again. Yesyes, it's recursive! And if our item happens to be a subdirectory, we'll collect the .cpp files it contains. And so on.

I'd explain the rest of it later. Or maybe even in the Wiki, because then I (and others too) can improve it and make it more powerful. Ultimate Makefiles will lead to ultimate world domination!

Link to comment
Share on other sites

i'm curious about the need to specify architecture or maybe i mean the architecture itself. I can understand if your calling specific processor functions, but for simple code is it really necessary? I don't claim to know any C++ and i only know a little C but i would think the compiler would handle the processor pipes and all that jazz and scale 64 down to 32.

I guess i mean 'Hello World!' will compile on any system providing you use the right compiler, won't it? or is it written differently in 64 bit?

i'm not entirely up on the 64 bit processors yet.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...