From Documentation
Jump to: navigation, search

Overview

The make utility reads a makefile containing instructions on how to compile and link your source files to produce an executable. You can write instructions in the makefile to test environment variables and select a specific compiler. You can display information on the environment, get help instruction and even submit jobs through the make utility. The make utility will recompile only files which are modified - and this is a big time saver if you have many source files.

You can have several makefiles in the subdirectory from where the make command will be executed. If you invoke the make command without any flags it will look for a file named makefile in your subdirectory, if it does not find it then it will look for a file named Makefile. If you want to use a particular makefile, say Makefile_v1, then issue the command:

make -f Makefile_v1

The best way of understanding the concepts is by going through an example. So, let's illustrate how the make utility works by compiling, linking, executing, ... etc some OpenMP codes.

All the files used in this document can be found in the tar file

/work/syam/MyOpenMP.tar.gz

Source Files

If you copy above tar file into your account and then ungzip and untar it (tar tzf MyOpenMP.tar.gz) you should see in subdirectory MyOpenMP the following files:

hello_world_openmpi.c
hello_world_openmpi.f90
parallel_do_loop.f90
parallel_pi.c
...
Makefile_c
Makefile_fortran90
...

Makefiles

In subdirectory MyOpenMP we have c and fortran 90 source files. We will use the makefile Makefile_c for our c programs and use the makefile Makefile_fortran90 for our fortran 90 programs. First let's list these makefiles and we will explain how to use them in the next section. (Important: the lines in makefiles containing empty space at the beginning are actually using a TAB character, not multiple space characters; this is a makefile format requirement. If you paste and copy these examples from this page, you'll have to replace the starting space characters with a TAB character in each of these lines.)

# Makefile for  hello_world_openmpi.c
# file name = Makefile_c
 
ROOTDIR  = $(shell pwd)
OPSYS    = $(shell uname -s )

SRCS_C   = hello_world_openmpi.c

OBJS_C   = $(SRCS_C:.c=.o)
TARGET   = my_OpenMP_exec

ifeq ($(CC), gcc)
CFLAGS = -fopenmp
OUTPUTFILE=OUTPUT_my_OpenMP$(CC)
endif

ifeq ($(CC), icc)
CFLAGS = -openmp
OUTPUTFILE=OUTPUT_my_OpenMP$(CC)
endif

all: $(TARGET)

$(TARGET): $(OBJS_C)
       $(CC) -o $@ $(CFLAGS)  $(OBJS_C)

$(OBJS_C): $(SRCS_C)
       $(CC) $(CFLAGS) -c $(SRCS_C)

help:; @echo " "
       @echo "Operating System Detected: $(OPSYS) "
       @echo " "
       @echo "USAGE: "
       @echo "make help       To get this listing"
       @echo "make            To compile the OpenMP  program in current environment"
       @echo "make clean      Remove *.o and executable files"
       @echo "make list       List the compilers in current environment"
       @echo "make brun       Submit a batch job"
       @echo " "

list:
       @echo
       @echo "OPSYS:       $(OPSYS)"
       @echo "ROOTDIR:     $(ROOTDIR)"
       @echo "FC Compiler: $(FC)"
       @echo "C  Compiler: $(CC)"
       @echo "CFLAGS:      $(CFLAGS)"
       @echo "OUTPUT FILE: $(OUTPUTFILE)"
       @echo "ARCH:        $(ARCH)"
       @echo " "

clean:
       @echo "Removing *.o and executable files"
       rm -rf *.o $(TARGET) OUTPUT_my_OpenMP*

brun:;
       rm -rf $(OUTPUTFILE)
       sqsub -r 1h --mpp=1.0G  -o $(OUTPUTFILE) -f threaded -n 8 ./$(TARGET)


For the fortran 90 programs we will use the makefile Makefile_fortran90:

# Makefile for hello_world_openmpi.f90
# file name = Makefile_fortran90

ROOTDIR  = $(shell pwd)
OPSYS    = $(shell uname -s )

SRCS_C   = hello_world_openmpi.f90

OBJS_C   = $(SRCS_C:.c=.o)
TARGET   = my_OpenMP_exec

ifeq ($(CC), gcc)
CFLAGS = -fopenmp
FFLAGS = -fopenmp
OUTPUTFILE=OUTPUT_my_OpenMP$(FC)
endif

