You are on page 1of 16

Chris' Makefile Tutorial

Chris Serson University of Victoria June 26, 2007

Contents:
Chapter Introduction 1 The most basic of Makefiles 2 Syntax so far 3 Making Makefiles Modular 4 Multi-file projects 5 Some more tidbits Conclusion Page 2 3 5 7 11 13 16

Introduction:
The purpose of this tutorial is to introduce new students to the wonders and terrors of Makefiles. When I was first introduced to these things, I didn't have a clue what was going on. I'd have to look over my friend's shoulder and copy his Makefile or copy and paste an old Makefile and hope I could just tweak it to run with my latest project. Even in 4th year, I had trouble with these things and I was always embarrassed to ask for help seeing as it is something I should have learned early on in my career as a student. My hope is that this tutorial will keep other students from suffering my fate. The idea behind having a Makefile is that you can save yourself a lot of typing of compiler commands. Rather than typing some crazy long string of flags and files, just type 'make' or 'make projectName' and away it goes.

Chapter 1 - The most basic of Makefiles:


The most basic of Makefiles is basically just a file that runs the same command you would normally type into the command line. For instance, you may type: > gcc -o myprog myfile.c which would run off and compile myfile.c to an executable called myprog. Instead of this, you could put that line in a Makefile (the name of the file is exactly that, 'Makefile' no extension) and then run: > make for the exact same effect. More useful still is the fact that you can create a Makefile with multiple targets: <--- in the file project1: myfile1.c gcc -o project1 myfile1.c project2: myfile2.c gcc -o project2 myfile2.c <--- end of file With this file, if you type: > make it will run the first compile command. But if you type: > make project2 it will run the compile command under the 'project2:' heading. Even better, lets add another target to our Makefile: <--- in the file ... clean: rm -f *.o project1 project2 <--- end of file Now if you type: > make clean it will delete all compiled files, giving you a clean workspace to recompile your source code from scratch. As a note, in case you didn't already know, '.o' files are 'object' files which contain compiled code which has yet to be linked together. We'll see this again in Chapter 4. Lets add another useful target:

4 <--- in the file all: project1 project2 ... <--- end of file If you add this line to the very top of your Makefile, now when you type: > make it will run your 'all:' target, which points to both of your 'project_' targets. Of course, you could add the 'all:' target anywhere in the file and you could compile all of your targets by typing: > make all but having it as the first target makes a certain amount of sense. Another option is to create a 'default:' target at the top of the file which you could leave empty if you wanted the default action to do nothing. <--- in the file default: ... <--- end of file In this case: > make would do nothing.

Chapter 2 - Syntax so far:


Up to now, we've done some things which may not yet be clear; so let's try to fix that by defining some basic syntax of a Makefile. A 'target' is the word listed before the colon. For instance, the statement 'project1:' is defining a target called 'project1'. As we've seen, you can call a target using: > make target which will run the compile command the target specifies. In a number of cases, there will be a list of files to the right of the target declaration. <--- in the file project1: myfile1.c myfile1.h myfile2.h <--- end of file This is the list of files the target needs - its dependencies. In the case of 'project1', we need the file 'myfile1.c' to compile. Note the space between the target and the dependencies. I think it is a good idea to put a 'tab' character in for that space. This is a good rule of thumb to use because the very next line MUST be indented with a 'tab' character. This next line is the actual command that gets run. <--- in the file project1: myfile1.c gcc -o project1 myfile1.c <--- end of file Something to remember here is that if 'myfile1.c' includes another file, that file will be compiled automatically using the above statement. However, you can use the list of dependencies as a guide to help you remember what files are needed for what, so adding those files to the list is a good thing. <--- in the file project1: myfile1.c extra.h myfile2.c gcc -o project1 myfile1.c <--- end of file

6 Assuming 'myfile1.c' includes both 'extra.h' and 'myfile2.c', the above lines will do the same thing as the previous incarnation but gives you more information about the structure of the project. Comments can be added to a Makefile by using the '#' character. For example: <--- in the file # this is a comment <--- end of file

Chapter 3 - Making Makefiles Modular:


