Makefiles are daunting

Started by
8 comments, last by Valeranth 15 years, 5 months ago
Recently my windows development machine was rendered useless...due to a power jack problem. Because of this I moved to an old iMac G4 for everything, but do to it's lack of speed(700Mhz) and memory, it's uncomfortable to use my favorite IDE for development. I decided I'd try using Makefiles and a fancy text editor writing code, but was wondering if anyone here could help me with my makefile.
#NinjaGame Makefile
#for Unix-Based systems where memory is an issue
DIR_SRC=src
DIR_INC=include
DIR_LIB=lib/osx
DIR_GAME=game
CXX=g++
CXX_FLAGS= -Wall -g
INCLUDES=-Ilib/osx/ -Ilib/osx/sdl/ -Iinclude
FRAMEWORKS=	-framework Carbon -framework Cocoa 			-framework OpenGL -framework AudioUnit 			-framework QuickTime -framework IOKit 			-framework SDL -framework SDL_mixer
LIBS=$(DIR_LIB)/libSOIL.a
DEFINES=-DPLATFORM_OSX

#a list of all the .cpp files
C_FILES=$(DIR_SRC)/fg.cpp $(DIR_SRC)/fgaudio.cpp $(DIR_SRC)/fggraphics.cpp 	$(DIR_SRC)/fglog.cpp $(DIR_SRC)/fgmessage.cpp $(DIR_SRC)/fgobject.cpp 	$(DIR_SRC)/fgsystem.cpp $(DIR_SRC)/fgxmldataobject.cpp $(DIR_SRC)/fgxmlgameobject.cpp 	$(DIR_SRC)/fgxmlsettings.cpp $(DIR_LIB)/xmlParser.cpp $(DIR_LIB)/SDLMain.m 	$(DIR_GAME)/gmplayer.cpp $(DIR_GAME)/main.cpp

all:
	$(CXX) -o build/NinjaGame $(INCLUDES) $(CXX_FLAGS) $(DEFINES) $(C_FILES) $(FRAMEWORKS) $(LIBS)
This is my first makefile, and although it works, I know it could be written much better. I've seen makefiles that use predefined rules to find .c/.cpp/.asm files and compile them to .o files, and then using another rule can use the .o files in the final compilation, but I can't seem to get the concept of some of these rules down. Anyone on here with experience writing makefiles care to help out a newbie to the world of make? Any help would be appreciated -Wynter Woods
Advertisement
I don't know anyone who writes non-trivial Makefiles by hand. You should really consider autoconf and automake if you want to use Makefiles. Or have a look at Scons for a non-Makefile based build system.

<hr />
Sander Marechal<small>[Lone Wolves][Hearts for GNOME][E-mail][Forum FAQ]</small>

I suggest either try Scons or even better CMake. CMake creates project files for many ide's.
Chalk up another vote for CMake. I've got a cross-platform project and CMake is a life saver. Before CMake when someone made a new file they'd have to add it to the Visual Studio project and the automake makefiles, of course people forgot every time.
Quote:Original post by zerotri
This is my first makefile, and although it works, I know it could be written much better. I've seen makefiles that use predefined rules to find .c/.cpp/.asm files and compile them to .o files, and then using another rule can use the .o files in the final compilation, but I can't seem to get the concept of some of these rules down. Anyone on here with experience writing makefiles care to help out a newbie to the world of make?

I'm glad you're taking this approach.

Before you jump to ubertools like automake or half-axed replacements like cmake or scons or jam or bjam or ant or a-a-p or ... you should get an understanding of make itself. Most of the most complex projects out there use make directly, and for a reason (although that reason is mostly that either the project predates the fancy tart-up tools or else the lead maintainer is pig-headed, but I digress).

First off, you need to let make do all the work. Your makefile defines what depends on what, and in edge cases haw to generate what from what, and make will take care of the rest. It already knows how to build an object file from C++ or objective-C, so let it do it's thing.

So, for a start, you should have a separate target for your executable. Don't have the 'all' rule build it, have the 'all' rule depend on your binary target. That way you could add additional targets, like bundling for distribution, generating docs, cleaning up before committing to your VCS and so forth, and be able to run them, all or individually as required.

Second, don't have your binary depend on source files directly: that will cause an awful lot of rebuilding all the time. It doesn't depend on the source files, it depends on the object (.o) files. The object files depend on the source files, which include the .cpp files and all of the headers that they include.

Most modern versions of make support the vpath directive, and I'm fairly certain the make on OS X is GNU make which definitely provides that directive, so you should take advantage of it.

Might I suggest you restructure your makefile into something like this (untested).
VPATH = src:lib/osx:gameOBJECTS = \   gf.o fgaudio.o fggraphics.o gflog.o fgmessage.o fgsystem.o \       fgxmldataobject.o fgxmlgameobject.o fgxmlsettings.o \    xmlParser.o SDLMain.m.o gmplayer.o main.oCXXFLAGS = \   -Wall -g   -Ilib/osx -Ilib/osx/sdl -Iinclude   -DPLATFORM_OSX# .. other make variable definitions here...all: build/NinjaGamebuild/NinjaGame: $(OBJECTS)	$(CXX) -o $@ $(LDFLAGS) $^ $(FRAMEWORKS) $(LIBS)