ifeq ($(CC), icc)
CFLAGS = -openmp
FFLAGS = -openmp
OUTPUTFILE=OUTPUT_my_OpenMP$(FC)
endif

all: $(TARGET)

$(TARGET): $(OBJS_C)
       $(FC) -o $@ $(FFLAGS)  $(OBJS_C)

$(OBJS_C): $(SRCS_C)
       $(FC) $(FFLAGS) -c $(SRCS_C)

help:;  @echo " "
       @echo "Operating System Detected: $(OPSYS) "
       @echo " "
       @echo "USAGE: "
       @echo "make help       To get this listing"
       @echo "make            To compile the OpenMP  program in current environment"
       @echo "make clean      Remove *.o and executable files"
       @echo "make list       List the compilers in current environment"
       @echo "make brun       Submit a batch job"
       @echo " "

list:
       @echo
       @echo "OPSYS:       $(OPSYS)"
       @echo "ROOTDIR:     $(ROOTDIR)"
       @echo "FC Compiler: $(FC)"
       @echo "C  Compiler: $(CC)"
       @echo "CFLAGS:      $(CFLAGS)"
       @echo "FFLAGS:      $(FFLAGS)"
       @echo "OUTPUT FILE: $(OUTPUTFILE)"
       @echo "ARCH:        $(ARCH)"
       @echo " "

clean:
       @echo "Removing *.o and executable files"
       rm -rf *.o $(TARGET) OUTPUT_my_OpenMP*

brun:;
       rm -rf $(OUTPUTFILE)
       sqsub -r 1h --mpp=2.0G  -o $(OUTPUTFILE) -f threaded -N 1 -n 16 ./$(TARGET)

Testing the environment, compiling and running the OpenMP programs

The two makefiles can be used for:

(1) removing the object files and executable, 
(2) compiling the source file,
(3) listing certain environment variables and 
(4) running the executable (submit batch job)
command resulting action
make clean removes the object files and executable
make compiles the source file
make list lists some environment variables defined in the Makefile
make brun submits a batch job to the queue using the sqsub command

You might have noticed that we have two makefiles: Makefile_c and Makefile_fortran90. So before you run the make utility you should copy into the file makefile one of the above makefiles. (Alternatively, you can say e.g. "make -f Makefile_c ...".)

Here is an example, where we will use the makefile for the fortran-OpenMP code (i.e. copy Makefile_fortran90 into makefile), then use the make utility to clear previous object files and executable, compile the source file, execute the fortran version of the OpenMP program, display the environment variables and get the name of the "OUTPUT FILE" and finally display the results:

[nickc@hnd20:/work/nickc/MyOpenMP] cp Makefile_fortran90 makefile
[nickc@hnd20:/work/nickc/MyOpenMP] make clean
Removing *.o and executable files
rm -rf *.o my_OpenMP_exec OUTPUT_my_OpenMP*
[nickc@hnd20:/work/nickc/MyOpenMP] make
make: Circular hello_world_openmpi.f90 <- hello_world_openmpi.f90 dependency dropped.
ifort -o my_OpenMP_exec -openmp  hello_world_openmpi.f90
hello_world_openmpi.f90(8): (col. 7) remark: OpenMP DEFINED REGION WAS PARALLELIZED.
[nickc@hnd20:/work/nickc/MyOpenMP] make brun
rm -rf OUTPUT_my_OpenMPifort
sqsub -t -r 1h --mpp=2.0G  -o OUTPUT_my_OpenMPifort -f threaded -N 1 -n 16 ./my_OpenMP_exec
submitted as jobid 380333
[nickc@hnd20:/work/nickc/MyOpenMP] make list

OPSYS:       Linux
ROOTDIR:     /work/nickc/MyOpenMP
FC Compiler: ifort
C  Compiler: icc
CFLAGS:      -openmp
FFLAGS:      -openmp
OUTPUT FILE: OUTPUT_my_OpenMPifort
ARCH:        x86_64
[nickc@hnd20:/work/nickc/MyOpenMP] cat OUTPUT_my_OpenMPifort
FORTRAN90: Hello World from thread           4
FORTRAN90: Hello World from thread          14
FORTRAN90: Hello World from thread           1
FORTRAN90: Hello World from thread           2
FORTRAN90: Hello World from thread           3
FORTRAN90: Hello World from thread           8
FORTRAN90: Hello World from thread           5
FORTRAN90: Hello World from thread          10
FORTRAN90: Hello World from thread          11
FORTRAN90: Hello World from thread           6
FORTRAN90: Hello World from thread           7
FORTRAN90: Hello World from thread          12
FORTRAN90: Hello World from thread          13
FORTRAN90: Hello World from thread           9
FORTRAN90: Hello World from thread          15
FORTRAN90: Hello World from thread           0
There are          16 threads