Here's where things start to get more complicated. I'm going to introduce a bunch of short-cuts to let you make more modular Makefiles. By this I mean that you should be able to create Makefiles that can be copied and pasted into different projects and be able to modify them quickly and easily without having to dig through the actual guts of the Makefile. The first of these short-cuts is the macro. If you can program enough code that you would even consider needing a Makefile, you've probably run across constants before. Macros in a Makefile are basically the same thing as constants but with slightly different syntax. Once a macro has been declared, you can use if whereever you could normally use the value it contains. For example: <--- in the file # the constant CC=gcc # this defines a macro CC which stands for the # compiler we will be using for our code. project1: myfile1.c $(CC) -o project1 myfile1.c <--- end of file

8 Notice how the macro is declared and then called. To declare a macro, simple write 'macroname=value'. Most people seem to leave out white space, but it will be just as correct to leave spaces in (ie, 'macroname = value'). Here's another example: <--- in the file CC=gcc # use the macro to name a target EXE=project1 # then use that macro to add the target to a list of # targets TARGETS=$(EXE) project2 # let's use the macro to declare the target now $(EXE): myfile1.c $(CC) -o myfile1.c ... # let's use our TARGETS macro to list all of the possible # executable files we may have created. # this will delete 'project1' and 'project2' clean: rm -f *.o $(TARGETS) <--- end of file

9 Another example of a macro would be to create one called 'CFLAGS', or some such, and use it to define compiler flags you call quite often. For instance, your executables may always need the '-Wall' flag (this just changes setting relating to warning messages): <--- in the file CC=gcc CFLAGS=-Wall project1: myfile1.c $(CC) $(CFLAGS) project1 myfile1.c <--- end of file On to the next thing. There are a number of built in macros which can simplify the copy-and-paste method of making a Makefile: $@ -> this will copy the current target name. $< -> this will copy the FIRST file name in the dependency list. $^ -> this will copy ALL of the file names in the dependency list. There are also other built-in macros, but these are the three I actually find useful at this level. Here's an example: <--- in the file CC=gcc # example 1: project1: myfile1.c extra.h $(CC) -o project1 myfile1.c # same as project1: myfile1.c extra.h $(CC) -o $@ $^ # or project1: myfile1.c extra.h $(CC) -o $@ $<

10 # example 2: # this will not work. It will give a whole bunch of errors # since it tries to compile both files and myfile1.c already # includes myfile2.c. We effectively get a redefinition of # everything in myfile2.c. Note: it is possible to write # your code so this error won't actually happen and this # line will work. project1: myfile1.c myfile2.c $(CC) -o $@ $^ # this will work. It only takes the first file and compiles # it, linking in the other file automatically as appropriate. project1: myfile1.c myfile2.c $(CC) -o $@ $< <--- end of file

11

Chapter 4 - Multi-file projects:


Now that we have a grasp of all kinds of different crazy things to help make our Makefile more useful and reusable, we need to go that extra step and talk about multi-file projects. By this, I mean projects that include a number of different '.c' and '.h' files. We've already seen a couple of examples in the previous chapters that included multiple files and worked just fine; however, to continue on the thought of making these Makefiles more modular, we're going to look at a more stuctured method. Let's look at a more complex project: ->main.c // includes queue.h ->list.h ->list.c // includes list.h ->queue.h // includes list.h ->queue.c // includes queue.h The simple approach to compiling this: <--- in the file CC=gcc project: main.c queue.h queue.c list.h list.c $(CC) -o $@ $^ <--- end of file This will work just fine, but what an evil list of file dependencies. Let's try something different: <--- in the file CC=gcc project: main.o queue.o list.o $(CC) -o $@ $^ main.o: main.c queue.h $(CC) -c $^ queue.o: queue.h queue.c list.h $(CC) -c $^ list.o: list.h list.c $(CC) -c $^ <--- end of file This style of Makefile shows the structure of the code much better than the previous way. You can see that the list code and queue code are strictly separate from the main

12 program, but you can also see where one section of code is dependant on another. If you wanted, you could remove the 'queue.h' from 'main.o' and 'list.h' from 'queue.o' and it would make no difference; however it would not be as clear the link between each code set. So what exactly is going on? How does this work? What are those '.o' files? Basically, we've split the program up into a bunch of pieces called object files (the '.o' files). These files make up the entirety of the program but they don't actually do anything on their own because they are not complete programs. They depend on the other files to work. So now what we have to do is link them together. That's what happens when we make the 'project' target. When you make the 'project' it runs make on each of the '.o' file targets. If those '.o' files don't exist or are not up-to-date, they are recompiled and then linked together as a final step.

