Introduction to make
The UNIX make utility facilitates the creation
and maintenance of executable programs from source code.
This tutorial will introduce the simple usage of the
make utility with the goal of building
an executable program from a series of source code files.
Introduction
The UNIX make utility facilitates the creation
and maintenance of executable programs from source code.
make keeps track of the commands needed to build
the code and when changes are made to a source file, recompiles
only the necessary files. make creates and updates
programs with a minimum of effort.
A small initial investment of time is needed to set up
make for a given software project, but afterward,
recompiling and linking is done consistently and quickly
by typing one command: make, instead of
issuing many complicated command lines that invoke the compiler
and linker.
This tutorial will introduce the simple usage of the
make utility with the goal of building
an executable program from a series of source code files.
Most of the varied, subtle, and complex
features of make are the subject of entire
books and are not covered here.
This tutorial assumes that you have some familiarity with UNIX, text editors and compiling programs from source code.
A project
We'll illustrate the use and usefulness of make
with a little project. This project does nothing more
than print out the current version of the program.
This project has three source code files
-
project.f - The main program, which calls a subroutine,
print_project_info -
print_project_info.f - Contains the subroutine
print_project_info -
project_info.f - Contains parameter definitions in a Fortran 90 module
that are used by the subroutine
print_project_info
! FILENAME: project.f ! PROGRAM project IMPLICIT NONE CALL print_project_info STOP END PROGRAM project
! FILENAME: print_project_info.f ! SUBROUTINE print_project_info USE project_info IMPLICIT NONE PRINT *,"This is version",MAJOR_VERSION,".",MINOR_VERSION END SUBROUTINE print_project_info
! FILENAME: project_info.f ! MODULE project_info IMPLICIT NONE SAVE INTEGER, PARAMETER:: MAJOR_VERSION=0, MINOR_VERSION=2 END MODULE project_info
Simple make
We'll use make in its most simple forms to produce
an executable program from these source code files.
Simple compile
In order to create an executable program, all these source code files need to be compiled and linked. This command line will do it:
% xlf90 -o project project_info.f print_project_info.f project.f
That's a lot of typing even for this simple program. make
will save you from having to remember and type the command line
each time.
The make utility gets its instructions from
a text file, by default named makefile or Makefile
in the current directory.
Here is possibly the simplest and clearest makefile
that will perform the above compile and link:
# # A first makefile project : project_info.f print_project_info.f project.f xlf90 -o project project_info.f print_project_info.f project.f
The pound character, #, in a makefile indicates a
comment line. A backslash character indicates
that the line is continued on the next line.
If we save these lines in a file named
makefile, typing make at the command line
will compile the program just as if we'd typed in the entire f90
command:
% make project_info.f: ** project_info === End of Compilation 1 === 1501-510 Compilation successful for file project_info.f. print_project_info.f: ** print_project_info === End of Compilation 1 === 1501-510 Compilation successful for file print_project_info.f. project.f: ** project === End of Compilation 1 === 1501-510 Compilation successful for file project.f.
In addition, if we don't change any files and type make
again:
% make Target "project" is up to date.
make knows that the file does not need to be
recompiled. However, if we change one of the
source code files, make will recompile the
program.
How does it know how to do this? Let's look at the makefile.
The first line,
project : project_info.f print_project_info.f project.f
contains the program name, project, to the left of
a colon. The word or label to the left of the colon is called
the target. The files that appear to the right of the colon
are called dependencies. The dependencies are those
files that are needed to produce the target. So this line is
telling make that all the source code files
are needed to produce the executable program.
make checks the dependency list and if any of the
files have been changed more recently than the target, make
executes the next line in the makefile:
xlf90 -o project project_info.f print_project_info.f project.f
If the target is more recent than the dependency, make
will tell you so and skip the command.
NOTE: Although you can't see it, there is a TAB
character before the xlf90 command. This TAB must be
present for make to work. All the blank spaces
in the world will not help if there is no TAB present
and make will give you
little clue as to why it is failing.
Better compile
In the previous example, every source code file was recompiled
every time a new executable was produced. This is not necessary
in general, and it could waste a lot of time.
If we can understand the steps xlf90 takes to build
the program,
we can instruct make to work in the most
efficient manner.
The xlf90 compiling system works in two stages; it:
- compiles each
.ffile into an object file ending with a.oextension - links all the
.ofiles and system library files into the executable program
All that is needed to produce the final program are
.o files. The .f files
are needed to create .o files. (A possible
subtlety regarding xlf90: if a .f file USEs
a module contained in a different .f file,
it reads the necessary information from the module's .mod
file, which must exist.)
Here are the dependencies for this program:
| This file's creation | depends on these files |
|---|---|
| project_info.o | project_info.f |
| print_project_info.o | project_info.o print_project_info.f |
| project.o | project.f |
| project | project.o print_project_info.o |
Here's the makefile that incorporates
all these dependencies:
project : project.o print_project_info.o
xlf90 -o project project.o print_project_info.o
project_info.o : project_info.f
xlf90 -c project_info.f
print_project_info.o : print_project_info.f project_info.o
xlf90 -c project_info.o print_project_info.f
project.o : project.f
xlf90 -c project.f
The xlf90 -c command tells the compiler to produce
the corresponding .o file without trying to
link it into an executable.
Now let's make the program:
% make
xlf90 -c project.f
** project === End of Compilation 1 ===
1501-510 Compilation successful for file project.f.
xlf90 -c project_info.f
** project_info === End of Compilation 1 ===
1501-510 Compilation successful for file project_info.f.
xlf90 -c project_info.o print_project_info.f
** print_project_info === End of Compilation 1 ===
1501-510 Compilation successful for file print_project_info.f.
xlf90 -o project project.o print_project_info.o
Typing make again produces:
% make Target "project" is up to date.
Running the program on the NERSC SP yields
% ./project This is PROJECT version 0 . 2
Now let's change the version to 0.3. After we
have edited the file project_info.f, we rebuild the
program:
% make
xlf90 -c project_info.f
** project_info === End of Compilation 1 ===
1501-510 Compilation successful for file project_info.f.
xlf90 -c project_info.o print_project_info.f
** print_project_info === End of Compilation 1 ===
1501-510 Compilation successful for file print_project_info.f.
xlf90 -o project project.o print_project_info.o
% ./project
This is PROJECT version 0 . 3
Note that make only executed the steps that were necessary to
build the program.
In particular, project.f was not recompiled.
print_project_info.o was recreated because it
contains information that is defined in project_info.f.
A bit more
The material presented so far only scratches the surface
of the features of the make utility.
make has many default and implicit rules
for how to build objects from source code. These additional
features and rules make make very powerful.
However, the rules are often not obvious and can vary from
platform to platform, which can make make
frustrating and difficult to use. Books devoted to make
are available for those who are interested in learning all the
details.
In this section, we'll discuss a few additional features of make.
All we will do is slightly rework the previous makefile.
Here's a makefile that maintains the same program we've been discussing. The modifications are described below.
FC = xlf90
FCOPTS = -O3
LD = xlff90
LDOPTS =
EXENAME = project
OBJS = project.o print_project_info.o
$(EXENAME) : $(OBJS)
$(LD) $(LDOPTS) -o $(EXENAME) $(OBJS)
print_project_info.o : print_project_info.f project_info.o
$(FC) $(FCOPTS) -c project_info.o print_project_info.f
.f.o :
$(FC) $(FCOPTS) -c $<
clean:
rm -f core *.o
clobber: clean
rm -f $(EXENAME)
Macros
You can define macros, or symbols that stand for other
things, in makefiles. The lines at the beginning of the
makefile are macros. For example
FC = xlf90 is a macro. Everywhere that
$(FC) appears in the makefile,
it will be replaced with xlf90.
These macro definitions are an easy way to change items that
appear in many places in the makefile. Notice that we defined
the macro FCOPTS = -O3 and placed it in all the
xlf90 command options. The -O3 option to
xlf90 specifies an optimization level. If we to change the
optimization level for the entire code,
we just change the one line in which
FCOPTS is defined.
Implicit suffix rules
You can give make a set of rules for creating
files with a certain suffix from files with the same root
file name, but a different suffix. For example, the line
.f.o :
tells make that all .o files
are created from the corresponding .f files.
The command in the next line will recompile any .f
file if it is newer than the corresponding .o file.
$(FC) $(FCOPTS) -c $<
This line uses the make internal macro $<,
which translates to "any dependency that is more recent than
its corresponding target." This internal macro can only be
used in suffix rules.
Exceptions to the suffix rule can be stated explicitly, as
is done here for the print_project_info.o object,
which needs the project_info.o module information.
Additional targets
We've added two useful targets in this makefile:
clean and clobber. If you
type make at the command line, make
makes the first target it encounters in the makefile,
in this case the executable program. If you type make
with an argument, make jumps to the target with the
name of the command line argument.
For example, make clean jumps to the clean
target. The two new targets perform these tasks
clean- Since the file "clean" does not exist,
make cleanwill always execute the commandrm -f *.o, which deletes the.ofiles. This is useful if you want to get rid of all those files after you're finished building your program. clobber-
make clobberwill get rid of the executable file as well as the.ofiles. The file clobber also does not exist somake clobberwill always cause the executable to be removed, and since it depends on clean, it will also implymake clean.
Example: making a library archive
In this example a number of subroutines are incorporated info a library archive. Routines from the library are linked into the main program during the linking process.
It is often convenient to maintain object files in a library. By doing so, you can reduce the number of .o files you need to keep in a directory, which can have many advantages.
The example project
Let's say we have four routines that we want to maintain in a library and a main program (libex.f90) that will call those routines. In this example, the routines simply perform an operation on two real numbers and print the result. We'll make a library file, named operators.a, that will contain the subroutines. After we create the library file, we will delete the object files (*.o).
Here's what the files look like:
% ls add.f90 libex.f90 multiply.f90 divide.f90 makefile subtract.f90
! FILENAME: add.f
SUBROUTINE add(x,y)
IMPLICIT NONE
REAL, INTENT(IN) :: x, y
PRINT *, "The sum of", x, " and ", y, "is ", x+y
RETURN
END SUBROUTINE add
!FILENAME: divide.f
SUBROUTINE divide(x,y)
IMPLICIT NONE
REAL, INTENT(IN) :: x, y
IF(y==0.) THEN
PRINT *, "Can not divide by zero!"
RETURN
END IF
PRINT *, x, "divided by", y, "is ", x/y
RETURN
END SUBROUTINE divide
!FILENAME: multiply.f
SUBROUTINE multiply(x,y)
IMPLICIT NONE
REAL, INTENT(IN) :: x, y
PRINT *, "The product of", x, " and ", y, "is ", x*y
RETURN
END SUBROUTINE multiply
!FILENAME: subtract.f
SUBROUTINE subtract(x,y)
IMPLICIT NONE
REAL, INTENT(IN) :: x, y
PRINT *, "The difference, ", x, " - ", y, "is ", x-y
RETURN
END SUBROUTINE subtract
!FILENAME: libex.f
! An example program that illustrates calling routines
! from a user-built library of object modules.
PROGRAM libex
IMPLICIT NONE
REAL:: a=2.0,b=3.0
CALL add(a,b)
CALL multiply(a,b)
CALL subtract(a,b)
CALL divide(a,b)
END PROGRAM libex
The makefile
The following simple makefile can be used to maintain the archive and produce the executable. make has a special syntax for dealing with libraries. The library is defined on the line
$(LIB) : $(LIB)(add.o) $(LIB)(multiply.o) \ $(LIB)(divide.o) $(LIB)(subtract.o)
The the rule for producing the library is the following (note that the .o files are removed immediately after updating the library).
.f90.a :
$(CF) -c $<
$(AR) -r $@ $*.o
/bin/rm -f $*.o
This rule says
- If the .f file has been modified since the .a file was modified, compile the .f file using $(CF) -c.
- Take the corresponding .o file and replace it in the proper library (as defined above) using $(AR) (The ar command builds and maintains libraries, see man ar).
- Remove the .o file.
CF = xlf90
LD = xlf90
LIBOBJS = add.o multiply.o divide.o subtract.o
LIB = operators.a
EXE = libex
OBJS = libex.o
AR = ar
$(EXE): $(OBJS) $(LIB)
$(CF) -o $(EXE) $(OBJS) $(LIB)
$(LIB) : $(LIB)(add.o) $(LIB)(multiply.o) \
$(LIB)(divide.o) $(LIB)(subtract.o)
.f.a :
$(CF) -c $<
$(AR) -r $@ $*.o
/bin/rm -f $*.o
.f.o :
$(CF) -c $<
clean :
/bin/rm $(OBJS)
clobber :
/bin/rm $(EXE) $(LIB) $(OBJS)
The Output
% make
xlf90 -c libex.f
** libex === End of Compilation 1 ===
1501-510 Compilation successful for file libex.f.
xlf90 -c add.f
** add === End of Compilation 1 ===
1501-510 Compilation successful for file add.f.
ar -r operators.a add.o
ar: Creating an archive file operators.a.
/bin/rm -f add.o
xlf90 -c multiply.f
** multiply === End of Compilation 1 ===
1501-510 Compilation successful for file multiply.f.
ar -r operators.a multiply.o
/bin/rm -f multiply.o
xlf90 -c divide.f
** divide === End of Compilation 1 ===
1501-510 Compilation successful for file divide.f.
ar -r operators.a divide.o
/bin/rm -f divide.o
xlf90 -c subtract.f
** subtract === End of Compilation 1 ===
1501-510 Compilation successful for file subtract.f.
ar -r operators.a subtract.o
/bin/rm -f subtract.o
xlf90 -o libex libex.o operators.a
s00505% ./libex
The sum of 2.000000000 and 3.000000000 is 5.000000000
The product of 2.000000000 and 3.000000000 is 6.000000000
The difference, 2.000000000 - 3.000000000 is -1.000000000
2.000000000 divided by 3.000000000 is 0.6666666865
![]() |
Page last modified: Mon, 11 Jan 2010 21:29:38 GMT Page URL: http://www.nersc.gov/nusers/help/tutorials/make/print.php Web contact: webmaster@nersc.gov Computing questions: consult@nersc.gov Privacy and Security Notice |
![]() |