Note how the makefile checks for the environment variable $CC and sets the FFLAGS = -openmp, because we have the INTEL compiler in our environment (i.e. $CC=icc).

Let's switch modules. You can find more detailed information on modules in the wiki page:

https://www.sharcnet.ca/help/index.php/Configuring_your_software_environment_with_Modules#Overview

Let's list currently loaded modulefiles, list available modules in the system, and then switch from intel to gcc:

[nickc@hnd19:/work/nickc/MyOpenMP] module list
Currently Loaded Modulefiles:
  1) moab/5.4.2               7) octave/3.2.4            13) gromacs/4.0.5
  2) torque/2.5.5             8) r/2.10.0                14) vmd/1.8.7
  3) sq-tm/2.4                9) namd/2.7b3              15) util/2.0
  4) intel/11.0.083          10) ansys/12.1.1            16) user-environment/1.0.0
  5) openmpi/intel/1.4.2     11) fftw/intel/2.1.5
  6) compile/1.3             12) lammps/10.08.2010
[nickc@hnd19:/work/nickc/MyOpenMP] module avail

------------------------------------- /opt/sharcnet/modules -------------------------------------
...
acml/gnu/3.6.0                gcc/3.4.6                     openmpi/intel-debug/1.4.2
acml/ifort/4.2.0              gcc/4.2.4                     openmpi/intel-debug/1.4.3
acml/ifort/4.3.0              gcc/4.3.4                     openmpi/intel-debug/1.5.4
acml/ifort/4.4.0              gcc/4.4.4                     openmpi/open64/1.4.3
acml/ifort-int64/4.2.0        gcc/4.4.6                     openmpi/open64-debug/1.4.3
...
[nickc@hnd19:/work/nickc/MyOpenMP] module switch intel gcc; module list
Currently Loaded Modulefiles:
  1) moab/5.4.2               7) octave/3.2.4            13) gromacs/4.0.5
  2) torque/2.5.5             8) r/2.10.0                14) vmd/1.8.7
  3) sq-tm/2.4                9) namd/2.7b3              15) util/2.0
  4) gcc/4.4.6               10) ansys/12.1.1            16) user-environment/1.0.0
  5) openmpi/intel/1.4.2     11) fftw/intel/2.1.5
  6) compile/1.3             12) lammps/10.08.2010


Now, clear old objects, recompile, submit a batch job and list the environment variables:

[nickc@hnd19:/work/nickc/MyOpenMP] make clean
Removing *.o and executable files
rm -rf *.o my_OpenMP_exec OUTPUT_my_OpenMP*
[nickc@hnd19:/work/nickc/MyOpenMP] make
make: Circular hello_world_openmpi.f90 <- hello_world_openmpi.f90 dependency dropped.
gfortran -o my_OpenMP_exec -fopenmp  hello_world_openmpi.f90
[nickc@hnd19:/work/nickc/MyOpenMP] make brun
rm -rf OUTPUT_my_OpenMPgfortran
sqsub -t -r 1h --mpp=2.0G  -o OUTPUT_my_OpenMPgfortran -f threaded -N 1 -n 16 ./my_OpenMP_exec
submitted as jobid 1147810
[nickc@hnd19:/work/nickc/MyOpenMP] make list

OPSYS:       Linux
ROOTDIR:     /work/nickc/MyOpenMP
FC Compiler: gfortran
C  Compiler: gcc
CFLAGS:      -fopenmp
FFLAGS:      -fopenmp
OUTPUT FILE: OUTPUT_my_OpenMPgfortran
ARCH:        x86_64

Note how the FFLAGS changed to -fopenmp, because now we have the GNU compilers in our environment.

Another Example

You will find a second example on the make utility and makefiles on the wiki page:

https://www.sharcnet.ca/help/index.php/USE_MAKEFILE_2_SELECT_COMPILER

There you will see how scripts are invoked from the makefile and how you can compile fortran 77 and fortran 90 subroutines and link them from the same makefile.