Automating Your Programs with Makefiles - C++ All-in-One For Dummies (2009)

C++ All-in-One For Dummies (2009)

Appendix A: Automating Your Programs with Makefiles

In This Appendix

Compiling and linking your programs

Automating your work

Implying work with inference rules in your Makefiles

Getting the most out of Makefiles

Since the beginning of time, or at least since the beginning of the Unix operating system, programmers have used a utility called make to build their programs. And it’s still often used today. The make utility looks at which of your source-code files have changed and decides what needs to be compiled and built.

Development tools, such as CodeBocks and Microsoft Visual C++, don’t require you to use a make utility because they have such decision-making features built in. But many free compilers use them. Fortunately, the process for creating and using make files is no longer as difficult as it once was. Third-party libraries come with make utilities (such as the Boost library’s Boost Jam and Boost Build; see Minibook VI, Chapter 3) that greatly reduce the complexity of creating and using make files.

Before using make, understanding the compile and link processes is important. In this appendix, we cover the compile and link processes and advise how to use make to automate your building. Please note, however, that make itself is a complex tool, and enough information is available about it to fill an entire For Dummies book. Therefore, we suggest that you don’t worry about mastering make and what are called Makefiles. Instead, read this appendix so you understand them. Then, if you work with Makefiles in your projects, start with an existing one. If you understand it, you can easily modify it for your project.

Compiling and Linking

When you create a program, you write your code in various source-code files. For one program, you can have many different source-code files. Some large corporate projects may have hundreds (or even thousands) of source-code files with dozens of programmers working on the different files. As you can imagine, dozens of strong-willed programmers working together makes for quite an adventure; but by using tools like make, these programmers are able to easily work together without a single disagreement ever taking place. Okay, we lied. But nevertheless, makemakes their lives easier.

To transform these source-code files into a single program, you need to compile and link them.

Compiling means transforming your C++ code (or whatever language you are using) into a machine-readable language called Assembler. The Assembler language differs among types of processors. Most of you reading this book are probably working on some version of an AMD or Intel processor, so the C++ compilers you use normally translate your programs into Assembler language appropriate for the processor on your system. (You can also tell the compiler to target some other processor in many cases.) The compiler stuffs all this Assembler code into a file called anobject file and typically names the file the same as the original source-code file but with an .obj or .o extension. For each source-code file, the compiler makes a single object file

After you have compiled all your source-code files, you run the linker. The linker takes the object files and combines them into a single file that you can run on your computer. This single file usually gets an .exe extension, which stands for executable. The origin of this term comes fromexecution, which refers to the running of a program and probably had something to do with what the user of the first computer program wanted to do to the programmer after using the program. But we’re only guessing.

The linker can produce other kinds of output file. For example, you can create a .lib (library) or .dll (Dynamic Link Library, or DLL) file. These files contain executable code, just as the .exe file does, but you don’t execute them as standalone files — an .exe file normally loads them and uses the executable code they contain.

The compiler also inserts into the object files a great deal of information in addition to the assembler code. For example, when you are still in the process of developing your program, you can instruct the compiler to put debug information in the file. (Although sometimes the debug information goes in a separate file; it depends on the compiler.) Debug information includes the names of your variables and the line numbers of the source code. When you use a debugger tool, that tool knows about your code by looking at the debug information.

The compiler also puts information about the code, such as the names of items that occur, in other source-code files. For example, if you are writing code that calls code in another source-code file, the compiler includes the name of that code in the object file. That way, the linker can connect the two.

image

Understanding external linkage

An example of where you may encounter external linkage is when code in one source-code file calls a function in another source-code file. When the compiler compiles the first source-code file, it doesn’t know where in the compiled assembler code the routine will be when it compiles the second source-code file. And even if the second source-code file is already compiled, the compiler isn’t particularly interested in digging around the other object file to figure out where it is. So instead, the compiler simply creates a reference to the routine. It places this reference inside the first object file.

Later, the linker replaces the reference with the address of the call, and that address is what will end up in the final executable. This address, however, is just temporary and serves as a placeholder until the final program is loaded into memory. When you run the executable and the program gets loaded into memory, a special application called the loader replaces the address with the actual memory address where the routine is located. Whew!

