module text

#---------------------------------------------------------------
#
# << marscfeng's Makefile template for gcc/gfortran >>
#
#---------------------------------------------------------------
# Location Flags for default path of libraries, binaries and modules
#---------------------------------------------------------------
LIB  =     /Users/Mars/.Setup/LIBs/
MOD  =     /Users/Mars/.Setup/MODs/
PROG =     /Users/Mars/.Setup/

#--------------------------------------------------------------------
# Compile&Link Flags
#--------------------------------------------------------------------
FFLAGS  = -O -g -fopenmp
FFLAGS += -Wall
LDFLAGS = -lm -lsacio -lmtspec -lgnufor2 -lpgbar
LDLIBS  = -L$(LIB)
CC      = gcc
FC      = gfortran

#--------------------------------------------------------------------
# Programs Flags
#--------------------------------------------------------------------
EXE = main
RUNPATH = ./examples/
PROGSRC = bessj0.f90 coherence.f90 \
          preprocess.f90 rglitch.f90 spac.f90
PROGOBJS = $(PROGSRC:.f90=.o)

#--------------------------------------------------------------------
# Module Flags for intel and gfortran compiler
#--------------------------------------------------------------------
MFLAG = -I
MODULE = $(MFLAG)$(MOD)

#--------------------------------------------------------------------
# Compile programs
#--------------------------------------------------------------------
all : $(EXE)

$(EXE) : $(PROGOBJS)
    $(FC) $^ $(FFLAGS) $(LDFLAGS) $(LDLIBS) -o $(PROG)$@

%.o : %.f90
    $(FC)  $(FFLAGS) $(MODULE) -c $< -o $@

#--------------------------------------------------------------------
# run
#--------------------------------------------------------------------
run:
    cd $(RUNPATH); $(EXE)

#--------------------------------------------------------------------
# Clean
#--------------------------------------------------------------------
clean:
    rm -f $(PROG)$(EXE) core *.o

Yeah, it’s my personal makefile module (download here) for gcc (GNU Compiler Collection). I am writting this rough explanation here to summary some key points of GNU make.

platform

OS: OSX 10.11 Compiler: gcc 5.1.0 (gfortran included)

Location Flags

I set these variables in order to tell compiler where my programs come and where they will go. LIB variable shows compiler a path to search libraries. MOD variable shows compiler a path to search fortran modules. PROG variable tells compiler where to put my programs. Commond line 44 fulfills this work by defining $(PROG)$@ as final target.

Note that, the last character of path variables, '/', should never be forgotten. Due to *make* is not able to identify `/Users/Mars/.Setupmain` with what your wanted `/Users/Mars/.Setup/main`.

It is my personal habits about usage of libraries, modules and binaries that I am used to setting up environment variables and aliases in ~/.bash_profile. I have created some private folders for private libs (/Users/Mars/.Setup/LIBs/ ), mods (/Users/Mars/.Setup/MODs/), bins (/Users/Mars/.Setup/). Then i added the following variables in my environment variables list.

#set env for shared libs
export LD_LIBRARY_PATH=/Users/Mars/.Setup/LDLIBS:$LD_LIBRARY_PATH
#set env for static libs
export LIBRARY_PATH=/Users/Mars/.Setup/LIBs:$LIBRARY_PATH

As mentioned in the last blog, however, it is also available to set this variables in makefile. It may shows in your makefile as follws:

LIBRARY_PATH=/Users/Mars/.Setup/LIBs/
LD_LIBRARY_PATH=/Users/Mars/.Setup/LDLIBS/

Compile&Link Flags

FFLAGS = -O -g -fopenmp defines the basic option for compiler. -fopenmp will only work, when !$ OMP conditional compilation sentinels have already been set in your codes. FFLAGS += -Wall appends more requists for compiler to show more and more warning infomation, then programmer can check thess tiny mistakes. LDFLAGS = -lm -lsacio -lmtspec -lgnufor2 -lpgbar links the whole essential libraries for program main. LDLIBS = -L$(LIB) adds the libs path from $(LIB) to search directory of make. CC = gcc defines gcc as chosen compiler for .c, .c++, .cpp, etc. FC = gfortran define gfortran as chosen compiler for .f, .f90, etc.

