Skip to content

Commit

Permalink
move MPI build scripts into here
Browse files Browse the repository at this point in the history
  • Loading branch information
scivision committed Aug 2, 2024
1 parent b747907 commit ef157ee
Show file tree
Hide file tree
Showing 11 changed files with 345 additions and 8 deletions.
4 changes: 2 additions & 2 deletions build_nano.cmake
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# download, build, install nano text editor
# requires Autotools and GNU Make

cmake_minimum_required(VERSION 3.19)
cmake_minimum_required(VERSION 3.21)

set(CMAKE_EXECUTE_PROCESS_COMMAND_ECHO STDOUT)

Expand All @@ -10,7 +10,7 @@ string(JSON version GET ${_j} "nano")

set(stem nano-${version})
set(prefix "~/${stem}")
get_filename_component(prefix ${prefix} ABSOLUTE)
file(REAL_PATH ${prefix} prefix EXPAND_TILDE)

option(CMAKE_TLS_VERIFY "verify certificates" true)

Expand Down
2 changes: 1 addition & 1 deletion download_clang.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ if(NOT prefix)
set(prefix ~/clang-${version})
endif()

get_filename_component(prefix ${prefix} ABSOLUTE)
file(REAL_PATH ${prefix} prefix EXPAND_TILDE)

if(NOT DEFINED n)
if(WIN32)
Expand Down
10 changes: 10 additions & 0 deletions scripts/build_mpich.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# USAGE:
# cmake -Dprefix=~/mpi -P build_mpich.cmake
cmake_minimum_required(VERSION 3.20)

execute_process(COMMAND
${CMAKE_COMMAND} -Dargs=-Dmpich:BOOL=true
-P ${CMAKE_CURRENT_LIST_DIR}/build_openmpi.cmake
)

# NOTE: to pass-through arguments, don't quote them
4 changes: 2 additions & 2 deletions scripts/build_ninja/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# build and install a recent Ninja version

cmake_minimum_required(VERSION 3.19)
cmake_minimum_required(VERSION 3.21)

project(ninja LANGUAGES C CXX)

Expand All @@ -14,7 +14,7 @@ if(NOT version)
endif()

if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
get_filename_component(p "~/ninja-${version}" ABSOLUTE)
file(REAL_PATH "~/ninja-${version}" p EXPAND_TILDE)
set_property(CACHE CMAKE_INSTALL_PREFIX PROPERTY VALUE "${p}")
endif()

Expand Down
35 changes: 35 additions & 0 deletions scripts/build_openmpi.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# USAGE:
# cmake -Dprefix=~/mpi -P build_openmpi.cmake
cmake_minimum_required(VERSION 3.20)

if(prefix)
list(APPEND args -DCMAKE_INSTALL_PREFIX:PATH=${prefix})
endif()

if(NOT bindir)
execute_process(COMMAND mktemp -d
OUTPUT_VARIABLE bindir
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE ret
)
if(NOT ret EQUAL 0)
string(RANDOM LENGTH 6 r)
set(bindir /tmp/build_${r})
endif()
endif()

execute_process(COMMAND ${CMAKE_COMMAND}
${args}
-B${bindir}
-S${CMAKE_CURRENT_LIST_DIR}/mpi
COMMAND_ERROR_IS_FATAL ANY
)

message(STATUS "MPI build in ${bindir}")


execute_process(COMMAND ${CMAKE_COMMAND} --build ${bindir}
COMMAND_ERROR_IS_FATAL ANY
)

message(STATUS "MPI install complete.")
4 changes: 2 additions & 2 deletions scripts/install_ninja.cmake
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.19...3.30)
cmake_minimum_required(VERSION 3.21...3.30)

include(FetchContent)

Expand All @@ -14,7 +14,7 @@ endif()
if(NOT prefix)
set(prefix ~/ninja-${version})
endif()
get_filename_component(prefix ${prefix} ABSOLUTE)
file(REAL_PATH ${prefix} prefix EXPAND_TILDE)

file(MAKE_DIRECTORY ${prefix})

Expand Down
136 changes: 136 additions & 0 deletions scripts/mpi/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
cmake_minimum_required(VERSION 3.21...3.28)

