Skip to content

Commit

Permalink
Merge pull request #64 from climbfuji/feature/add_find_gptl_pnetcdf_u…
Browse files Browse the repository at this point in the history
…dunits

Add find-package modules for GPTL, PnetCDF, udunits (unify JCSDA and EMC cmakemodules)
  • Loading branch information
climbfuji authored Jul 27, 2022
2 parents da47ad6 + 406705b commit cabd775
Show file tree
Hide file tree
Showing 3 changed files with 401 additions and 0 deletions.
180 changes: 180 additions & 0 deletions Modules/FindGPTL.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# (C) Copyright 2020- UCAR.
#
# Find the GPTL: General Purpose Timing Library (https://jmrosinski.github.io/GPTL/)
#
# This find module sets the following variables and targets:
#
# Variables:
# GPTL_FOUND - True if GPTL was found
# GPTL_VERSION_STRING - Version of installed GPTL
# GPTL_BIN_DIR - GPTL binary directory
# GPTL_HAS_PKG_CONFIG - GPTL was found with installed `gptl.pc` and pkg-config. This indicates full support
# for compiler and linker flags as exported by GPTL.
# Targets:
# GPTL::GPTL - Imported interface target to pass to target_link_libraries()
#
# NOTE: This find modules uses `pkg-config` to locate GPTL and glean the appropriate flags, directories,
# and link dependency ordering. For this to work, both a `pkg-config` executable and a `gptl.pc`
# config file need to be found.
# * To find the `pkg-config` executable, ensure it is on your PATH.
# * For non-standard locations the official CMake FindPkgConfig uses Cmake variable `PKG_CONFIG_EXECUTABLE`
# or environment variable `PKG_CONFIG`. See: https://cmake.org/cmake/help/latest/module/FindPkgConfig.html
# * To find `gptl.pc` ensure it is on the (colon-separated) directories listed in standard pkg-config
# environment variable `PKG_CONFIG_PATH`.
# * See: https://linux.die.net/man/1/pkg-config
# * A working GPTL pkg-config install can be confirmed on the command line, e.g.,
# ```
# $ pkg-config --modversion gptl
# 8.0.2
# ```
# To set a non-standard location for GPTL, ensure the correct `gptl.pc` pkg config file is found first
# on the environment's `PKG_CONFIG_PATH`. This can be checked with the pkg-config executable, e.g.,
# ```
# $ pkg-config --variable=prefix gptl
# /usr/local
# ```
# Only when pkg-config is not supported or available, GPTL will be searched by the standard CMake search procedures.
# Set environment or CMake variable GPTL_ROOT to control this search. The GPTL_ROOT variable will have no effect
# if GPTL_HAS_PKG_CONFIG=True.
#

find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
message(DEBUG "[FindGPTL] Using PKG_CONFIG_EXECUTABLE:${PKG_CONFIG_EXECUTABLE}")
endif()

#Helper:
#check_pkg_config(ret_var pcname pcflags...)
# Check if pcname is known to pkg-config
# Returns:
# Boolean: true if ${pcname}.pc file is found by pkg-config).
# Args:
# ret_var: return variable name.
# pcname: pkg-config name to look for (.pc file)
function(check_pkg_config ret_var pcname)
if(NOT PKG_CONFIG_FOUND OR NOT EXISTS ${PKG_CONFIG_EXECUTABLE})
set(${ret_var} False PARENT_SCOPE)
else()
execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --exists ${pcname} RESULT_VARIABLE _found)
if(_found EQUAL 0)
set(${ret_var} True PARENT_SCOPE)
else()
set(${ret_var} False PARENT_SCOPE)
endif()
endif()
endfunction()

#Helper:
#get_pkg_config(ret_var pcname pcflags...)
# Get the output of pkg-config
# Args:
# ret_var: return variable name
# pcname: pkg-config name to look for (.pc file)
# pcflags: pkg-config flags to pass
function(get_pkg_config ret_var pcname pcflags)
execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} ${ARGN} ${pcname} ${pcflags} OUTPUT_VARIABLE _out RESULT_VARIABLE _ret OUTPUT_STRIP_TRAILING_WHITESPACE)
if(_ret EQUAL 0)
separate_arguments(_out)
set(${ret_var} ${_out} PARENT_SCOPE)
else()
set(${ret_var} "" PARENT_SCOPE)
endif()
endfunction()

