Subsections


6 Compiling and Linking User Code against an ESMF Installation

Building user applications against an ESMF installation requires that the compiler and linker be able to find the appropriate ESMF header, module, and library files. If this procedure has been documented by the installer of the ESMF library on your system then follow the directions provided.

In the absence of site-specific instructions, there are standard CMake and GNU Make methods that ESMF supports to build user code against an ESMF installation.

6.1 CMake method using config mode

Starting with ESMF version 9, a standard ESMFConfig.cmake file is generated when ESMF built and installed. CMake tries to locate this ESMFConfig.cmake file in standard locations. During this search, CMake respects paths provided by the CMAKE_PREFIX_PATH variable in the user's environment. Spack-loaded installations of ESMF automatically set CMAKE_PREFIX_PATH, making this the most convenient method of building CMake-based applications against ESMF. For ESMF installations not managed by Spack, explicitly add the ESMF installation root directory to the CMAKE_PREFIX_PATH environment variable for seamless integration with CMake.

The configuration file provides exported imported targets that transitively bundle include directories, linked libraries, and compiler definitions (such as OpenMP or MPI requirements). To consume ESMF targets, utilize the find_package() command in your project's CMakeLists.txt file:

cmake_minimum_required(VERSION 3.22)
project(MyESMFApplication LANGUAGES Fortran C)

# 1. Locate the ESMF Package
# Specify a minimum version or version constraints if desired
# Explicitly request config mode
find_package(ESMF 9.0.0 REQUIRED CONFIG)

# 2. Define your application target
add_executable(my_model main.F90 physics_mod.F90)

# 3. Link against ESMF Imported Targets
# This automatically manages include paths, RPATHs, and downstream dependencies
target_link_libraries(my_model PUBLIC ESMF::ESMF)

Depending on whether the use application code is written in Fortran or C/C++, the ESMF package defines separate targets to avoid linking mismatch issues. Target aliases are provided for compatibility.





Target Alias Language Focus Description
ESMF::ESMF ESMF::ESMF_Fortran Fortran Configured for Fortran libraries and compiler variables, OpenMP flags, and tracking for MPI::MPI_Fortran dependencies.
ESMF::ESMC ESMF::ESMF_C C/C++ Configured with native C/C++ compiler variables, OpenMP flags, and tracking for MPI::MPI_C dependencies.

6.2 CMake method using module mode

An ESMF CMake find file is located at: <ESMF source or install directory>/cmake/FindESMF.cmake

The find file will parse the esmf.mk file created following a successful ESMF build. (See 6.3 for a description of the esmf.mk file.) The CMake module sets esmf.mk variables as global CMake variables. When using the find file, ESMFMKFILE must be set to the filepath of esmf.mk. If this is NOT set, then ESMF_FOUND will always be FALSE. If ESMFMKFILE exists, then ESMF_FOUND=TRUE and all ESMF makefile variables will be set in the global scope. Optionally, set ESMF_MKGLOBALS to a string list to filter makefile variables. For example, to globally scope only ESMF_LIBSDIR and ESMF_APPSDIR variables, use this CMake command in CMakeLists.txt: set(ESMF_MKGLOBALS "LIBSDIR" "APPSDIR")


6.3 GNU Make method using the esmf.mk file

Every ESMF installation provides a file named esmf.mk that contains the information needed to build a user application gainst the installation. The location of the esmf.mk file should be documented by the party that installed ESMF on the system. We recommend that a single ESMF specific environment variable, ESMFMKFILE, be provided by the system that points to the esmf.mk file. See section 9.9 for the related discussion aimed at the person that installs ESMF on a system.

The information in esmf.mk is defined in form of variables. In fact, syntactically esmf.mk is a makefile fragment and can be imported by an application specific makefile via the include command. All the variables in esmf.mk start with the "ESMF_" prefix to prevent conflicts. The information in esmf.mk is fully specified and is not affected by any variables set in the user's environment.

The information defined in esmf.mk includes Fortran compiler and linker, as well as C++ compiler and linker. It further includes the recommended Fortran and C++ specific compiler and linker flags for building ESMF applications. One way of using the esmf.mk is to glean the necessary information from it. This information can then be used either directly on the command line when compiling a user application, or to hardwire the settings into the application specific build system. However, the recommended use of esmf.mk is to include this file in the application specific makefile directly via the include command.

The Makefile template below demonstrates how a user build system can be constructed to leverage the esmf.mk file. In practice, most user build systems will be more complex. However, this template does show that the added complexity introduced by using esmf.mk is minimal. Examples of how to use this build system in realistic user scenarios can be found in the external demos.