Your code may also call code that lives outside your program, such as a routine in a DLL. In that case, the compiler still puts the name of the routine inside the object file. But at link time, the linker includes a placeholder that notes the name of the item (or perhaps a unique number) and the name of the DLL file. The linker puts that information inside the executable file. When you run the executable file and the program gets loaded into memory, the computer first makes sure that the DLL is loaded (following the same steps here that it’s using to load the executable! Oh my!) and then replaces the information with the real live address of the DLL’s routine. Again, Whew!

Finally, a compiler can add resources to the object files, such as graphics and sounds that your application requires. These resources appear as data that the application accesses later. Data appears in separate areas from executable code within the file, but you don’t need to worry about the object file organization — the compiler takes care of managing both code and data for you.

Automating Your Work

When you’re developing a program and working with, say, ten different source-code files and you’re ready to compile and link your work, you could compile each source-code file separately and then link them all together. However, it would be nice if you had to compile only the source files that have changed since the last time you compiled. After all, if one of the source-code files was compiled to an object file earlier and hasn’t changed since the last compile, why bother compiling it again?

The solution is to use some kind of special program that checks to find out which source files have changed and compiles only those. Or if none of the source files have changed, the special program checks only to see whether any external libraries have changed and, if necessary, does only a link. Or if nothing has changed at all, do nothing and call the program up to date.

That is exactly what the make utility does. Using make, you can be sure that only the object files and executables that are out of date get updated. How does make do this? It just compares the dates on the files, that’s all. It looks at a source-code file and its associated object file. If no object file is associated with the source code, it definitely compiles the source-code files. If an object file is available, and the object file is newer than the source-code file, make knows that the source-code file hasn’t changed since the last compile, and therefore there’s no reason to recompile it. But if the source-code file is newer than the object file, it must have changed since the last compile, and thus the make utility compiles it.

To build your program by using make, you just type this at the console (that is, either the Unix prompt on Unix computers or the DOS window on Windows computers):

make

image Note that different make applications have different names. For example, Microsoft provides the nmake utility that you can read about at http://msdn.microsoft.com/en-us/library/dd9y37ha.aspx. When using these other kinds of make utilities, simply type its name instead ofmake.

If anything in your project needs building, make will do it. Otherwise, it prints the simple but sweet little message:

