[CMake] FindPETSc attempt, FindMPI brittleness, common dependencies

Jed Brown jed at 59A2.org
Mon Oct 6 04:30:22 EDT 2008


Attached is an attempt at a FindPETSc module.  Since PETSc is often
linked to many libraries (more than 40, configuration dependent, in many
different directories) it is unreasonable to ask the user to provide all
libs.  This module uses PETSC_DIR and PETSC_ARCH (when using the PETSc
build system these must be environment variables so we check there if
these are not in cache) to determine the correct paths.  It parses the
link line in order, creating a reverse order list of library paths to be
used as HINTS for find_library.

I have noticed two problems with FindMPI.  First, even when I specify
MPI_COMPILER to be a nonstandard path, it still finds MPIEXEC in
/usr/bin.  Second, while it uses MPI_LINK_PATH as HINTS, it first
constructs the full list of libraries and then finds all the libraries.
This will not work correctly if the link line is `-L/A -la -L/B -lb' and
/B/liba.so exists.  I think that the same construction as in FindPETSc
needs to be used here.

Currently my FindPETSc module sets PETSC_MPIEXEC and PETSC_COMPILER,
these can be used to set MPIEXEC and MPI_COMPILER in order to let the
PETSc configuration determine which MPI is used.

I have another library which uses a related configuration method.  There
is a file /path/to/lib/iMesh-Defs.inc which is intended to be included
in the Makefile and sets variables IMESH_INCLUDES and IMESH_LIBS.  I am
using a similar scheme of writing a temporary Makefile, using
exec_program [1] to extract the variables through the Makefile,
constructing a reverse-order list of library paths as I traverse
IMESH_LIBS, and using this as HINTS to find_library.  Maybe this
functionality could be put in a separate module?

The link line could probably be improved by retaining only the last
occurrence of each library.  REMOVE_DUPLICATES works on my system, but I
think it would break when the link line is `-la -lb -la' where libb
depends on liba.  Am I wrong?

How can we determine at configuration time whether a given set of
libraries are linked against the same MPI (and other common intermediate
libs such as ParMetis, Zoltan, HDF5, etc)?  This is critical since
different MPI implementations are not binary-compatible (for instance
MPI_Comm has size 4 on x86_64 MPICH and size 8 with Open MPI or LAM).
The current approach is entirely manual (the *user* must know the link
tree of each library intermediate lib and manually check consistency in
advanced mode) and can build cleanly but fail in a nasty way (seg fault
at runtime).

[1] execute_process is broken for me with CMake 2.6.2.
  execute_process (echo foo bar OUTPUT_VARIABLE THIS_IS_EMPTY)
  exec_program (echo ARGS foo bar OUTPUT_VARIABLE THIS_HAS_STRING)

Jed
-------------- next part --------------
# - Try to find PETSc
# Once done this will define
#
#  PETSC_FOUND - system has PETSc
#  PETSC_INCLUDE_PATH - the PETSc include directories
#  PETSC_LIBRARIES - Link these to use PETSc
#  PETSC_COMPILER - Compiler used by PETSc
#  PETSC_DEFINITIONS - Compiler switches required for using PETSc
#  PETSC_MPIEXEC - Executable for running MPI programs
#
# Setting these changes the behavior of the search
#  PETSC_DIR - directory in which PETSc resides
#  PETSC_ARCH - build architecture
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
#

# If unset, try environment
if (NOT PETSC_DIR)
  set (PETSC_DIR $ENV{PETSC_DIR})
endif (NOT PETSC_DIR)
if (NOT PETSC_ARCH)
  set (PETSC_ARCH $ENV{PETSC_ARCH})
endif (NOT PETSC_ARCH)

# Crude attempt to determine PETSC_DIR, not useful since we can't
# determine PETSC_ARCH
if (NOT PETSC_DIR)
  find_path (PETSC_DIR include/petsc.h
    PATHS /usr /usr/local $ENV{HOME}/petsc)
endif (NOT PETSC_DIR)

# The configuration is current if both PETSC_DIR and PETSC_ARCH are
# equal to their saved values.  On the first pass, these will match if
# nothing is in the environment, this is okay since PETSC_INCLUDE_PATH
# and PETSC_LIBRARIES are unset.
set (PETSC_CONFIG_CURRENT TRUE)
if (NOT "${PETSC_DIR}" STREQUAL "${PETSC_DIR_PRIVATE}")
  set (PETSC_CONFIG_CURRENT FALSE)