The advantages of using esmf.mk, over hard coding suitable compiler and linker flags into the user build system directly, are robustness and portability. Robustness is a consequence of the fact that everything defined in esmf.mk corresponds to the exact settings used during the ESMF library build (consistency) and during the ESMF test suite build. Using esmf.mk thus guarantees that the user application is build in the exact same manner as the ESMF test suite applications that undergo strict regression testing before every ESMF release. Portability means that a user build system, which uses esmf.mk in the way the template Makefile demonstrates, will function as expected on any system where ESMF was successfully installed and tested, without the need of modifying anything. Every esmf.mk is generated during a specific ESMF installation using the ESMF tested settings for the host platform.

################################################################################
### Makefile template for user ESMF application, leveraging esmf.mk mechanism ##
################################################################################

################################################################################
### Finding and including esmf.mk ##############################################

# Note: This fully portable Makefile template depends on finding environment
#       variable "ESMFMKFILE" set to point to the appropriate "esmf.mk" file,
#       as is discussed in the User's Guide.
#       However, you can still use this Makefile template even if the person
#       that installed ESMF on your system did not provide for a mechanism to
#       automatically set the environment variable "ESMFMKFILE". In this case
#       either manually set "ESMFMKFILE" in your environment or hard code the
#       location of "esmf.mk" into the include statement below.
#       Notice that the latter approach has negative impact on portability.

ifneq ($(origin ESMFMKFILE), environment)
$(error Environment variable ESMFMKFILE was not set.)
endif

include $(ESMFMKFILE)

################################################################################
### Compiler and linker rules using ESMF_ variables supplied by esmf.mk ########

.SUFFIXES: .f90 .F90 .c .C

.f90:
	$(ESMF_F90COMPILER) -c $(ESMF_F90COMPILEOPTS) $(ESMF_F90COMPILEPATHS) \
          $(ESMF_F90COMPILEFREENOCPP) $<
	$(ESMF_F90LINKER) $(ESMF_F90LINKOPTS) $(ESMF_F90LINKPATHS) \
          $(ESMF_F90LINKRPATHS) -o $@ $*.o $(ESMF_F90ESMFLINKLIBS)        

.F90:
	$(ESMF_F90COMPILER) -c $(ESMF_F90COMPILEOPTS) $(ESMF_F90COMPILEPATHS) \
          $(ESMF_F90COMPILEFREECPP) $(ESMF_F90COMPILECPPFLAGS) $<
	$(ESMF_F90LINKER) $(ESMF_F90LINKOPTS) $(ESMF_F90LINKPATHS) \
          $(ESMF_F90LINKRPATHS) -o $@ $*.o $(ESMF_F90ESMFLINKLIBS)        
        
.c:
	$(ESMF_CXXCOMPILER) -c $(ESMF_CXXCOMPILEOPTS) \
          $(ESMF_CXXCOMPILEPATHSLOCAL) $(ESMF_CXXCOMPILEPATHS) \
          $(ESMF_CXXCOMPILECPPFLAGS) $<
	$(ESMF_CXXLINKER) $(ESMF_CXXLINKOPTS) $(ESMF_CXXLINKPATHS) \
          $(ESMF_CXXLINKRPATHS) -o $@ $*.o $(ESMF_CXXESMFLINKLIBS)

.C:
	$(ESMF_CXXCOMPILER) -c $(ESMF_CXXCOMPILEOPTS) \
          $(ESMF_CXXCOMPILEPATHSLOCAL) $(ESMF_CXXCOMPILEPATHS) \
          $(ESMF_CXXCOMPILECPPFLAGS) $<
	$(ESMF_CXXLINKER) $(ESMF_CXXLINKOPTS) $(ESMF_CXXLINKPATHS) \
          $(ESMF_CXXLINKRPATHS) -o $@ $*.o $(ESMF_CXXESMFLINKLIBS)

################################################################################
### Sample targets for user ESMF applications ##################################

all: esmf_UserApplication esmc_UserApplication

esmf_UserApplication:

esmc_UserApplication:

################################################################################

Notice that the ESMF_F90LINKPATHS, ESMF_F90LINKRPATHS, ESMF_CXXLINKPATHS, and ESMF_CXXLINKRPATHS variables used in the linking targets might contain paths to the specific compiler version, MPI implementation, and 3rd party libraries (see section 9.4) used when building ESMF. The paths are explicitly included in order to simplify the process of writing an application build system that is consistent with the ESMF library that is used.

There are, however, situations where it is desirable to let the application decide what compiler version, MPI version, and/or 3rd party library verson (e.g. NetCDF) to use. To this end, esmf.mk defines an alternative set of variables: ESMF_F90ESMFLINKPATHS, ESMF_F90ESMFLINKRPATHS, ESMF_CXXESMFLINKPATHS, and ESMF_CXXESMFLINKRPATHS. These variables only encode the precise path to the ESMF library, and do not specify where to find the compiler, MPI, and/or 3rd party libraries. When using this alternative set of variables, it becomes the responsibility of the application build system to ensure the required libraries can be found by the linker, and are compatible with the ESMF installation.

esmf_support@ucar.edu