check_pkg_config(GPTL_HAS_PKG_CONFIG gptl)
if(GPTL_HAS_PKG_CONFIG)
#Use pkg-config to find the prefix, flags, directories, executables, and libraries
get_pkg_config(GPTL_VERSION_STRING gptl --modversion)
get_pkg_config(GPTL_PREFIX gptl --variable=prefix)
get_pkg_config(GPTL_INCLUDE_DIR gptl --cflags-only-I)
if(GPTL_INCLUDE_DIR)
string(REGEX REPLACE "-I([^ ]+)" "\\1" GPTL_INCLUDE_DIR ${GPTL_INCLUDE_DIR}) #Remove -I
endif()
if(NOT EXISTS ${GPTL_INCLUDE_DIR})
find_path(GPTL_INCLUDE_DIR NAMES gptl.h PATH_SUFFIXES include include/gptl PATHS ${GPTL_PREFIX} NO_DEFAULT_PATH)
endif()
find_path(GPTL_MODULE_DIR NAMES gptl.mod PATH_SUFFIXES include include/gptl module module/gptl PATHS ${GPTL_PREFIX} NO_DEFAULT_PATH)
get_pkg_config(GPTL_COMPILE_OPTIONS gptl --cflags-only-other)
get_pkg_config(GPTL_LINK_LIBRARIES gptl --libs-only-l)
get_pkg_config(GPTL_LINK_DIRECTORIES gptl --libs-only-L)
if(GPTL_LINK_DIRECTORIES)
string(REGEX REPLACE "-L([^ ]+)" "\\1" GPTL_LINK_DIRECTORIES ${GPTL_LINK_DIRECTORIES}) #Remove -L
endif()
get_pkg_config(GPTL_LINK_OPTIONS gptl --libs-only-other)
find_library(GPTL_LIBRARY NAMES gptl PATH_SUFFIXES lib lib64 PATHS ${GPTL_PREFIX} NO_DEFAULT_PATH)
find_path(GPTL_BIN_DIR NAMES gptl_avail gran_overhead print_mpistatus_size PATH_SUFFIXES bin PATHS ${GPTL_PREFIX} NO_DEFAULT_PATH)
else()
#Attempt to find GPTL without pkg-config as last resort.
if(NOT EXISTS ${PKG_CONFIG_EXECUTABLE})
message(WARNING "\
FindGPTL: The `pkg-config` executable was not found. Ensure it is on your path or set \
environment variable PKG_CONFIG to your pkg-config executable. \
Attempting to find GPTL without pkg-config support may cause some required compiler and linker options to be unset.")
else()
message(WARNING "\
FindGPTL: The `pkg-config` executable was found at: ${PKG_CONFIG_EXECUTABLE}. However no `gptl.pc` file was found on \
PKG_CONFIG_PATH='$ENV{PKG_CONFIG_PATH}'. \
Attempting to find GPTL without pkg-config support may cause some required compiler and linker options to be unset.")
endif()
find_path(GPTL_INCLUDE_DIR NAMES gptl.h PATH_SUFFIXES include include/gptl)
find_path(GPTL_MODULE_DIR NAMES gptl.mod PATH_SUFFIXES include include/gptl module module/gptl)
find_library(GPTL_LIBRARY NAMES gptl PATH_SUFFIXES lib lib64)
find_path(GPTL_BIN_DIR NAMES gptl_avail gran_overhead print_mpistatus_size PATH_SUFFIXES bin)
endif()

#Hide non-documented cache variables reserved for internal/advanced usage
mark_as_advanced( GPTL_INCLUDE_DIR
GPTL_MODULE_DIR
GPTL_LIBRARY )

#Debugging output
message(DEBUG "[FindGPTL] GPTL_FOUND: ${GPTL_FOUND}")
message(DEBUG "[FindGPTL] GPTL_VERSION_STRING: ${GPTL_VERSION_STRING}")
message(DEBUG "[FindGPTL] GPTL_HAS_PKG_CONFIG: ${GPTL_HAS_PKG_CONFIG}")
message(DEBUG "[FindGPTL] GPTL_PREFIX: ${GPTL_PREFIX}")
message(DEBUG "[FindGPTL] GPTL_BIN_DIR: ${GPTL_BIN_DIR}")
message(DEBUG "[FindGPTL] GPTL_INCLUDE_DIR: ${GPTL_INCLUDE_DIR}")
message(DEBUG "[FindGPTL] GPTL_MODULE_DIR: ${GPTL_MODULE_DIR}")
message(DEBUG "[FindGPTL] GPTL_LIBRARY: ${GPTL_LIBRARY}")
message(DEBUG "[FindGPTL] GPTL_LINK_LIBRARIES: ${GPTL_LINK_LIBRARIES}")
message(DEBUG "[FindGPTL] GPTL_LINK_DIRECTORIES: ${GPTL_LINK_DIRECTORIES}")
message(DEBUG "[FindGPTL] GPTL_LINK_OPTIONS: ${GPTL_LINK_OPTIONS}")