endif (NOT "${PETSC_DIR}" STREQUAL "${PETSC_DIR_PRIVATE}")
if (NOT "${PETSC_ARCH}" STREQUAL "${PETSC_ARCH_PRIVATE}")
  set (PETSC_CONFIG_CURRENT FALSE)
endif (NOT "${PETSC_ARCH}" STREQUAL "${PETSC_ARCH_PRIVATE}")

# Determine whether the PETSc layout is old-style (through 2.3.3) or
# new-style (not yet released, petsc-dev)
if (EXISTS ${PETSC_DIR}/${PETSC_ARCH}/include/petscconf.h) # new
  set (PETSC_CONF_BASE ${PETSC_DIR}/conf/base)
elseif (EXISTS ${PETSC_DIR}/bmake/${PETSC_ARCH}/petscconf.h) # old
  set (PETSC_CONF_BASE ${PETSC_DIR}/bmake/common/base)
else (EXISTS ${PETSC_DIR}/bmake/${PETSC_ARCH}/petscconf.h)
  # The layout is not recognized, how can we give a meaningful warning?
endif (EXISTS ${PETSC_DIR}/${PETSC_ARCH}/include/petscconf.h)

if (PETSC_CONFIG_CURRENT AND PETSC_INCLUDE_PATH AND PETSC_LIBRARIES)
  # Do nothing: all variables are in cache