Note that variables like 'CXXFLAGS' and 'LDFLAGS' are built in to make, so they're automatically picked up by the built in rules after you redefine them. This small makefile also does not automatically generate .cpp file dependencies (that would be the next thing to add -- use $(CXX) -MM -MD and friends as appropriate).

Once you get a good grasp of how make works best, you can go on to use other tools that try to replace it, and be able to judge what they do better and what they lack. You can also go on to, say, read the Makefile used to build the Linux kernel or the GNU compiler toolchain.

Stephen M. Webb
Professional Free Software Developer

Thanks for the help Bregma. I figured there were newer, easier options out there like CMake and automake, but because I have seen make on just about every non windows system I've used(and even windows with cygwin) I figured it might be a better option for me to learn in order to allow me to develop even if I'm on a platform I'm not entirely used to(or can't install newer options on).

Also, thanks to Bregma I now have a slightly modified, but working makefile that can build and run the code. I was also able to discover a little about Make's special symbols.
$@ refers to the current target name, right?
and given the line:
clean: $(OBJECTS)

it would refer to the variable objects, right?
so I could then do
rm $^

to remove them, which seems to work here.

Thanks for all your help!
Some systems have "rm" aliased to "rm -i" (prompt on each file) so you may want to use "rm -f" instead.

<hr />
Sander Marechal<small>[Lone Wolves][Hearts for GNOME][E-mail][Forum FAQ]</small>

here is the make file I use, can be used in any project. It gets all the files in folder ./*/ and ./*/*/ ( meaning it is set up for a src/ folder to be made. might not be 'profesional quality' but w/e works

CC=g++CFLAGS= -g -D_DEBUG_ -WallLDFLAGS= -lSDL -lGL -lGLU -lpng -lboost_threadSRCDIR= srcSRC= $(wildcard $(SRCDIR)/*.cpp) $(wildcard $(SRCDIR)/*/*.cpp)OBJS= $(patsubst %.cpp, %.o, $(SRC))PROG= testbedcurrent: $(PROG)$(PROG): $(OBJS)        $(CC) $(LDFLAGS) $(OBJS) -o $(PROG) %.o: %.cpp %.h        $(CC) $(CFLAGS) -c $< -o $(patsubst %.cpp, %.o, $<)%.o: %.cpp        $(CC) $(CFLAGS) -c $< -o $(patsubst %.cpp, %.o, $<)run:        ./$(PROG)debug:        gdb $(PROG)clean:        rm */*.o


this file also accompanys it ( goes in the src/ folder and beyond )

DEPTH= ../compile:        cd $(DEPTH); makerun:        cd $(DEPTH); make runclean:        cd $(DEPTH); make cleandebug:        cd $(DEPTH); make debug


so from anywhere I can run 'make' to compile, 'make run' runs the program and 'make debug' will start gdb..
Just a couple of critiques.
Quote:Original post by Valeranth
$(PROG): $(OBJS)        $(CC) $(LDFLAGS) $(OBJS) -o $(PROG) 

Libraries should come after object files because the linker will only scan the libraries to resolve undefined symbols. Linking in this order is likely to fail with "undefined symbol" in many cases.
Quote:
%.o: %.cpp %.h        $(CC) $(CFLAGS) -c $< -o $(patsubst %.cpp, %.o, $<)

This naive way of depending on headers with the same name as the source file may pick up a few extra dependencies, but will fail you more often than not whenever a change in a header file is made.

I would also suggest that redefining the built-in rules for basic stuff like compiling should be avoided. Do you also reinvent the wheel every time you buy a new car? Do you really think you can do a better job with those rules than the many decades of people who have come before you?
Quote:
compile:        cd $(DEPTH); make

This could be better done using a single subprocess and built-in symbols that Do The Right Thing (like passing -n and $DESTDIR, for example).
compile:        $(MAKE) $(MAKEFLAGS) -C $(DEPTH)

Stephen M. Webb
Professional Free Software Developer

Quote:Original post by Bregma
Just a couple of critiques.
Quote:Original post by Valeranth
$(PROG): $(OBJS)        $(CC) $(LDFLAGS) $(OBJS) -o $(PROG) 

Libraries should come after object files because the linker will only scan the libraries to resolve undefined symbols. Linking in this order is likely to fail with "undefined symbol" in many cases.



so something like this?

$(PROG): $(OBJS)        $(CC) $(OBJS) $(LDFLAGS) -o $(PROG) 


Quote:
Quote:
%.o: %.cpp %.h        $(CC) $(CFLAGS) -c $< -o $(patsubst %.cpp, %.o, $<)

This naive way of depending on headers with the same name as the source file may pick up a few extra dependencies, but will fail you more often than not whenever a change in a header file is made.


yeah, I've kinda noticed that, but whats the right way to check for a change in a header file??

Quote:
I would also suggest that redefining the built-in rules for basic stuff like compiling should be avoided. Do you also reinvent the wheel every time you buy a new car? Do you really think you can do a better job with those rules than the many decades of people who have come before you?
Quote:
compile:        cd $(DEPTH); make

This could be better done using a single subprocess and built-in symbols that Do The Right Thing (like passing -n and $DESTDIR, for example).
compile:        $(MAKE) $(MAKEFLAGS) -C $(DEPTH)


Thanks for advice, but what do you mean about redefiing the built-in rules? If your talking about things like setting up things like CFLAGS, isnt that how you'r suppost to do this? Im pretty much a noob at makefiles :P

This topic is closed to new replies.

Advertisement