if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(FATAL_ERROR "Use out of source build like
cmake -Bbuild")
endif()

project(BuildMPI LANGUAGES C CXX Fortran)

include(CheckIncludeFile)
include(ExternalProject)

option(mpich "Build MPICH instead of OpenMPI")

option(CMAKE_TLS_VERIFY "Verify TLS certs" on)

list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake)

file(GENERATE OUTPUT .gitignore CONTENT "*")

# --- check for updated external projects when "false"
set_property(DIRECTORY PROPERTY EP_UPDATE_DISCONNECTED true)


find_package(Autotools REQUIRED)

# --- determine number of cores for parallel build
# MPI builds spawn too many threads with bare "make -j" giving build crashes like
# libtool: fork: Resource temporarily unavailable
# clang: error: unable to execute command: posix_spawn failed: Resource temporarily unavailable
if(DEFINED ENV{CMAKE_BUILD_PARALLEL_LEVEL})
set(Ncpu $ENV{CMAKE_BUILD_PARALLEL_LEVEL})
else()
cmake_host_system_information(RESULT Ncpu QUERY NUMBER_OF_PHYSICAL_CORES)
endif()

set(mpi_flags --prefix=${CMAKE_INSTALL_PREFIX})

# to avoid ABI problems and confusing build or runtime errors, mandate that C and C++ are same compiler vendor
if(NOT CMAKE_C_COMPILER_ID STREQUAL CMAKE_CXX_COMPILER_ID)
message(FATAL_ERROR "C compiler ${CMAKE_C_COMPILER_ID} and C++ compiler ${CMAKE_CXX_COMPILER_ID} must be the same to avoid ABI build/runtime errors.
Set environment variables CC and CXX to the same compiler by prepending the command line:
CC=gcc-13 CXX=g++-13")
endiF()

# help OpenMPI/MPICH by hinting compilers, particularly on MacOS
# assume whatever the C compiler is, the C++ and Fortran compilers are the same vendor
if(CMAKE_C_COMPILER_ID MATCHES "GNU|$Intel")
# OpenMPI / MPICH needs full path to oneAPI compilers
list(APPEND mpi_flags CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} FC=${CMAKE_Fortran_COMPILER})
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang$")
# until Flang is ready, use Gfortran
list(APPEND mpi_flags CC=clang CXX=clang++ FC=gfortran)
endif()

# OpenMPI/MPICH have trouble finding -lm on MacOS especially
find_library(math NAMES m REQUIRED)
cmake_path(GET math PARENT_PATH math_LIBDIR)

set(mpi_ldflags "LDFLAGS=${CMAKE_LIBRARY_PATH_FLAG}${math_LIBDIR}")

# --- read JSON with URLs for each library
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/../versions.json json_meta)

if(mpich)
check_include_file("ISO_Fortran_binding.h" HAVE_ISO_FORTRAN_BINDING_H)
if(NOT HAVE_ISO_FORTRAN_BINDING_H)
message(FATAL_ERROR "ISO_Fortran_binding.h not found with ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION} ${CMAKE_C_COMPILER}
We suggest GCC compiler. Specify GCC by prepending \"CC=gcc-13\" or similar to your command.
Setting \"CC=gcc\" on macOS defaults to Clang, which will fail. MPICH requires ISO_Fortran_binding.h")
endif()

string(JSON mpi_url GET ${json_meta} "mpich")

list(APPEND mpi_flags --with-device=ch3)
# MPICH 4.2.0 etc. fails to build without this option
else()
string(JSON mpi_url GET ${json_meta} "openmpi")

# this --with-hwloc breaks OpenMPI 4.1 and 5.0 at least
# list(APPEND mpi_flags --with-hwloc=internal)
# internal HWLOC avoids error in MPI:
# ibopen-pal.a(topology-linux.o): multiple definition of `hwloc_linux_component'
#--with-hwloc-libdir=${CMAKE_INSTALL_PREFIX}/lib

list(APPEND mpi_flags --disable-sphinx)
# avoids errors with docs build that aren't used anyway