#Check package has been found correctly
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
GPTL
REQUIRED_VARS
GPTL_LIBRARY
GPTL_INCLUDE_DIR
GPTL_MODULE_DIR
GPTL_BIN_DIR
VERSION_VAR
GPTL_VERSION_STRING
)

#Create GPTL::GPTL imported interface target
if(GPTL_FOUND AND NOT TARGET GPTL::GPTL)
add_library(GPTL::GPTL INTERFACE IMPORTED)
set_property(TARGET GPTL::GPTL PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${GPTL_INCLUDE_DIR})
if(GPTL_MODULE_DIR)
set_property(TARGET GPTL::GPTL APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${GPTL_MODULE_DIR})
endif()
if(GPTL_COMPILE_OPTIONS)
set_property(TARGET GPTL::GPTL PROPERTY INTERFACE_COMPILE_OPTIONS ${GPTL_COMPILE_OPTIONS})
endif()
if(GPTL_LINK_DIRECTORIES)
set_property(TARGET GPTL::GPTL PROPERTY INTERFACE_LINK_DIRECTORIES ${GPTL_LINK_DIRECTORIES})
endif()
if(GPTL_LINK_OPTIONS)
set_property(TARGET GPTL::GPTL PROPERTY INTERFACE_LINK_OPTIONS ${GPTL_LINK_OPTIONS})
endif()
if(GPTL_LINK_LIBRARIES)
set_property(TARGET GPTL::GPTL PROPERTY INTERFACE_LINK_LIBRARIES ${GPTL_LINK_LIBRARIES})
else()
set_property(TARGET GPTL::GPTL PROPERTY INTERFACE_LINK_LIBRARIES ${GPTL_LIBRARY})
get_filename_component(_lib_dir ${GPTL_LIBRARY} DIRECTORY)
set_property(TARGET GPTL::GPTL APPEND PROPERTY INTERFACE_LINK_DIRECTORIES ${_lib_dir})
unset(_lib_dir)
endif()
endif()
174 changes: 174 additions & 0 deletions Modules/FindPnetCDF.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# (C) Copyright 2020- UCAR.
#
# Find PnetCDF: A Parallel I/O Library for NetCDF File Access
# https://parallel-netcdf.github.io/
#
# Components available for query:
# C - Has C support
# CXX - Has CXX support
# Fortran - Has Fortran support
# NetCDF4 - Has NetCDF4 output support
# GPTL - Has profiling support with GPTL enabled
# Threads - Has thread safety enabled
#
# Variables provided:
# PnetCDF_FOUND - True if PnetCDFL was found
# PnetCDF_CONFIG_EXE - pnetcdf-config executable if found
# PnetCDF_VERSION - Version of installed PnetCDF
# PnetCDF_BIN_DIR - PnetCDF binary directory
# PnetCDF_DEBUG - True if PnetCDF is built in debug mode
#
# Targets provided:
# PnetCDF::PnetCDF_Fortran - Fortran interface target
# PnetCDF::PnetCDF_C - C interface target
# PnetCDF::PnetCDF_CXX - CXX interface target
#
# Functions provided:
# pnetcdf_get_config(ret_var flags) - Call `pnetcdf-config` with flags and set ret_var with output on execution success.
#
#
# This module requires the `pnetcdf-config` executable to detect the directories and compiler and linker flags
# necessary for the PnetCDF::PnetCDF target. To control where PnetCDF is found:
# * Option 1: Set an environment or cmake variable `PnetCDF_ROOT` to the install prefix for PnetCDF (e.g. /usr/local)
# * Option 2: Set an environment or cmake variable `PnetCDF_CONFIG_EXE` to the full path to the `pnetcdf-config`
# (e.g., /usr/local/bin/pnetcdf-config)
#

find_program(PnetCDF_CONFIG_EXE NAMES pnetcdf-config PATH_SUFFIXES bin bin64 PATHS
$ENV{PnetCDF_CONFIG_EXE} ${PnetCDF_ROOT} $ENV{PnetCDF_ROOT} ${PNETCDF_ROOT} $ENV{PNETCDF_ROOT})
message(DEBUG "[FindPnetCDF] Using PnetCDF_CONFIG_EXE:${PnetCDF_CONFIG_EXE}")

