Using make

Introduction

The make command invokes a project management tool that uses rules to determine what commands need to be executed in order to build targets based on dependencies. Each of the italicized terms in this paragraph will be explained in this web page.

The make utility is used in virtually all software development environments, including UNIX, Windows, and other operating systems. The program might be named make, nmake, mk, or gmake, or might be buried behind the "Build" button of an IDE (Integrated Development Environment), but it is virtually always there.

This web page introduces some basic concepts common to most implementations of make, but many of the details here are specific to the version used in our lab, which comes from the GNU project at MIT. There is a Documentation and References section below that you can consult for more information than what is given here.

The basic idea of make is that it can be used to automate common tasks efficiently. The tasks you automate almost always involve manipulating files by issuing commands. (The make utility is so general that just about every statement about it has to be qualified with phrases like "almost always," "usually," or "normally." In the interest of simplicity, I'll skip some of those qualifiers in what follows.)

The make Command Line

You can invoke the make processor just by typing the name of the command with no command line options, but in this case, there must be a Makefile in the current directory to tell make what to do. If there is no Makefile, but there is a subdirectory named RCS, make will attempt to check out its Makefile using the RCS co command. There is a web page, Using RCS, available for further information on using RCS. All but the simplest tasks (such as compiling a single source file into an executable file) require the use of a Makefile, and this web page is intended to help you write your own Makefiles. To help get you started, there is a Skeleton Makefile that you can use to get you started. You might want to have a copy of that web page available to look at as you read this one.

Often, you supply one or more targets on the command line, which tell make what it is you want it to make. Targets are described below.

To the right of the make command name and to the left of any targets you might specify on the command line, you may also provide values for make "macros", using name=value syntax. (Macros are also called make variables), and are described in more detail below.

For example, if you want make to use "-r1.3" for the value of its COFLAGS macro when making the target named "myprog," you could use the following make command line:

      make COFLAGS="-r1.3" myprog

Implicit Rules

There are a lot of things that make knows how to do completely automatically by following a set of built-in implicit rules. Other tasks have to be specified by explicit rules written by the user.

Most of make's implicit rules are based on filename pattern matching. For example, GNU make knows how to do the following file operations using its pattern matching rules:

Since make knows how to do all these things automatically, they should never be specified in explicit rules. If you were to use explict rules for any of these operations, (1) other people looking at your rules would be confused, and (2) you might make a mistake that could be avoided by using the implicit rules, which have been very carefully designed and tested.

There is a web page with a fuller description of these implicit rules . There is also information about the the implicit rule for linking below. You can find the complete list of implicit rules that GNU make knows about in the info pages or make manual, referenced in theDocumentation and References section below.

Explicit Rules

Explicit Rules are typed into a file named Makefile in the current directory. Makefiles may contain comments (beginning with a '#' character) and macro (variable) definitions as well as Explicit Rules.

An explicit rule has the following format:

      <target> : [<dependency list>]
      [<command list>]
The <target> is usually the name of a file that you want make to build, but it can be a "phony" name that is used just to get make to execute some commands for you. You can specify the rules that you want make to process by listing the target parts of those rules on the make command line, but if you don't give any targets on the command line, make will process the first rule it finds in the Makefile. If there is no Makefile in the current directory (even after make tries its implicit rule for obtaining it from an RCS or SCCS database), you must give a target on the make command line, and make will try to build the target using only its implicit rules.

The first thing make does once it has selected a rule to process is to use implicit or explicit rules to bring all the files listed in the <dependency list> "up to date." Once all the files in the dependency list are up to date (see below), make executes each of the commands in the <command list> for the rule. If the dependency list is empty, make executes the command list for the rule unconditionally.

In order to be "up to date" a file must be present, and it must have been modified more recently than any files on its own dependency list. A common example of this that you will encounter is when make uses one of its implicit rules to build a .o file from the corresponding .cc file: the target (the .o file) must have a more recent modification time than the file it depends on (the corresponding .cc file). If it doesn't, it means that you have edited the .cc file since it was last compiled, so make automatically executes a g++ command to compile the .cc file with the -c command line option.

Once all the files in the dependency list for a rule are up to date, make executes each command in the command list, if there are any. It doesn't matter if the commands actually build a file with the same name as the target, they just get executed.

Note on Implicit Linking. Normally, no commands get executed unless a rule includes at least one command in its command list, but there is an important exception to this: If rule consists of just a target with a list of .o files for its dependencies, make will bring all the .o files up to date using its implicit rules for compilation, and then will use another implicit rule to link all the .o files together to produce an executable file with the same name as the target. One of the .o files in the dependency list must have the same basename as the target for this rule to work. By the time you are finished with this web page, you should see that the line,

    $(EXEC) : $(OBJS)
in the Skeleton Makefile is an example of how this implicit rule can be used.

There is a syntax rule that says that each command in the command list has to start with a <tab> character, so that make can tell where the list ends (by coming to a line that does not start with a <tab>). If a command is too long to fit one one line, it can be continued by putting a '\' character at the end of lines that are to be continued. Commands that are continued have to start with a <tab> on the first line, but any additional lines don't have to have one.

Note: Every command that make executes, whether because of an explicit or implicit rule, is executed by its own shell, which is the Bourne shell (sh) by default. One implication of this is that features that are specific to csh or tcsh (like '~/' representing your home directory) won't work in the commands in a rule's command list. Also, setting an environment variable in one command will have no effect during successive commands because each invocation of a shell maintains its own environment list. You can use environment variables that were already set when make was started, though, (like $HOME to represent your home directory), but you have to make them look like make macros, which will be covered in the next section.

Summary: Every rule has a target and either a dependency list or a command list (or both). A missing dependency list means to execute the command list unconditionally. For example, this rule causes the command, "make clean" to delete files that have been checked out of RCS without being locked, and then to remove some unwanted files:

      clean :
      <tab> rcsclean
      <tab> rm core *.o *.bak
(The name "clean" is conventionally used as the target for the rule in a Makefile that removes all files from the project directory that can be regenerated either by checking them out of the RCS database or by rebuilding them using make rules. See the Skeleton Makefile web page for the definition of this and other "standard" rules.)

A missing command list means that bringing the dependencies up to date is all the user wants to do. For example, the rule below causes make to process the rule with "depend" as its target (another "standard" rule), and then to build "myprog" (perhaps by using an implicit rule to compile and link myprog.cc, or perhaps by executing an explict rule for "myprog" that appears elsewhere in the Makefile):

      all : depend myprog
(The name "all" is conventionally used as the target for the rule in a Makefile that names all the other targets except "clean.")

Macros (make Variables)

Strictly speaking you can write a Makefile that doesn't use any macros, but the Skeleton Makefile uses several of them, and the make command itself uses some of them for its implicit rules. Knowing how to use them yourself can help you construct more flexible Makefiles than you can without them.

A macro is just the name of a string. Wherever make finds a reference to a macro, which consists of a dollar sign followed by the macro name in parantheses, it substitutes the string for the reference to the name. Here are examples of a macro definition and a macro reference from a Makefile:

      MYFLAGS = These words are the value of the macro named "MYFLAGS"
      
      default :
      <tab> echo $(MYFLAGS)
Now, the command "make default" generates this output:
      % make default
      echo These words are the value of the macro named "MYFLAGS"
      These words are the value of the macro named MYFLAGS
The first output line is displayed by make as it shows you the command it is going to execute, and the second output line is the result of executing the echo command.

(The name "default" is conventionally used as the target for the first rule in a Makefile, so the commands "make default" and just plain "make" will have the same effect.)

You can define a macro in the Makefile as in the previous example, on the make command line, or by setting an environment variable. Defining a macro on the command line overrides both the other two methods:

      % setenv MYFLAGS good-bye
      % make MYFLAGS=hello default
      echo hello
      hello
Normally, defining a macro in the Makefile overrides defining an environment variable, but this can be reversed with the -e option on the make command line:
      % make -e default
      echo good-bye
      goodbye
      % make default
      echo These words are the value of the macro named "MYFLAGS"
      These words are the value of the macro named MYFLAGS
It is perfectly all right to reference a make macro that isn't defined anywhere. In this case, make will silently substitute an empty string for the reference.

Macros Used for Implicit Rules

There are lots of these, but here are the ones you need to know about for this course:

CXXFLAGS Used for g++ commands
CFLAGS Used for gcc commands
COFLAGS Used for RCS co commands
LDFLAGS Used for g++ or gcc commands that involve linking, normally to specify linker options.
LDLIBS or LOADLIBES Used for g++ or gcc commands that involve linking, normally to specify library names.

NOTE: Make also uses macros to name the commands that it generates for its implicit rules. In particular, it uses the same macro name, CC, in its implict rules for compiling .c files and in its implict rule for linking .o files. This macro name is different from CXX, which is used in its implicit rule for compiling .cc files. This convention "works" because the gcc and g++ compiler drivers both use the same linker (ld) when linking .o files.

Thus, in the Skeleton Makefile the rule,

    $(EXEC) : $(OBJS)
says to be sure all the .o files are "up to date," which will cause make to using its implicit rules for compiling .cc files (and/or .c files) into .o files. It will generate the appropriate compiler driver command, gcc or g++ for each file it has to compile, based on whether the source file name ends with .c or .cc respectively. Then, regardless of what compiler driver was used to produce the .o files, make will use its single implicit rule for linking .o files to generate a gcc command to produce the executable file.

Suffix Substitution.

Let's say you need a list of .o files that will be used in a rule that links the files into an executable file, and that you also need a list of the corresponding .cc files that will be passed to a makedepend command. If you generate one list from the other, you eliminate the chance that the two lists will get out of sync with each other. Here is an example that shows how to do it:
      MY_SRCS = my_main.cc my_sub1.cc my_sub2.cc
      MY_SRCS = $(MY_OBJS:.cc=.o)
In this example, MY_OBJS is derived from MY_SRCS by substituting the suffix .o for all occurrences of the suffix .cc. Later, both lists can be updated by editing just the definition of MY_SRCS. Note: there must be no spaces inside the parentheses.

Documentation and Reference Material

There is a web page with a Catalogue of make's Implicit Rules on this web site.

There is a man page for make availalable on any Unix system with make installed on it, but the most complete documentation is in GNU's info format. Type "info make" to get started on that. Unfortunately, info is hard to get used to. It does web-like hypertext without a mouse.

There is also a full manual on GNU make that contains all the information you can get from info. There is an HTML version of the manual available from ftp.gnu.org, and there is a PDF version of it here.

There is a small book on make called "Managing Projects with make, Second Edition", written by Andrew Oram and Steve Talbott, and published by O'Reilly and Associates. It's highly recommended for a good survey of the features available in the various implementations of make that are available.


Last updated 2002-Apr-05

Christopher Vickery
Computer Science Department
Queens College of CUNY