find_package(ZLIB)
if(ZLIB_FOUND)
cmake_path(GET ZLIB_LIBRARIES PARENT_PATH ZLIB_LIBDIR)
list(APPEND mpi_flags --with-zlib-libdir=${ZLIB_LIBDIR})
endif()
endif()

if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
file(REAL_PATH "~" home EXPAND_TILDE)
cmake_path(GET mpi_url STEM n)
set_property(CACHE CMAKE_INSTALL_PREFIX PROPERTY VALUE "${home}/${n}")
endif()


# Downloading URL instead of Git avoids very long "autogen" step
ExternalProject_Add(MPI
URL ${mpi_url}
CONFIGURE_COMMAND <SOURCE_DIR>/configure ${mpi_flags} ${mpi_ldflags}
BUILD_COMMAND ${MAKE_EXECUTABLE} -j${Ncpu}
INSTALL_COMMAND ${MAKE_EXECUTABLE} install
TEST_COMMAND ""
USES_TERMINAL_DOWNLOAD true
USES_TERMINAL_UPDATE true
USES_TERMINAL_CONFIGURE true
USES_TERMINAL_BUILD true
USES_TERMINAL_INSTALL true
USES_TERMINAL_TEST true
CONFIGURE_HANDLED_BY_BUILD ON
)


if(mpich)
message(STATUS "MPICH: ${mpi_url} => ${CMAKE_INSTALL_PREFIX}")
else()
message(STATUS "OpenMPI: ${mpi_url} => ${CMAKE_INSTALL_PREFIX}")
endif()
message(STATUS "MPI flags: ${mpi_flags}")
message(STATUS "MPI LDFLAGS: ${mpi_ldflags}")


# --- check that MPI-3 Fortran is working
ExternalProject_add(MPItest
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/test
CMAKE_ARGS -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DMPI_ROOT:PATH=${CMAKE_INSTALL_PREFIX}
INSTALL_COMMAND ""
CONFIGURE_HANDLED_BY_BUILD on
DEPENDS MPI
)
50 changes: 50 additions & 0 deletions scripts/mpi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Build MPI

This mini-project is to build MPI-3 library OpenMPI for those systems not having MPI for the desired compiler.

```sh
cmake -Bbuild --install-prefix $HOME/my_mpi

cmake --build build
```

## MPICH

```sh
cmake -Dmpich=yes
```

builds MPICH instead of the default OpenMPI.