13

Chapter 5 - Some more tid-bits:


You should now have enough knowledge to pump out Makefiles for your standard, every day project. This chapter is dedicated to stuff that you may or may not find useful. These are things that I thought are pretty nifty. Splitting long lines up If you happen to have a line of text in your Makefile that is way to long to fit the average line width without scrolling, use the backslash character to split the line. This tells the make program that the line is continued on the next line. Example: <--- in the file ... bigproject: main.o queue.o stack.o list.o recursive.o \ fileio.o database.o network.o ... <--- end of file Multi-folder projects Let's say you have a big project and you want to keep your headers in one folder (/headers) and your source (/source) in another; and maybe your object files (the '.o's) should be in their own folder as well (/obj). No problem. Just make sure your includes are correct in your code and add another macro: <--- in the file HDIR=headers SDIR=source ODIR=obj project: $(ODIR)/main.o $(ODIR)/queue.o $(ODIR)/list.o $(CC) -o $@ $^

$(ODIR)/main.o: $(SDIR)/main.c $(HDIR)/queue.h $(CC) -c $^ $(ODIR)/queue.o: $(HDIR)/queue.h $(SDIR)/queue.c $(HDIR)/list.h $(CC) -c $^ \

14 $(ODIR)/list.o: $(HDIR)/list.h $(SDIR)/list.c $(CC) -c $^ <--- end of file Condensing your targets You may find all those '.o' targets really annoying. We can get rid of them, but we'll lose a lot of readability. You can do this using the '%' character as below: <--- in the file project: main.o queue.o list.o $(CC) -o $@ $^ %.o: %.c $(CC) -c $^ <--- end of file What this does is say that every .o file is compiled from a .c file of the same name. The COMPILEOBJ macro I thought this little macro would be handy for replacing all of those nasty strings of characters you get with every object target. Mind you, this isn't really as useful if you're using the previous tid-bit; however, it does still move all of the things you're likely to change to the top of the file. This is a good technique since you don't want to go digging through the file every time you make a change. You can make a COMPILEEXE macro in the same manner: <--- in the file CC=gcc COMPILEOBJ=$(CC) -c $^ CREATEEXE=$(CC) -o $@ $^ project: main.o queue.o list.o $(CREATEEXE) %.o: %.c $(CREATEOBJ) <--- end of file

15 The OBJS macro This one is probably pretty obvious, but I'll mention it anyway. Rather than writing all of the object files directly in the main project's dependency list, build another macro at the top of the file: <--- in the file CC=gcc COMPILEOBJ=$(CC) -c $^ CREATEEXE=$(CC) -o $@ $^ OBJS=main.o queue.o list.o project: $(OBJS) $(CREATEEXE) %.o: %.c $(CREATEOBJ) <--- end of file LFLAGS vs CFLAGS We've seen the CFLAGS macro used before to define the flags used in relation to the compiler. But if you've noticed, we use different flags to compile the object files than we do to link them together. You may also want to consider a DEBUG macro that contains flags relating to any debugging you are doing: <--- in the file CC=gcc DEBUG=-g # as an example. You can look up what this does CFLAGS=-Wall -c $(DEBUG) LFLAGS=-Wall $(DEBUG) -o # the -o doesn't actually do # anything # other than rename the executable # and could be left out of LFLAGS but # I've added it in to illustrate my # point. COMPILEOBJ=$(CC) $(CFLAGS) $^ CREATEEXE=$(CC) $(LFLAGS) $@ $^ OBJS=main.o queue.o list.o project: $(OBJS) $(CREATEEXE) %.o: %.c $(CREATEOBJ) <--- end of file

16

Conclusion:
So by now you should have a fair understanding of how a Makefile works. I've tried to simplify things and give lots of examples while still giving you a lot to work with. I hope I've succeeded, but if I haven't then below are a few websites which I used as references. Also, a websearch for "Makefile Tutorial" will turn up lots of helpful information. 1. http://palantir.swarthmore.edu/maxwell/classes/tutorials/maketutor/ 2. http://www.hsrl.rutgers.edu/ug/make_help.html 3. http://www.cs.umd.edu/class/spring2002/cmsc214/Tutorial/makefile.html

You might also like