HOME


DSP         Embedded Systems         GNU Tutorials         SW Development        


GDB Tutorial         GNU Make Tutorial         Library with GCC         Vi Tutorial    

Intended Audience

        This tutorial is intended to demonstrate the basic features of GNU Make utility. I have assumed that the audience of this tutorial has some prior experience in building C applications with Linux-GCC. I am providing some examples of Makefile along with this tutorial (the source files are in "C").

Why use Make

        Makefile helps you to automate the process of generating the binary (intermediate and final output) files from source files. Consider a scenario where you are developing a C project under Linux-GCC GCC. In order to build the final executable you need to:
  • compile the source files to generate the object files
  • link the object files any libaries (if any) in to final executable
        During the development process you may make frequent changes to the source files. Each time you make such changes, you need to repeat the above steps. Managing this process manually exposes a programmer to following problems:
  • Having made changes to a few files, you might not remember which all files were updated. So you may have to compile all the source files again. In case you do not re-compile all the source files, you may forget to re-compile some of the file which was recently changed. This might create a bigger problem on later stage.
  • Typing the build commands manually each time (either to recompile all the files or a few files), needs lot of effort and time. Also there a possibility of typign a wrong file name or wrong path.
        Make helps you to automate this process by specifying the build rules for your project. Once you have specified the build rules, you can rebuild (partial or complete) your project by issuing very simple commands. You can avoid any human errors provided that you specified the rules correctly initially. Since you need to specify build rules only once, you can pay more attention to this process and avoid any errors on later stages.

What constitutes a Make file

Make file is made up of various make rules. Each rule has the following syntax:

target : prequisite
        commands
        ....
        commands
  • Make file can have one or more such rules
  • There can be one or more command associated with each rule
  • Notice that a "tab" precedes each command
  • You can issue any make command with "make target" : When such a command is issued, Make will check if any of the prequisites is newer than the target (by checking time stamps). If so, commands associated with the given target rule will be executed.
  • If any of the prequisite does not exist, make tries to build those prequisites. The rule to build the prequisite must be available to make file.
  • Prequisites are optional, i.e. you can have zero prequisites. In case of no prequisite, the commands associated with the target will always be executed (whenever make rule associated with the corresponding target is issued to theMake).
  • The target need not be necessarily a file name. It can be any name. In such cases, there will be no time stamp associated with target. Hence make can not figure out if there are any prequisites which are newer than the target. In such a scenario, the commands will be associated with the Make rule always executed.
  • In case you forget to provide the target name to "make target" command and issue just an empty "make" command, the very first rule of the make file will be executed. For this reason, generally the very first rule specified in Make File, builds the complete application.
  • You can specify comments in your Make file followed by a "#". Any text following "#" in a given file is ignored by Make.
  • You can name your make file as "makefile" or "Makefile".

Getting Started

        Consider a simple application (Refer to the example ex00) which has two source files "main.c" and "operation.c" and one header file "operation.h". You can specifiy the make rules as follows:

app: main.o operation.o
        gcc main.o operation.o -o myapp.out

main.o: main.c operation.h
        gcc -c main.c -o main.o

operation.o: operation.c
        gcc -c operation.c -o operation.o

        Listing.1 Makefile of example ex00

        While writing the above Makefile it is assumed that all the source files associated with the project are stored in the same folder where Makefile is stored. To build the final executable for the given project you can issue a command "make app" : Make will notice that prequisites main.o and operation.o does not exist, so first it will try to build these two object files. Rules for building the object files have also been specified in the make file. Once the object files are built, these will be linked to generate the final executable "myapp.out".

In a Makefile, generally a target called "clean" is specified, with a purpose of removing all the intermediate and output files. So, in the Listing.1 you can add:

clean:
        rm -f *.o *.out

Variables

        Make supports variable definitions:
  • A "variable-name" can contain can contain any characters (except some puncuation).
  • The variable name must be surrounded by $() [e.g. $(variable-name) ], when you want it to be expanded.
  • ${} can also be used in place of $().
  • Variable names are case-sesitive.

Automatic variables

Automatic variables are the variable which are set by Make, once a rule is matched. Some of the important automatic variables are:
  • $@     this is the name of the target
  • $^     the name of all the prequisites (with duplicate file names removed)
  • $?     names of all the prequisites which are newer than the target
  • $<     name of the first prequisite
  • $+     same as $^, but duplicates are not removed
  • $*     the stem of the target name
        Refer to the Makefile in example ex01. The make file has been modified to use automatic variables:

app: main.o operation.o
        gcc $^ -o myapp.out

main.o: main.c operation.h
        gcc -c $< -o $@

operation.o: operation.c
        gcc -c $< -o $@

clean:
        rm -f *.o *.out

        Listing.2 Makefile of example ex01