Note that
1. '+=' is a appending character to append more text to variable.
2. ‘#’ in a line of a makefile starts a comment. It and the rest of the line are ignored, except that a trailing backslash not escaped by another backslash will continue the comment across multiple lines. A line containing just a comment (with perhaps spaces before it) is effectively blank, and is ignored. If you want a literal #, escape it with a backslash (e.g., \#). Comments may appear on any line in the makefile, although they are treated specially in certain situations.

Programs Flags

EXE = main defines the name of the output executable file. RUNPATH = ./examples/ defines a destination for the exe. PROGSRC defines the required source codes to build the exe. PROGOBJS = $(PROGSRC:.f90=.o) defines the required prerequisites that the final target (exe) depends on.

Note that, the backslash (\) character in commond line 28 is used to add newlines. Makefiles use a “line-based” syntax in which the newline character is special and marks the end of a statement. GNU make has no limit on the length of a statement line, up to the amount of memory in your computer. `$(PROGSRC:.f90=.o)` is a classic substitution reference which substitutes the value '.f90' of a variable with alterations '.o' that you specify.

Module Flags

MFLAG = -I
MODULE = $(MFLAG)$(MOD)

These flags tell the compiler where to include the required modules. Here, i only display module flag for intel(ifort) and gcc(gfortran) compiler. Flags for including modules in some other compilers are listed as follws:

# Sun Compiler
#MFLAG = -M

# Nag Compiler
#MFLAG = -i
#MFLAG = -I

# Absoft Compiler
#MFLAG = -p

Compile programs

1. Phony targets

  1. one target remade once all : $(EXE), this is a phony targets ‘all’ and its prerequisite ‘$(EXE)’. Yeah, phony targets can have prerequisites. When one directory contains multiple programs, it is most convenient to describe all of the programs in one makefile ./Makefile. Since the target remade by default will be the first one in the makefile, it is common to make this a phony target named ‘all’ and give it, as prerequisites, all the individual programs.
  2. how to remake specfic target You know that run and clean are both phony targets. As mentioned above, the first target all is the target remade by default. But we can also run make run or make clean to choose remake the specific target. So we can also run prerequisite-target by make $(EXE1).

2. makefile rules

A simple makefile consists of ‘rules’ with the following shape:

target … : prerequisites …
    commond
    …
    …

It consists of targets list,prerequisites list,commond, and Tab character. We need to put a Tab character at the beginning of every commond line! This is an obscurity that catches the unwary. As for the following rules of my makefile,

$(EXE) : $(PROGOBJS)
    $(FC) $^ $(FFLAGS) $(LDFLAGS) $(LDLIBS) -o $(PROG)$@

$(EXE) means the target, who depends on prerequisites ($(PROGOBJS)). To remake the target, i give the rule as the subsequent commond line shows.

3. automatic variable

$@

The file name of the target of the rule. If the target is an archive member, then $@ is the name of the archive file. In a pattern rule that has multiple targets (see Pattern Rules), $@ is the name of whichever target caused the rule’s recipe to be run.

$<

The name of the first prerequisite. If the target got its recipe from an implicit rule, this will be the first prerequisite added by the implicit rule.

$?

The names of all the prerequisites that are newer than the target, with spaces between them. For prerequisites which are archive members, only the named member is used.

$^

The names of all the prerequisites, with spaces between them. For prerequisites which are archive members, only the named member is used. A target has only one prerequisite on each other file it depends on, no matter how many times each file is listed as a prerequisite. So if you list a prerequisite more than once for a target, the value of $^ contains just one copy of the name. This list does not contain any of the order-only prerequisites.

Note that, they only have values within the commond lines.

4. Pattern Rules

%.o : %.f90
    $(FC)  $(FFLAGS) $(MODULE) -c $< -o $@

A pattern rule contains the character % (exactly one of them) in the target; otherwise, it looks exactly like an ordinary rule. The target is a pattern for matching file names; the % matches any nonempty substring, while other characters match only themselves.

Note that, specfic filename is unavailable in pattern rules, so automatic variables come into being.

Tips

  1. Shell commonds is available in makefile For example, cd $(RUNPATH); $(EXE) tells the terminal to change pwd temporarily to $(RUNPATH) and execute $(EXE). Note here, the pwd will back to its original path after that.
  2. Conditional execution is available in makefile You can use the included conditional directive ifeq in makefile as follows. But i prefer to use if directive of Shell as follows. if [ -e '*.mod' ]; then mv *.mod $(MOD); fi
  3. Display variables infomation in makefile We can use $(info $(PROG)) to display the value of $(PROG).