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.
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.
- Build and run “Hello World” from the command line.
To compile app.c, type the following command:
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.
- Target = _______________
- Dependency = ________________
- Command = __________________
- On your host computer use the man command to look up gcc.
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:
host$ man gcc
What does the –c option (from the previous step) tell the compiler to do?
To quit the man page, type q.
- Link the object file and produce the final executable.
Next, link the object file (app.o) to create the executable app.arm:
beagle$ gcc –g app.o –o app.arm
Now run the executable:
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.
- “Clean” the existing executable (.arm) and intermediate (.o) files.
Type the following to remove the files generated by the gcc commands you executed:
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.
- Examine “starter” makefile.
The current makefile simply contains comments and placeholders for the code you will write. Using your favorite editor, open the makefile. For example:
beagle$ gedit makefile
- Create rules for app.arm and app.o in your 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:
# --------------------------------------------------- # ------ intermeditate object files rule (.o) ------- # --------------------------------------------------- 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
- Type in the rule for .x.
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.
- Test your makefile.
Close makefile and type the following:
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…
- Open makefile in a different Linux process.
Stop. Before you open makefile again, try opening it in a different Linux process by typing in the following:
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.
- Create a “clean” rule in your makefile.
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.
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.
- Create an “all” rule in your makefile.
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.
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…
- Run make to create the executable app.arm.
On the command line, type in the following:
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
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
- Run app.arm.
You should see “Hello World” again. Ok, now that we have the simple makefile done, let’s turn it up a few notches…
- Review the different ways to run make.
As a review, you can run make in several ways:
make (makes the first rule in the make file named makefile or Makefile) make <rule> (makes the rule specified with <rule>, e.g. “make clean”) make –f my_makefile (forces the use of a make file named my_makefile)