elseif (PETSC_DIR AND PETSC_ARCH AND CMAKE_MAKE_PROGRAM AND PETSC_CONF_BASE)
  set (PETSC_DIR_PRIVATE ${PETSC_DIR} CACHE INTERNAL "Saved value" FORCE)
  set (PETSC_ARCH_PRIVATE ${PETSC_ARCH} CACHE INTERNAL "Saved value" FORCE)

  # Put variables into environment since they are needed to get
  # configuration (petscvariables) in the PETSc makefile
  set (ENV{PETSC_DIR} ${PETSC_DIR})
  set (ENV{PETSC_ARCH} ${PETSC_ARCH})

  # A temporary makefile to probe the PETSc configuration
  set (PETSC_CONFIG_MAKEFILE ${dohp_BINARY_DIR}/Makefile.petsc)

  file (WRITE ${PETSC_CONFIG_MAKEFILE}
"## This file was autogenerated by FindPETSc.cmake
# PETSC_DIR  = ${PETSC_DIR}
# PETSC_ARCH = ${PETSC_ARCH}
include ${PETSC_CONF_BASE}
show_clinker :
	- at echo \${CLINKER}
show_lib_line :
	- at echo \${PETSC_LIB}
show_cpp_line :
	- at echo \${PETSC_CCPPFLAGS}
show_flags :
	- at echo \${CCPPFLAGS}
show_cc : 
	- at echo \${PCC}
show_mpiexec :
	- at echo \${MPIEXEC}
")

  exec_program(${CMAKE_MAKE_PROGRAM}
    ARGS -f ${PETSC_CONFIG_MAKEFILE} show_cpp_line
    OUTPUT_VARIABLE PETSC_CPP_LINE
    RETURN_VALUE PETSC_RETURN)

  exec_program(${CMAKE_MAKE_PROGRAM}
    ARGS -f ${PETSC_CONFIG_MAKEFILE} show_lib_line
    OUTPUT_VARIABLE PETSC_LIB_LINE
    RETURN_VALUE PETSC_RETURN)

  exec_program(${CMAKE_MAKE_PROGRAM}
    ARGS -f ${PETSC_CONFIG_MAKEFILE} show_cc
    OUTPUT_VARIABLE PETSC_CC
    RETURN_VALUE PETSC_RETURN)

  exec_program(${CMAKE_MAKE_PROGRAM}
    ARGS -f ${PETSC_CONFIG_MAKEFILE} show_mpiexec
    OUTPUT_VARIABLE PETSC_MPIEXEC
    RETURN_VALUE PETSC_RETURN)

  file (REMOVE ${PETSC_CONFIG_MAKEFILE})

  # Extract include paths from compile command line
  string (REGEX MATCHALL "-I([^\" ]+|\"[^\"]+\")" PETSC_ALL_INCLUDE_PATHS "${PETSC_CPP_LINE}")
  set (PETSC_INCLUDE_PATH_WORK)
  foreach (IPATH ${PETSC_ALL_INCLUDE_PATHS})
    string (REGEX REPLACE "^-I" "" IPATH ${IPATH})
    string (REGEX REPLACE "//" "/" IPATH ${IPATH})
    list (APPEND PETSC_INCLUDE_PATH_WORK ${IPATH})
  endforeach (IPATH)
  list (REMOVE_DUPLICATES PETSC_INCLUDE_PATH_WORK)

  string (REGEX MATCHALL "(-L|-Wl,|-l)([^\" ]+|\"[^\"]+\")" PETSC_ALL_LINK_TOKENS "${PETSC_LIB_LINE}")
  set (PETSC_LINK_PATHS)
  set (PETSC_LINK_FLAGS_WORK)
  set (PETSC_LIBRARIES_FOUND)
  set (PETSC_LIBRARIES_MISSING)
  foreach (TOKEN ${PETSC_ALL_LINK_TOKENS})
    if (TOKEN MATCHES "-L([^\" ]+|\"[^\"]+\")") # If it's a library path, prepend it to the list
      string (REGEX REPLACE "^-L" "" TOKEN ${TOKEN})
      string (REGEX REPLACE "//" "/" TOKEN ${TOKEN})
      list (INSERT PETSC_LINK_PATHS 0 ${TOKEN})
    elseif (TOKEN MATCHES "-Wl,([^\" ]+|\"[^\"]+\")") # If it's a link flag, put it in the flags list
      if (PETSC_LINK_FLAGS_WORK)
	set (PETSC_LINK_FLAGS_WORK "${PETSC_LINK_FLAGS_WORK} ${TOKEN}")
      else (PETSC_LINK_FLAGS_WORK)
	set (PETSC_LINK_FLAGS_WORK ${TOKEN})
      endif (PETSC_LINK_FLAGS_WORK)
    elseif (TOKEN MATCHES "-l([^\" ]+|\"[^\"]+\")") # If it's a library, get the absolute path by searching in PETSC_LINK_PATHS
      string (REGEX REPLACE "^-l" "" TOKEN ${TOKEN})
      set (PETSC_LIB "PETSC_LIB-NOTFOUND" CACHE FILEPATH "Cleared" FORCE)
      find_library (PETSC_LIB ${TOKEN} HINTS ${PETSC_LINK_PATHS})
      if (PETSC_LIB)
	list (APPEND PETSC_LIBRARIES_FOUND ${PETSC_LIB})
      else (PETSC_LIB)
	list (APPEND PETSC_LIBRARIES_MISSING ${PETSC_LIB})
	message (SEND_ERROR "Unable to find PETSc library ${TOKEN}")
      endif (PETSC_LIB)      
    endif (TOKEN MATCHES "-L([^\" ]+|\"[^\"]+\")")
  endforeach (TOKEN)
  set (PETSC_LIB "PETSC_LIB-NOTFOUND" CACHE INTERNAL "Scratch variable for PETSc detection" FORCE)
  # This is okay on my system, but I think it would break easily.
  #list (REMOVE_DUPLICATES PETSC_LIBRARIES_FOUND)

  # We do an out-of-source build so __FILE__ will be an absolute path, hence defining __SDIR__ is superfluous
  set (PETSC_DEFINITIONS "-D__SDIR__=\"\"" CACHE STRING "PETSc definitions")

  # Sometimes this can be used to assist FindMPI.cmake
  set (PETSC_MPIEXEC ${PETSC_MPIEXEC} CACHE FILEPATH "Executable for running PETSc MPI programs")

  set (PETSC_INCLUDE_PATH ${PETSC_INCLUDE_PATH_WORK} CACHE STRING "PETSc include path" FORCE)
  set (PETSC_LIBRARIES ${PETSC_LIBRARIES_FOUND} CACHE STRING "PETSc libraries" FORCE)
  set (PETSC_COMPILER ${PETSC_CC} CACHE FILEPATH "PETSc compiler" FORCE)
endif (PETSC_CONFIG_CURRENT AND PETSC_INCLUDE_PATH AND PETSC_LIBRARIES)

set (PETSC_DIR ${PETSC_DIR} CACHE PATH "PETSc Directory")
set (PETSC_ARCH ${PETSC_ARCH} CACHE STRING "PETSc build architecture")

include (FindPackageHandleStandardArgs)
find_package_handle_standard_args (PETSc
  "PETSc could not be found.  Be sure to set PETSC_DIR and PETSC_ARCH."
  PETSC_INCLUDE_PATH PETSC_LIBRARIES)

# show the PETSC_INCLUDE_DIR and PETSC_LIBRARIES variables only in the advanced view
mark_as_advanced (PETSC_INCLUDE_PATH PETSC_LIBRARIES PETSC_COMPILER PETSC_DEFINITIONS PETSC_MPIEXEC)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 197 bytes
Desc: not available
URL: <http://www.cmake.org/pipermail/cmake/attachments/20081006/2cadc379/attachment.pgp>


More information about the CMake mailing list