EBC Exercise 15 make

As your programs become more complex they will spread over multiple files. make is a utility that tells which files depend on which and how to compile them all together into an executable. This exercise walks you through how to create a Makefile.

This exercise is heavily based on TI's OMAP™/DaVinci™ System Integration using Linux Workshop.

Basic make
In part A of the lab, you will build your first basic Makefile – basically turning command line execution into make rules. In Part B, you will increase the usability of your makefile by adding built-in variables and user-defined variables. This will provide you with a fundamental understanding of how makefiles work.

Lab Prep – Examine the directory contents and app.c
beagle$ cd exercises beagle$ git pull beagle$ cd make beagle$ ls beagle$ gedit app.c

(See here if this doesn't work.)

Part A – Using the Command Line and Creating a Simple Makefile
In this part, we will simply use the GNU compiler (gcc) from the command line to build the “Hello World” example and run it. Then, we’ll place these commands into a basic makefile and run the makefile. In the next part, we’ll use built-in and user-defined variables. To compile app.c, type the following command:
 * Build and run “Hello World” from the command line.

beagle$ gcc –g –c app.c –o app.o

gcc = GNU C compiler (command) –g = symbolic debug (compiler option) –c = (fill in answer below) app.c = file to compile (kind of “dependency” or “prerequisite”) -o = output filename is next (compiler option) app.o = output file (the “target”)

In the above gcc command, name the target, dependency and command.
 * 1) Target = _______________
 * 2) Dependency = ________________
 * 3) Command = __________________

To find the parameters for any standard C functions or Linux commands, you can use the “man” (short for “manual”) command. Let’s try it on gcc:
 * On your host computer use the man command to look up gcc.

host$ man gcc

What does the –c option (from the previous step) tell the compiler to do?

To quit the man page, type q. Next, link the object file (app.o) to create the executable app.arm:
 * Link the object file and produce the final executable.

beagle$ gcc –g app.o –o app.arm

Now run the executable:

beagle$ ./app.arm

You should see “Hello World” displayed in the command window. The extension used for the output file (.arm) indicates we are building for the arm (or Beagle).

Note: For those of you who know Linux well, you can skip this explanation. For the rest …

before the name of an executable tells Linux to look for the program in the current directory. We use this as it is the proper way to specify the path of the file to be run.

Type the following to remove the files generated by the gcc commands you executed:
 * “Clean” the existing executable (.arm) and intermediate (.o) files.

beagle$ rm –rf app.arm beagle$ rm –rf app.o

This removal of files mirrors what a “clean” macro or rule might do. We’ll actually add a rule shortly to accomplish this in our makefile.

The current makefile simply contains comments and placeholders for the code you will write. Using your favorite editor, open the makefile. For example:
 * Examine “starter” makefile.

beagle$ gedit makefile

Remember, a rule is made up of a target, dependency(ies) and command(s). For example: target : dependency CMD Also note that the commands are tabbed over (at least one tab). Create the rule for app.o in the area of the makefile with the header comments specifying the intermediate (.o) rule (as shown below). We’ll help you with the rule for app.o, but app.arm is up to you. For app.o, type in the following rule. We will use the absolute path of gcc for now and later turn it into a variable: app.o : app.c       /usr/bin/gcc –g –c app.c –o app.o            app.o = target app.c = dependency /usr/bin/gcc -g … = command Next, type in the rule for app.arm ABOVE the rule for app.o in the area specified for the (.x) rule. Make sure you use the –g compiler option in the .x rule. Close makefile and type the following:
 * Create rules for app.arm and app.o in your makefile.
 * 1) -- intermeditate object files rule (.o) ---
 * 1) -- intermeditate object files rule (.o) ---
 * Type in the rule for .x.
 * Test your makefile.

beagle$ make

After running make, list the current directory. Do you see a new app.arm executable? Run it.

Do you see “Hello World”? If so, your rules work. Next, let’s add a few more rules…

Stop. Before you open makefile again, try opening it in a different Linux process by typing in the following:
 * Open makefile in a different Linux process.

beagle$ gedit makefile &

The “&” tells Linux to open the makefile in a separate process (window). When you edit a file, you can simply click Save, then click inside the terminal window and run it without having to re-open the makefile. Handy – and could save you some time. Whenever you run make, it will search and note the timestamps of the source files and executables and won’t run if everything is up to date. So, it is common to create a “clean” rule that removes the intermediate and executable files prior to the next build.
 * Create a “clean” rule in your makefile.

In the makefile (underneath the comment header for “clean all”), add the following .PHONY rule for “clean” (these are the same commands you used earlier on the command line): .PHONY : clean clean : rm –rf ___.arm rm –rf _______ .PHONY tells make to NOT search for a file named “clean” because this is a phony target (i.e. it is not a file that needs to be searched for or created). In a large and complex makefile, this actually saves some compile time (plus, it is just good practice to use .PHONY when the target is not an actual file). The two files are the final executable and the intermediate object file. When make runs without any rules specified (i.e. you just type “make” on the command line), it will make (by default) the first rule in the makefile. Therefore, it is common to create an “all” rule that is placed first in the makefile. Our example only has one final target (app.arm), so “all” doesn’t make as much sense now.
 * Create an “all” rule in your makefile.

In the makefile (under the comment header for “make all”), add the following .PHONY rule for “all”: .PHONY : all all : app.arm Close makefile and let’s run it… On the command line, type in the following:
 * Run make to create the executable app.arm.

beagle$ make

make will probably tell you that the files are “up to date” and there is nothing to do. So, you must run “clean” before you build again. Type:

beagle$ make clean

and then:

beagle$ make

make runs the first rule in the makefile which is the “all” rule. This should successfully build the app.arm executable.

Note: make assumes the name of the make file is makefile or Makefile. make also looks for the FIRST makefile it finds. So, to be safe, you might want to capitalize Makefile because capital “M” comes before lower-case “m” alphabetically. You can also use a different name for the makefile – e.g. my_makefile.mak. In this case, you need to use the following command to “force” the use of a different make file name:

beagle$ make –f my_makefile.mak

You should see “Hello World” again. Ok, now that we have the simple makefile done, let’s turn it up a few notches… As a review, you can run make in several ways: make               (makes the first rule in the make file named makefile or Makefile) make        (makes the rule specified with, e.g. “make clean”) make –f my_makefile (forces the use of a make file named my_makefile)
 * Run app.arm.
 * Review the different ways to run make.