# pnetcdf_get_config(ret_var flags...)
# Get the output of pnetcdf-config
# Args:
# ret_var: return variable name
# flags: flags to pass to pnetcdf-config
function(pnetcdf_get_config ret_var pcflags)
execute_process(COMMAND ${PnetCDF_CONFIG_EXE} ${pcflags} OUTPUT_VARIABLE _out RESULT_VARIABLE _ret OUTPUT_STRIP_TRAILING_WHITESPACE)
if(_ret EQUAL 0)
separate_arguments(_out)
set(${ret_var} ${_out} PARENT_SCOPE)
else()
set(${ret_var} "" PARENT_SCOPE)
endif()
endfunction()

## Find libraries and paths, and determine found components
if(EXISTS ${PnetCDF_CONFIG_EXE})
#Use pnetcdf-config to find the prefix, flags, directories, executables, and libraries
pnetcdf_get_config(PnetCDF_VERSION --version)
string(REGEX MATCH "([0-9.]+)" PnetCDF_VERSION "${PnetCDF_VERSION}") #Match only version actual number

pnetcdf_get_config(PnetCDF_PREFIX --prefix)
pnetcdf_get_config(PnetCDF_CXX_FOUND --has-c++)
pnetcdf_get_config(PnetCDF_Fortran_FOUND --has-fortran)
pnetcdf_get_config(PnetCDF_NetCDF4_FOUND --netcdf4)
pnetcdf_get_config(PnetCDF_GPTL_FOUND --profiling)
pnetcdf_get_config(PnetCDF_Threads_FOUND --thread-safe)
pnetcdf_get_config(PnetCDF_DEBUG --debug)
pnetcdf_get_config(PnetCDF_INCLUDE_DIR --includedir)
pnetcdf_get_config(PnetCDF_LIB_DIR --libdir)

#Translate boolean variables from pnetcdf-config enabled/disabled to True/False
foreach(_var IN ITEMS PnetCDF_CXX_FOUND PnetCDF_Fortran_FOUND PnetCDF_NetCDF4_FOUND PnetCDF_GPTL_FOUND PnetCDF_Threads_FOUND PnetCDF_DEBUG)
if( ${_var} MATCHES "(enabled)|([Yy][Ee][Ss])")
set(${_var} True)
else()
set(${_var} False)
endif()
endforeach()

find_path(PnetCDF_MODULE_DIR NAMES pnetcdf.mod HINTS ${PnetCDF_PREFIX} ${PnetCDF_INCLUDE_DIR}
PATH_SUFFIXES include include/pnetcdf module module/pnetcdf lib/pnetcdf/module NO_DEFAULT_PATH)
if(PnetCDF_Fortran_FOUND AND NOT EXISTS ${PnetCDF_MODULE_DIR})
message(WARNING "[PnetCDF] pnetcdf-config --has-fortran=yes, but could not find pnetcdf.mod. Set PnetCDF_MODULE_DIR to path containing pnetcdf.mod")
set(PnetCDF_Fortran_FOUND NO)
endif()

if(PnetCDF_INCLUDE_DIR AND PnetCDF_LIB_DIR)
set(PnetCDF_C_FOUND True)
endif()

find_path(PnetCDF_BIN_DIR NAMES pnetcdf-config PATH_SUFFIXES bin PATHS ${PnetCDF_PREFIX} NO_DEFAULT_PATH)
find_library(PnetCDF_LIBRARY NAMES pnetcdf PATH_SUFFIXES lib lib64 PATHS ${PnetCDF_PREFIX} NO_DEFAULT_PATH)
#Hide non-documented cache variables reserved for internal/advanced usage
mark_as_advanced( PnetCDF_MODULE_DIR PnetCDF_LIBRARY )
endif()