make: `myprogram’ is up to date.

Well, this is all fine and dandy, except for one little catch: the make utility needs something called a Makefile. The Makefile lists information about what exactly it’s supposed to make. Unfortunately, Makefiles aren’t exactly simple. The make utility and its Makefile concept was, after all, invented something like 30 years ago, back in the dark ages when computers were made of stone and cars had square wheels. But that’s okay. Today’s computers still handle Makefiles, and you can find out how to use them. The next section covers what exactly goes inside these animals. The things in the files are themselves interesting little animals called inference rules.

Implying with Inference Rules

Everybody loves rules, and so does the make utility. Before make knows what to make, you need to supply it with a set of rules. Programmers have decided to call these rules inference rules for lack of a better term. Well, actually, tons of better terms that take up less space are available. But they chose inference because the make utility will infer what it’s supposed to do based on the rules implied by the Makefile.

In general, an inference rule specifies the file you want to create (such as an object file) and the file or files it depends on (such as its associated source-code file). Next the rule states how exactly to create that file. For example, if you have an object file that depends on a source-code file, the way to create it is by running the compiler command.

A typical inference rule looks like this. The first line specifies the name of the file you want to create, then it has a colon, and then the files it depends on are listed. The next line starts with a tab and then lists the commands to run to create that new file, with one command per line. The following example is a rule for creating a text file:

test.out: test.txt

cp test.txt test.out

echo WORKED >> test.out

The first line means that we want to create a file called test.out, and it depends on the file called test.txt. If test.txt is newer than test.out (meaning it has changed since the last time this rule was run), make executes the two commands that follow. In this case, the first one copies test.txt into the test.out file, and the second one appends the word WORKED at the end of test.out.

image You may notice something interesting about the preceding inference rule. Instead of using the usual Windows/DOS command copy, here we used the command cp, which is the equivalent under Unix. Many of the free make utilities on the market, such as Cygwin, rely on Unix-style commands. If you prefer to work with the MinGW compiler or the Borland compiler, you can use the DOS copy command instead.

imageIf you want to try out this Makefile, create a text file called Makefile, and put the following in it:

test.out: test.txt

cp test.txt test.out

echo WORKED >> test.out

(Note that the second and third lines begin with a single tab; do not start the first line with any tabs.) Next, make a text file in the same directory and call the file test.txt. Finally, while in the same directory, type the following command:

make

Note that you will need to have a make command installed. For example, you can obtain Cygwin at http://www.cygwin.com/. Minibook VI, Chapter 3 describes the Boost Jam and Boost Build compilers. You can also obtain the Borland compiler fromhttp://www.codegear.com/downloads/free/cppbuilder.

imageIf you do some exploring, you are likely to discover that you can optionally name your Makefile something other than Makefile and then specify your filename in the make command. For most versions of make, you do this by typing

make -f filename

However, we do not recommend doing this. Most programmers always use the filename Makefile, and if you call yours something different, you are likely to rattle their chains a little. Computer people are not known for their flexibility; and if you do this, your coworkers probably won’t pay for your pizza this Friday.

Using rules that depend on other rules

Some special situations arise when working with inference rules. For example, test.out may not exist at all. In that case, the commands will definitely run because the idea is that this rule tells how to create a test.out file.

Another special situation deals with test.txt itself in the previous examples. Does it depend on anything? For example, another rule may say this:

test.txt: originalfile.txt

cp originalfile.txt test.txt

This rule states that test.txt depends on originalfile.txt. If originalfile.txt has changed, make creates test.txt based on the command, which is a cp command.

As it turns out, make is surprisingly smart for such an old computer tool. Before it ventures into the rule to create test.txt, it sees that test.txt depends on originalfile.txt, and checks if originalfile.txt has any rules. If so, make keeps tracing backwards until it gets to a rule with no prior dependencies.

So these two rules would all be lumped together into a single Makefile:

test.out: test.txt

cp test.txt test.out

echo WORKED >> test.out

test.txt: originalfile.txt

cp originalfile.txt test.txt

imageYou can have multiple rules in your Makefile, and they don’t all have to depend on each other. If you have multiple rules without such interdependencies in your file, however, make starts by running the first rule that it encounters.

Making specific items

When you have a Makefile filled with all sorts of rules, you may possibly want to build only one particular item inside it. For example, if you have dozens of source-code files, you may just want to compile the source-code file you’re working on, without building the whole shebang all the way to the final executable.

To specify exactly what you want to build, just throw in the name of the item after the word make:

make test.txt

If you are working with the Makefile with rules for both test.out and test.txt, then when you issue this command, you create only test.txt; you will not create test.out. However, if make requires other rules to be built before this rule can run, it will run those rules.

So with the example we gave regarding multiple source-code files, if you want to compile only the one called NiftyFeature.cpp, you would type

make NiftyFeature.o

image Note that the file extension in this line is .o. The reason is that, although you’re compiling the source file (NiftyFeature.cpp), you are making the object file (NiftyFeature.o). Therefore, you specify the name of the object file in the make command when you want to compile a particular source file.

Depending on multiple files

Not only can you have multiple rules in your Makefiles, but you can have one rule that depends on multiple files. This is common when building files, particularly in the link step. For example, the final program itself, such as myprogram.exe, is going to depend on many files, including all the object files in your project, as well as any libraries.

To list multiple dependencies, you create your Makefile rule, listing all the dependencies on the right of the colon, separated by spaces, as in the following example. (Don’t use commas or semicolons to separate them.)

test.out: test1.txt test2.txt

echo hello > test.out

Here, test.out depends on both test1.txt and test2.txt. When you run make, if either of these two latter files has changed since the last time you ran make, the echo line executes.

imageIf you have lots of files to the right of the colon, you can put them on multiple lines if you prefer by ending each line except the final one with a backslash (\), as in the following example.

test.out: test1.txt \

test2.txt

echo hello > test.out

Often, people indent the following lines with a single space, as we did there, primarily for readability. Although using a tab here is okay, we don’t recom-mend doing that because the tabbed line makes it harder for us mere humans to read because the tabbed line will be aligned with the commands that follow.

imageIf a header file changes and your source-code file uses it, you will want your source-code file to be rebuilt the next time you run make. Therefore, including header files in your list of dependencies is a good idea. But it can be difficult to figure out what header files your source file depends on. It turns out that there’s a nifty little trick that can help you do this. Compilers such as Cygwin and MinGW can build such a list for you. To do this, you use the -M option, as in the following example:

g++ -M main.cpp

When we ran this on one of our source-code files, we saw the following appear on the screen:

main.o: main.cpp /usr/include/g++-3/iostream.h \

/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \

/usr/include/_G_config.h \

/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \

/usr/include/sys/cdefs.h main.h

Notice the left of the colon is the object file, main.o. To the right are the files it depends on, starting with our source-code file (main.cpp). The several include files follow, including their paths. To use these, you would then paste them into your Makefile; they represent the first line of the rule.

imageIf you want to use the -M option to generate your dependencies (but only want to list the header files in your project and not all those other bizarre ones that live inside the main include directory), you can throw an extra M in, as follows.

g++ -MM main.cpp

When we run this, we see only the header files in our project. Here’s the output we get:

main.o: main.cpp main.h

Often, this is more useful because it’s rare that the system header files change.

Compiling and linking with make

To really use make in your projects, you need rules that tell make to run the compiler. For each of your source-code files, you want to build an object file; and from there, you want to link the object files into a single executable. You can write a Makefile that specifies these rules, such as the following:

mystuff.o: mystuff.cpp

g++ -c mystuff.cpp -o mystuff.o

The first line says that our object file depends on the source file. The second line is the command to run to create the object file.

Linking is a bit more complex because to successfully link a program, you need to include several libraries. Which libraries you include depends on which compiler you use.

The beauty of using Makefile, however, is that after the information for compiling and linking is there, you don’t need to do anything else. When it’s time to build your program, you simply type

make

image Certainly, you will be compiling and linking. Remember that with Makefiles, the best way to use them is to simply understand them. That way, you can take an existing one and fix them up so they work with your particular project. Writing one from scratch requires a strong knowledge of the intricate workings of the inference rules. Toward the end of this appendix (in the “Discovering more about make” sidebar), we tell you where you can find that information if you really want to study it.

Cleaning up and making it all

When you have a Makefile and a huge project, you may want to periodically start over fresh, cleaning out all your object files and the final executable, before you do a build. For example, you may be working on a project, and so much has changed that you would prefer to start out fresh with your next build. To do this, you can include a clean section in your Makefile that looks like this, if you’re using Cygwin:

clean :

rm -f *.o

rm -f *.obj

rm -f myprogram.exe

Note that no files are listed after the colon. Thus, when you type

make clean

the rm commands (which delete the files) will always run.

imageIf you’re using either the MinGW or Borland compilers, you want your clean section to look like this:

clean :

del *.o *.obj myprogramexe

Using macros

Environment variables can include macros. A macro is basically a word that represents something else that is probably more complicated. For example, you may have something like this:

MYFILES = one.cpp two.cpp three.cpp four.cpp

Then, any place in your Makefile where you want to refer to the four files one.cpp, two.cpp, three.cpp, and four.cpp, you can instead simply write

$(MYFILES)

When you access a macro, you precede it with a dollar sign ($), and you put the name inside parentheses. The make utility then knows that this is a macro and needs to be expanded.

image

Discovering more about make

If you want to be a serious, diehard, late-into-the-night, big-time maker, you can read its online manual. You can do tons and tons of things with make, and you could easily stay up all night playing with it all — or, at least, trying to learn it all. You may not ever have a need for all the things you learn, but you never know. Here’s the site: www.gnu.org/manual/.

Scroll down to find the entry on make. (make will be followed by a hyphen and some numbers representing the version number.) When you click it, you will see an enormous page filled with the wonders of make. Trust us: It’s long and boring.

Getting the most out of Makefiles

Here are some other features you can use when working with Makefiles:

♦ If your lines run long and you want to continue them on the next line without confusing poor old make, you can end a line with a backslash (\), and then continue it on the next line.

♦ Your best bet when working with Makefiles is to start with one you know works and then change it so it applies to your current project. The truth is, almost no programmer creates a Makefile from scratch. They don’t like to work that hard on auxiliary projects like messing with Makefiles. They’d rather get to their programming. So if you need a starting point, you can find a sample in this appendix.

♦ Most Makefiles will have a rule called all. The idea behind this rule is that it encompasses all the other rules. When you type make all, you can build your whole project.

♦ You can include comments in your Makefiles by starting them with a # character. These comments are not used by the Makefile.

♦ Makefiles can include what are called implicit rules, which are rules that pertain to a whole set of files with the same file extension (such as .cpp). These comments can help you understand the Makefiles when working with them.

♦ If you don’t like Makefiles, you don’t have to use them. Development environments such as CodeBlocks and Microsoft Visual C++ make it possible to create great applications without ever touching a Makefile.