Specifying Search Directories

        While handling a considerably large project, with a number of source and header files, it is convenient to the source files stored in different directories.. The makefiles which we used so far, will work fine if the source files are stored in the same folder. When you move the source and header files to a different folder, and try to use the makefile of previous examples, the Make will fail. This problem can be resolved by specifying to Make, the directories which it should search for, to find a given prequisite. You can specify the search directories to make using the VPATH variable. Either a single or multiple directories can be specified:

        VPATH = dir1 dir2 dir3 ....

        A file may exist in more than one directoryfolder, in such cases the first occurence of file will be considered as the valid file.

        Refer to the example ex02. The "C-source" files have been moved to a directory named "src" and the "C-header" file has been moved to a directory named "include". You can specify these options to Make using:

        VPATH = src include

        "vpath" (notice the lower case) is a more precise way of specifying the search directories:

        vpath pattern dir-list

        In example ex02, We have used:

        vpath %.c src
        vpath %.h include

        You will also need to specify the "-I" option for gcc.

#VPATH = src include
vpath %.c src
vpath %.h include

app: main.o operation.o
        gcc $^ -o myapp.out

main.o: main.c operation.h
        gcc -I include -c $< -o main.o

operation.o: operation.c
        gcc -c $< -o operation.o

clean:
        rm -f *.o *.out

        Listing.3 Makefile of example ex02

.PHONY Targets

        As we discussed before, a "target" need not necessarily be a file name. For example we have been using a target named "clean" in our example Makefiles. The purpose of this target is to clean up the directory by removing all the binary (intermediate and final output) files. However, in case a file name called "clean" exists in the given directory, make will not be able to perform the clean operation. It will throw a message, "make: `clean' is up to date" . You can avoid such scenarios using a special target .PHONY. You can declare a target as .PHONY by declaring it as a prequisite of .PHONY:

.PHONY: clean
clean:
        rm -f *.o *.out

        Make will execute commands associated with PHONY Targets, even if the (PHONY) target already existed.

Predefined Variables

        There are some predefined variables which are used by Make. One of such variable is $(COMPILE.c). By default COMPILE.c is defined as:

        COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
        CC = gcc
  • CC is the cross compiler to be used and the default value is set to gcc
  • CFLAGS are the compiler flags
  • CPPFLAGS are pre-processor flags
  • TARGET_ARCH is architecture type

Built-in rules

        There are some built-in rules under make. One such rule is:

%.o : $.c
        $(COMPILE.c) $(OUTPUT_OPTION) $<

        We already discussed about variable COMPILE.c in previous section. The $(OUTPUT_OPTION) is set to the default value of "OUTPUT_OPTION = $@" .

        Revisiting the Makefiles which we used so far, we had been specifying make rules to create object files, explicitly. Since this rule is built-in with in Make, we need not specify this rule explicity. Refer to the Makefile in example ex04.

Some useful tips

        some times you might want to specify some command line options while invoking the make from command line. For example you may want to specify CPPFLAGS options from command line. In that case the command line options will override the options specified in the make file. Under such scenarios it might be wise to modify the COMPILE.c such that you have separate FLAGS to choose options from command line and from within Makefile. Refer to the example ex04 , COMPILE.c here has been modified in such a way that the CPPFLAGS specified on command line will not interfere with the -I option specified for compiler.

        "make target --just-print option" allows you to view the intended functionality of a Make rule, without actually executing the commands.

        So far we had used variable assignments, where we can assign a single line of text to any variable. You can also create variables which are more than one line of text (we can call them as macros), using the DEFINE directive. The syntax is:
        define macro-name
                line-1
                ...
                line-n
        endef

Target modifiers

        There are some target modifiers supported by Make which change the way make operates (similar to .PHONY) on the target. Here we will discuss one such modifier ".INTERMEDIATE" . Prequisites of this target are treated as intermediate files. If Make creates any files while updating another target, those files will be deleted automatically when make exits. If the file already exists when make considers updating the file, the file will not be deleted.

variable assignments

        There are different assignment operators available under Make:
  • :=
  • =
  • ?=
  • +=
        The ":=" operator can be used for creating simple variables. The "=" operator is used for creating recursive variables. Whenever Make encounters any recursive variable assignment, the right hand side of the assignment expression is not expanded immediately, rather it is expanded at the time when this variable is used. Also, the RHS is expanded each time this variable is used. As an example consider the definition of COMPILE.c

        COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

As we mentioned before, initially $(CC) is set as gcc. At a later point, if you change the $(CC) to "CC = arm-linux-gcc", then future invocation of COMPILE.c in Make will use this new value of $(CC). The reason being that the COMPILE.c has been assigned a value using recursive assignment. The "?=" operator is a conditional assignment operator. This operator performs the assignemnt only if the variable on LHS does not have a value yet. consider an assignment

        SRC_DIR ?= $(PROJECT_DIR)/src

        Here $(SRC_DIR) will be assigned a value, only if it did not have any value assigned to it before. The "+=" assignment operator (called append), appends text to a variable.

Summary

       I have tried to cover the basic features of Make in this tutorial. I will soon publish a second part of this tutorial covering more advanced topics in Make.

References

  • "Managing Projects With GNU Make" by Robert Mecklenburg

HOME

Copyright : Kunal Singh

Content of this site shall not be reused without my written permission

This page is XHTML Certified