## Debugging output
message(DEBUG "[FindPnetCDF] PnetCDF_CONFIG_EXE: ${PnetCDF_CONFIG_EXE}")
message(DEBUG "[FindPnetCDF] PnetCDF_VERSION: ${PnetCDF_VERSION}")
message(DEBUG "[FindPnetCDF] PnetCDF_C_FOUND: ${PnetCDF_C_FOUND}")
message(DEBUG "[FindPnetCDF] PnetCDF_CXX_FOUND: ${PnetCDF_CXX_FOUND}")
message(DEBUG "[FindPnetCDF] PnetCDF_Fortran_FOUND: ${PnetCDF_Fortran_FOUND}")
message(DEBUG "[FindPnetCDF] PnetCDF_NetCDF4_FOUND: ${PnetCDF_NetCDF4_FOUND}")
message(DEBUG "[FindPnetCDF] PnetCDF_GPTL_FOUND: ${PnetCDF_GPTL_FOUND}")
message(DEBUG "[FindPnetCDF] PnetCDF_Threads_FOUND: ${PnetCDF_Threads_FOUND}")
message(DEBUG "[FindPnetCDF] PnetCDF_DEBUG: ${PnetCDF_DEBUG}")
message(DEBUG "[FindPnetCDF] PnetCDF_PREFIX: ${PnetCDF_PREFIX}")
message(DEBUG "[FindPnetCDF] PnetCDF_BIN_DIR: ${PnetCDF_BIN_DIR}")
message(DEBUG "[FindPnetCDF] PnetCDF_INCLUDE_DIR: ${PnetCDF_INCLUDE_DIR}")
message(DEBUG "[FindPnetCDF] PnetCDF_MODULE_DIR: ${PnetCDF_MODULE_DIR}")
message(DEBUG "[FindPnetCDF] PnetCDF_LIB_DIR: ${PnetCDF_LIB_DIR}")

## Check package has been found correctly
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
PnetCDF
REQUIRED_VARS
PnetCDF_CONFIG_EXE
PnetCDF_PREFIX
VERSION_VAR
PnetCDF_VERSION
HANDLE_COMPONENTS
)
message(DEBUG "[FindPnetCDF] PnetCDF_FOUND: ${PnetCDF_FOUND}")

## Create targets
set(_new_components)

# PnetCDF::PnetCDF_Fortran imported interface target
if(PnetCDF_Fortran_FOUND AND NOT TARGET PnetCDF::PnetCDF_Fortran)
add_library(PnetCDF::PnetCDF_Fortran INTERFACE IMPORTED)
set_target_properties(PnetCDF::PnetCDF_Fortran PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${PnetCDF_INCLUDE_DIR}
INTERFACE_LINK_DIRECTORIES ${PnetCDF_LIB_DIR})
if(PnetCDF_MODULE_DIR AND NOT PnetCDF_MODULE_DIR STREQUAL PnetCDF_INCLUDE_DIR )
set_property(TARGET PnetCDF::PnetCDF_Fortran APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PnetCDF_MODULE_DIR})
endif()
set(_new_components 1)
target_link_libraries(PnetCDF::PnetCDF_Fortran INTERFACE -lpnetcdf)
endif()

# PnetCDF::PnetCDF_C imported interface target
if(PnetCDF_C_FOUND AND NOT TARGET PnetCDF::PnetCDF_C)
add_library(PnetCDF::PnetCDF_C INTERFACE IMPORTED)
set_target_properties(PnetCDF::PnetCDF_C PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${PnetCDF_INCLUDE_DIR}
INTERFACE_LINK_DIRECTORIES ${PnetCDF_LIB_DIR})
set(_new_components 1)
target_link_libraries(PnetCDF::PnetCDF_C INTERFACE -lpnetcdf)
endif()

# PnetCDF::PnetCDF_CXX imported interface target
if(PnetCDF_CXX_FOUND AND NOT TARGET PnetCDF::PnetCDF_CXX)
add_library(PnetCDF::PnetCDF_CXX INTERFACE IMPORTED)
set_target_properties(PnetCDF::PnetCDF_CXX PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${PnetCDF_INCLUDE_DIR}
INTERFACE_LINK_DIRECTORIES ${PnetCDF_LIB_DIR})
set(_new_components 1)
target_link_libraries(PnetCDF::PnetCDF_CXX INTERFACE -lpnetcdf)
endif()

## Print status
if(${CMAKE_FIND_PACKAGE_NAME}_FOUND AND NOT ${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY AND _new_components)
message( STATUS "Find${CMAKE_FIND_PACKAGE_NAME}:" )
message( STATUS " - ${CMAKE_FIND_PACKAGE_NAME}_VERSION [${${CMAKE_FIND_PACKAGE_NAME}_VERSION}]")
message( STATUS " - ${CMAKE_FIND_PACKAGE_NAME}_PREFIX [${${CMAKE_FIND_PACKAGE_NAME}_PREFIX}]")
set(_found_comps)
foreach( _comp IN ITEMS Fortran C CXX NetCDF4 GPTL Threads )
if( ${CMAKE_FIND_PACKAGE_NAME}_${_comp}_FOUND )
list(APPEND _found_comps ${_comp})
endif()
endforeach()
message( STATUS " - ${CMAKE_FIND_PACKAGE_NAME} Components Found: ${_found_comps}")
unset(_found_comps)
endif()
unset(_new_components)
Loading

0 comments on commit cabd775

Please sign in to comment.