Makefiles

Makefile Rules

A makefile takes a set of rules in the following format

target ... : prerequisites ...
 (tab space) recipe
 (tab space) ...
  • A target usually refers to a file name that is generated by executing your program. This is your outcome from running your code.
  • A prerequisite is a file that is required as input in order to generate the target, which often depends on several files.
  • A recipe is an action that takes place when make carries out. One very important thing is to put a tab space in front of each recipe.

Makeifle examples

The directory $lecture_note/chapters/chapt02/codes/multifile contains a Fortran code fullcode.f90 that consists of a main program and two subroutines:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
! /codes/multifile/fullcode.f90

program demo
  print *, "In main program"
  call sub1()
  call sub2()
end program demo

subroutine sub1()
  print *, "In sub1"
end subroutine sub1

subroutine sub2()
  print *, "In sub2"
end subroutine sub2

To illustrate the construction of a Makefile, we first break this up into three separate files:

1
2
3
4
5
6
7
! /codes/multifile/demo.f90

program demo
    print *, "In main program"
    call sub1()
    call sub2()
end program demo
1
2
3
4
5
! /codes/multifile/sub1.f90

subroutine sub1()
  print *, "In sub1"
end subroutine sub1
1
2
3
4
5
! /codes/multifile/sub2.f90

subroutine sub2()
  print *, "In sub2"
end subroutine sub2

The directory $lecture_note/chapters/chapt02/codes/multifile contains several Makefiles that get successively more sophisticated to compile the codes in this directory.

In the first version we write out explicitly what to do for each file. In order to run this Makefile, just type make as long as your makefile has a default name Makefile or makefile:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# /codes/multifile/Makefile

output.txt: main.exe
	./main.exe > output.txt

main.exe: main.o sub1.o sub2.o
	gfortran main.o sub1.o sub2.o -o main.exe

main.o: main.f90
	gfortran -c main.f90
sub1.o: sub1.f90
	gfortran -c sub1.f90
sub2.o: sub2.f90
	gfortran -c sub2.f90

In the second version there is a general rule for creating .o files from .f90 files, called inference rules (see more article-inference), or pattern rules (see more article-pattern). There is an old style rule called suffix rules which we will not use in our course. See article-suffix for more details. The last line (i.e., recipe) has the special macro $< which implies the filename of the prerequisite (i.e., *.f90 files). There are seven frequently used core macro variables. In order to run this non-default Makefile2, you need to type in make -f Makefile2:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# /codes/multifile/Makefile2

output.txt: main.exe
	./main.exe > output.txt

main.exe: main.o sub1.o sub2.o
	gfortran main.o sub1.o sub2.o -o main.exe

%.o : %.f90
	gfortran -c $< 

In the third version we define a macro OBJECTS so we only have to write out this list once, which minimizes the chance of introducing errors:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# /codes/multifile/Makefile3

OBJECTS = main.o sub1.o sub2.o

output.txt: main.exe
	./main.exe > output.txt

main.exe: $(OBJECTS)
	gfortran $(OBJECTS) -o main.exe

%.o : %.f90
	gfortran -c $< 

In the fourth version, we add a Fortran compile flag (for level 3 optimization) and an linker flag (blank in this example):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# /codes/multifile/Makefile4

FC = gfortran    
FFLAGS = -O3
DFLAGS = -g3
LFLAGS =
OBJECTS = main.o sub1.o sub2.o

output.txt: main.exe
	./main.exe > output.txt

main.exe: $(OBJECTS)
	$(FC) $(LFLAGS) $(OBJECTS) -o main.exe

%.o : %.f90
	gfortran -c $(DFLAGS)  $< 

Next we add a phony target clean that removes the files created when compiling the code in order to facilitate cleanup. It is phony because it does not create a file named clean, rather clean is an action. To run clean, type make -f Makefile5 clean:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# /codes/multifile/Makefile5

OBJECTS = main.o sub1.o sub2.o
.PHONY: clean

output.txt: main.exe
	./main.exe > output.txt

main.exe: $(OBJECTS)
	  gfortran $(OBJECTS) -o main.exe

%.o : %.f90
	gfortran -c $< 

clean:
	rm -f $(OBJECTS) main.exe

Finally we add a help message so that make help says something useful (you need to type make -f Makefile6 help for this example):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# /codes/multifile/Makefile6

OBJECTS = main.o sub1.o sub2.o
.PHONY: clean help

output.txt: main.exe
	./main.exe > output.txt

main.exe: $(OBJECTS)
	gfortran $(OBJECTS) -o main.exe

%.o : %.f90
	gfortran -c $< 

clean:
	rm -f $(OBJECTS) main.exe

help:
	@echo "Valid targets:"
	@echo "  main.exe"
	@echo "  main.o"
	@echo "  sub1.o"
	@echo "  sub2.o"
	@echo "  clean:  removes .o and .exe files"

Fancier things are also possible, for example automatically detecting all the .f90 files in the directory to construct the list of SOURCES and OBJECTS:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# /codes/multifile/Makefile7

SOURCES = $(wildcard *.f90)  
OBJECTS = $(subst .f90,.o,$(SOURCES))

.PHONY: test

test:
	@echo "Sources are: " $(SOURCES)
	@echo "Objects are: " $(OBJECTS)