Note that MPICH does [not yet work with Clang](https://releases.llvm.org/11.0.0/tools/flang/docs/RuntimeDescriptor.html#interoperability-requirements)
This is particularly relevant for macOS, where Clang is the default compiler.

A successful MPICH configure step ends like:

```
config.status: executing gen_binding_f90 commands
[ /scripts/mpi/build/MPI-prefix/src/MPI/maint/gen_binding_f90.py -f-logical-size=4 -ignore-tkr=gcc ]
--> [src/binding/fortran/use_mpi/mpi_base.f90]
--> [src/binding/fortran/use_mpi/mpi_constants.f90]
--> [src/binding/fortran/use_mpi/mpi_sizeofs.f90]
config.status: executing gen_binding_f08 commands
[ /scripts/mpi/build/MPI-prefix/src/MPI/maint/gen_binding_f08.py -fint-size=4 -aint-size=8 -count-size=8 -cint-size=4 ]
--> [src/binding/fortran/use_mpi_f08/wrappers_c/f08_cdesc.c]
--> [src/binding/fortran/use_mpi_f08/wrappers_c/cdesc_proto.h]
--> [src/binding/fortran/use_mpi_f08/wrappers_f/f08ts.f90]
--> [src/binding/fortran/use_mpi_f08/wrappers_f/pf08ts.f90]
--> [src/binding/fortran/use_mpi_f08/mpi_c_interface_cdesc.f90]
--> [src/binding/fortran/use_mpi_f08/mpi_c_interface_nobuf.f90]
--> [src/binding/fortran/use_mpi_f08/mpi_f08.f90]
--> [src/binding/fortran/use_mpi_f08/pmpi_f08.f90]
--> [src/binding/fortran/use_mpi_f08/mpi_f08_types.f90]
*****************************************************
***
*** device configuration: ch3:nemesis
*** nemesis networks: tcp
***
*****************************************************
Configuration completed.
```

and the internal MPICH header confdef.h must have `#define HAVE_F08_BINDING 1`
64 changes: 64 additions & 0 deletions scripts/mpi/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
cmake_minimum_required(VERSION 3.20)

project(MPItest LANGUAGES C Fortran)

enable_testing()

include(CheckSourceCompiles)

if(NOT DEFINED MPI_ROOT AND DEFINED ENV{MPI_ROOT})
set(MPI_ROOT $ENV{MPI_ROOT})
endif()
if(MPI_ROOT)
message(STATUS "Using MPI_ROOT=${MPI_ROOT}")
else()
message(STATUS "MPI_ROOT not set, using default MPI search paths")
endif()

set(MPI_DETERMINE_LIBRARY_VERSION true)

find_package(MPI COMPONENTS C Fortran REQUIRED)

message(STATUS "${MPI_Fortran_LIBRARY_VERSION_STRING}")
message(STATUS "MPI libs: ${MPI_Fortran_LIBRARIES}")
message(STATUS "MPI include: ${MPI_Fortran_INCLUDE_DIRS}")
message(STATUS "MPI compile flags: ${MPI_Fortran_COMPILER_FLAGS}")
message(STATUS "MPI link flags: ${MPI_Fortran_LINK_FLAGS}")

if(MPI_Fortran_HAVE_F08_MODULE)
return()
endif()

set(CMAKE_REQUIRED_LIBRARIES MPI::MPI_Fortran)

# sometimes factory FindMPI.cmake doesn't define this
message(CHECK_START "Checking for Fortran MPI-3 binding")
check_source_compiles(Fortran
[=[
program test
use mpi_f08, only : mpi_comm_rank, mpi_real, mpi_comm_world, mpi_init, mpi_finalize
implicit none
call mpi_init
call mpi_finalize
end program
]=]
MPI_Fortran_HAVE_F08_MODULE
)

if(MPI_Fortran_HAVE_F08_MODULE)
message(CHECK_PASS "yes")
else()
message(CHECK_FAIL "no")
message(WARNING "MPI-3 Fortran module mpi_f08 not found, builds may fail.")
endif()

add_executable(test_mpi3 mpi3.f90)
target_link_libraries(test_mpi3 PRIVATE MPI::MPI_Fortran)

add_test(NAME test_mpi3 COMMAND test_mpi3)

if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.29)
set_property(TARGET test_mpi3 PROPERTY TEST_LAUNCHER ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_MAX_NUMPROCS})
else()
set_property(TARGET test_mpi3 PROPERTY CROSSCOMPILING_EMULATOR ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_MAX_NUMPROCS})
endif()
40 changes: 40 additions & 0 deletions scripts/mpi/test/mpi3.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
program hw_mpi
!! Each process prints out a "Hello, world!" message with a process ID
!! Original Author: John Burkardt
!! Modified: Michael Hirsch, Ph.D.

use mpi_f08, only : mpi_init, mpi_comm_size, mpi_comm_world, mpi_wtime, mpi_comm_rank, mpi_finalize
use, intrinsic:: iso_fortran_env, only: dp=>real64, compiler_version

implicit none

integer :: id, Nproc
real(dp) :: wtime

!> Initialize MPI.
call MPI_Init()

!> Get the number of processes.
call MPI_Comm_size(MPI_COMM_WORLD, Nproc)

!> Get the individual process ID.
call MPI_Comm_rank(MPI_COMM_WORLD, id)

!> Print a message.
if (id == 0) then
print *,compiler_version()
wtime = MPI_Wtime()
print *, 'number of processes: ', Nproc
end if

print *, 'Process ', id

if (id == 0) then
wtime = MPI_Wtime() - wtime
print *, 'Elapsed wall clock time = ', wtime, ' seconds.'
end if

!> Shut down MPI.
call MPI_Finalize()

end program
Loading

0 comments on commit ef157ee

Please sign in to comment.