forked from lijiext/lammps
774 lines
31 KiB
CMake
774 lines
31 KiB
CMake
########################################
|
|
# CMake build system
|
|
# This file is part of LAMMPS
|
|
# Created by Christoph Junghans and Richard Berger
|
|
cmake_minimum_required(VERSION 3.10)
|
|
# set policy to silence warnings about ignoring <PackageName>_ROOT but use it
|
|
if(POLICY CMP0074)
|
|
cmake_policy(SET CMP0074 NEW)
|
|
endif()
|
|
########################################
|
|
|
|
project(lammps CXX)
|
|
set(SOVERSION 0)
|
|
|
|
get_filename_component(LAMMPS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/.. ABSOLUTE)
|
|
get_filename_component(LAMMPS_LIB_BINARY_DIR ${CMAKE_BINARY_DIR}/lib ABSOLUTE)
|
|
|
|
set(LAMMPS_SOURCE_DIR ${LAMMPS_DIR}/src)
|
|
set(LAMMPS_LIB_SOURCE_DIR ${LAMMPS_DIR}/lib)
|
|
set(LAMMPS_DOC_DIR ${LAMMPS_DIR}/doc)
|
|
set(LAMMPS_TOOLS_DIR ${LAMMPS_DIR}/tools)
|
|
set(LAMMPS_PYTHON_DIR ${LAMMPS_DIR}/python)
|
|
set(LAMMPS_POTENTIALS_DIR ${LAMMPS_DIR}/potentials)
|
|
|
|
find_package(Git)
|
|
|
|
# by default, install into $HOME/.local (not /usr/local), so that no root access (and sudo!!) is needed
|
|
if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
|
set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/.local" CACHE PATH "default install path" FORCE )
|
|
endif()
|
|
|
|
# Cmake modules/macros are in a subdirectory to keep this file cleaner
|
|
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Modules)
|
|
|
|
# make sure LIBRARY_PATH is set if environment variable is set
|
|
if (DEFINED ENV{LIBRARY_PATH})
|
|
list(APPEND CMAKE_LIBRARY_PATH "$ENV{LIBRARY_PATH}")
|
|
message(STATUS "Appending $ENV{LIBRARY_PATH} to CMAKE_LIBRARY_PATH: ${CMAKE_LIBRARY_PATH}")
|
|
endif()
|
|
|
|
include(LAMMPSUtils)
|
|
|
|
get_lammps_version(${LAMMPS_SOURCE_DIR}/version.h PROJECT_VERSION)
|
|
|
|
include(PreventInSourceBuilds)
|
|
|
|
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CXX_FLAGS)
|
|
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE)
|
|
endif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CXX_FLAGS)
|
|
string(TOUPPER "${CMAKE_BUILD_TYPE}" BTYPE)
|
|
|
|
# check for files auto-generated by make-based buildsystem
|
|
# this is fast, so check for it all the time
|
|
check_for_autogen_files(${LAMMPS_SOURCE_DIR})
|
|
|
|
######################################################################
|
|
# compiler tests
|
|
# these need ot be done early (before further tests).
|
|
#####################################################################
|
|
include(CheckCCompilerFlag)
|
|
include(CheckIncludeFileCXX)
|
|
|
|
# set required compiler flags and compiler/CPU arch specific optimizations
|
|
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Intel")
|
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -restrict")
|
|
if(CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 17.3 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 17.4)
|
|
set(CMAKE_TUNE_DEFAULT "-xCOMMON-AVX512")
|
|
else()
|
|
set(CMAKE_TUNE_DEFAULT "-xHost")
|
|
endif()
|
|
endif()
|
|
|
|
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
|
|
set(CMAKE_TUNE_DEFAULT "-march=native")
|
|
endif()
|
|
|
|
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
|
|
set(CMAKE_TUNE_DEFAULT "-march=native")
|
|
endif()
|
|
|
|
# we require C++11 without extensions
|
|
set(CMAKE_CXX_STANDARD 11)
|
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
|
|
|
# GNU compiler specific features for testing
|
|
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
|
|
option(ENABLE_COVERAGE "Enable collecting code coverage data" OFF)
|
|
mark_as_advanced(ENABLE_COVERAGE)
|
|
if(ENABLE_COVERAGE)
|
|
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage")
|
|
endif()
|
|
endif()
|
|
|
|
########################################################################
|
|
# User input options #
|
|
########################################################################
|
|
set(LAMMPS_MACHINE "" CACHE STRING "Suffix to append to lmp binary (WON'T enable any features automatically")
|
|
mark_as_advanced(LAMMPS_MACHINE)
|
|
if(LAMMPS_MACHINE)
|
|
set(LAMMPS_MACHINE "_${LAMMPS_MACHINE}")
|
|
endif()
|
|
set(LAMMPS_BINARY lmp${LAMMPS_MACHINE})
|
|
|
|
option(BUILD_SHARED_LIBS "Build shared library" OFF)
|
|
if(BUILD_SHARED_LIBS) # for all pkg libs, mpi_stubs and linalg
|
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
|
endif()
|
|
|
|
option(BUILD_TOOLS "Build and install LAMMPS tools (msi2lmp, binary2txt, chain)" OFF)
|
|
|
|
include(GNUInstallDirs)
|
|
file(GLOB ALL_SOURCES ${LAMMPS_SOURCE_DIR}/[^.]*.cpp)
|
|
file(GLOB MAIN_SOURCES ${LAMMPS_SOURCE_DIR}/main.cpp)
|
|
list(REMOVE_ITEM ALL_SOURCES ${MAIN_SOURCES})
|
|
add_library(lammps ${ALL_SOURCES})
|
|
add_executable(lmp ${MAIN_SOURCES})
|
|
target_link_libraries(lmp PRIVATE lammps)
|
|
set_target_properties(lmp PROPERTIES OUTPUT_NAME ${LAMMPS_BINARY})
|
|
install(TARGETS lmp EXPORT LAMMPS_Targets DESTINATION ${CMAKE_INSTALL_BINDIR})
|
|
|
|
option(CMAKE_VERBOSE_MAKEFILE "Generate verbose Makefiles" OFF)
|
|
|
|
set(STANDARD_PACKAGES ASPHERE BODY CLASS2 COLLOID COMPRESS DIPOLE
|
|
GRANULAR KSPACE LATTE MANYBODY MC MESSAGE MISC MOLECULE PERI POEMS QEQ
|
|
REPLICA RIGID SHOCK SPIN SNAP SRD KIM PYTHON MSCG MPIIO VORONOI
|
|
USER-ATC USER-AWPMD USER-BOCS USER-CGDNA USER-MESODPD USER-CGSDK USER-COLVARS
|
|
USER-DIFFRACTION USER-DPD USER-DRUDE USER-EFF USER-FEP USER-H5MD USER-LB
|
|
USER-MANIFOLD USER-MEAMC USER-MGPT USER-MISC USER-MOFFF USER-MOLFILE
|
|
USER-NETCDF USER-PHONON USER-PLUMED USER-PTM USER-QTB USER-REACTION
|
|
USER-REAXC USER-SCAFACOS USER-SDPD USER-SMD USER-SMTBQ USER-SPH USER-TALLY
|
|
USER-UEF USER-VTK USER-QUIP USER-QMMM USER-YAFF USER-ADIOS)
|
|
set(SUFFIX_PACKAGES CORESHELL USER-OMP KOKKOS OPT USER-INTEL GPU)
|
|
foreach(PKG ${STANDARD_PACKAGES} ${SUFFIX_PACKAGES})
|
|
option(PKG_${PKG} "Build ${PKG} Package" OFF)
|
|
endforeach()
|
|
|
|
######################################################
|
|
# packages with special compiler needs or external libs
|
|
######################################################
|
|
target_include_directories(lammps PUBLIC $<BUILD_INTERFACE:${LAMMPS_SOURCE_DIR}>)
|
|
|
|
if(PKG_USER-ADIOS)
|
|
# The search for ADIOS2 must come before MPI because
|
|
# it includes its own MPI search with the latest FindMPI.cmake
|
|
# script that defines the MPI::MPI_C target
|
|
enable_language(C)
|
|
find_package(ADIOS2 REQUIRED)
|
|
target_link_libraries(lammps PRIVATE adios2::adios2)
|
|
endif()
|
|
|
|
if(NOT CMAKE_CROSSCOMPILING)
|
|
set(MPI_CXX_SKIP_MPICXX TRUE)
|
|
find_package(MPI QUIET)
|
|
option(BUILD_MPI "Build MPI version" ${MPI_FOUND})
|
|
else()
|
|
option(BUILD_MPI "Build MPI version" OFF)
|
|
endif()
|
|
|
|
if(BUILD_MPI)
|
|
# We use a non-standard procedure to cross-compile with MPI on Windows
|
|
if((CMAKE_SYSTEM_NAME STREQUAL Windows) AND CMAKE_CROSSCOMPILING)
|
|
include(MPI4WIN)
|
|
target_link_libraries(lammps PUBLIC MPI::MPI_CXX)
|
|
else()
|
|
find_package(MPI REQUIRED)
|
|
target_link_libraries(lammps PUBLIC MPI::MPI_CXX)
|
|
option(LAMMPS_LONGLONG_TO_LONG "Workaround if your system or MPI version does not recognize 'long long' data types" OFF)
|
|
if(LAMMPS_LONGLONG_TO_LONG)
|
|
target_compile_definitions(lammps PRIVATE -DLAMMPS_LONGLONG_TO_LONG)
|
|
endif()
|
|
endif()
|
|
else()
|
|
enable_language(C)
|
|
file(GLOB MPI_SOURCES ${LAMMPS_SOURCE_DIR}/STUBS/mpi.c)
|
|
add_library(mpi_stubs STATIC ${MPI_SOURCES})
|
|
set_target_properties(mpi_stubs PROPERTIES OUTPUT_NAME lammps_mpi_stubs${LAMMPS_MACHINE})
|
|
target_include_directories(mpi_stubs PUBLIC $<BUILD_INTERFACE:${LAMMPS_SOURCE_DIR}/STUBS> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/lammps/mpi>)
|
|
install(FILES ${LAMMPS_SOURCE_DIR}/STUBS/mpi.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/lammps/mpi)
|
|
if(BUILD_SHARED_LIBS)
|
|
target_link_libraries(lammps PRIVATE mpi_stubs)
|
|
target_include_directories(lammps INTERFACE $<BUILD_INTERFACE:${LAMMPS_SOURCE_DIR}/STUBS> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/lammps/mpi>)
|
|
else()
|
|
target_link_libraries(lammps PUBLIC mpi_stubs)
|
|
endif()
|
|
add_library(MPI::MPI_CXX ALIAS mpi_stubs)
|
|
endif()
|
|
|
|
set(LAMMPS_SIZES "smallbig" CACHE STRING "LAMMPS integer sizes (smallsmall: all 32-bit, smallbig: 64-bit #atoms #timesteps, bigbig: also 64-bit imageint, 64-bit atom ids)")
|
|
set(LAMMPS_SIZES_VALUES smallbig bigbig smallsmall)
|
|
set_property(CACHE LAMMPS_SIZES PROPERTY STRINGS ${LAMMPS_SIZES_VALUES})
|
|
validate_option(LAMMPS_SIZES LAMMPS_SIZES_VALUES)
|
|
string(TOUPPER ${LAMMPS_SIZES} LAMMPS_SIZES)
|
|
target_compile_definitions(lammps PUBLIC -DLAMMPS_${LAMMPS_SIZES})
|
|
|
|
# posix_memalign is not available on Windows
|
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
|
set(LAMMPS_MEMALIGN "0" CACHE STRING "posix_memalign() is not available on Windows" FORCE)
|
|
else()
|
|
set(LAMMPS_MEMALIGN "64" CACHE STRING "enables the use of the posix_memalign() call instead of malloc() when large chunks or memory are allocated by LAMMPS. Set to 0 to disable")
|
|
endif()
|
|
if(NOT ${LAMMPS_MEMALIGN} STREQUAL "0")
|
|
target_compile_definitions(lammps PRIVATE -DLAMMPS_MEMALIGN=${LAMMPS_MEMALIGN})
|
|
endif()
|
|
|
|
option(LAMMPS_EXCEPTIONS "enable the use of C++ exceptions for error messages (useful for library interface)" OFF)
|
|
if(LAMMPS_EXCEPTIONS)
|
|
target_compile_definitions(lammps PUBLIC -DLAMMPS_EXCEPTIONS)
|
|
endif()
|
|
|
|
# "hard" dependencies between packages resulting
|
|
# in an error instead of skipping over files
|
|
pkg_depends(MPIIO MPI)
|
|
pkg_depends(USER-ATC MANYBODY)
|
|
pkg_depends(USER-LB MPI)
|
|
pkg_depends(USER-PHONON KSPACE)
|
|
pkg_depends(USER-SCAFACOS MPI)
|
|
|
|
# detect if we may enable OpenMP support by default
|
|
set(BUILD_OMP_DEFAULT OFF)
|
|
find_package(OpenMP QUIET)
|
|
if(OpenMP_FOUND)
|
|
check_include_file_cxx(omp.h HAVE_OMP_H_INCLUDE)
|
|
if(HAVE_OMP_H_INCLUDE)
|
|
set(BUILD_OMP_DEFAULT ON)
|
|
endif()
|
|
endif()
|
|
|
|
option(BUILD_OMP "Build with OpenMP support" ${BUILD_OMP_DEFAULT})
|
|
|
|
if(BUILD_OMP)
|
|
find_package(OpenMP REQUIRED)
|
|
check_include_file_cxx(omp.h HAVE_OMP_H_INCLUDE)
|
|
if(NOT HAVE_OMP_H_INCLUDE)
|
|
message(FATAL_ERROR "Cannot find the 'omp.h' header file required for full OpenMP support")
|
|
endif()
|
|
|
|
if (((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.99.9)) OR
|
|
((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9.99.9)) OR
|
|
((CMAKE_CXX_COMPILER_ID STREQUAL "Intel") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 18.99.9))
|
|
)
|
|
# GCC 9.x and later plus Clang 10.x and later implement strict OpenMP 4.0 semantics for consts.
|
|
# Intel 18.0 was tested to support both, so we switch to OpenMP 4+ from 19.x onward to be safe.
|
|
target_compile_definitions(lammps PRIVATE -DLAMMPS_OMP_COMPAT=4)
|
|
else()
|
|
target_compile_definitions(lammps PRIVATE -DLAMMPS_OMP_COMPAT=3)
|
|
endif()
|
|
target_link_libraries(lammps PRIVATE OpenMP::OpenMP_CXX)
|
|
endif()
|
|
|
|
if(PKG_MSCG OR PKG_USER-ATC OR PKG_USER-AWPMD OR PKG_USER-QUIP OR PKG_LATTE)
|
|
enable_language(C)
|
|
find_package(LAPACK)
|
|
find_package(BLAS)
|
|
if(NOT LAPACK_FOUND OR NOT BLAS_FOUND)
|
|
include(CheckGeneratorSupport)
|
|
if(NOT CMAKE_GENERATOR_SUPPORT_FORTRAN)
|
|
status(FATAL_ERROR "Cannot build internal linear algebra library as CMake build tool lacks Fortran support")
|
|
endif()
|
|
enable_language(Fortran)
|
|
file(GLOB LAPACK_SOURCES ${LAMMPS_LIB_SOURCE_DIR}/linalg/[^.]*.[fF])
|
|
add_library(linalg STATIC ${LAPACK_SOURCES})
|
|
set_target_properties(linalg PROPERTIES OUTPUT_NAME lammps_linalg${LAMMPS_MACHINE})
|
|
set(BLAS_LIBRARIES "$<TARGET_FILE:linalg>")
|
|
set(LAPACK_LIBRARIES "$<TARGET_FILE:linalg>")
|
|
else()
|
|
list(APPEND LAPACK_LIBRARIES ${BLAS_LIBRARIES})
|
|
endif()
|
|
endif()
|
|
|
|
|
|
find_package(JPEG QUIET)
|
|
option(WITH_JPEG "Enable JPEG support" ${JPEG_FOUND})
|
|
if(WITH_JPEG)
|
|
find_package(JPEG REQUIRED)
|
|
target_compile_definitions(lammps PRIVATE -DLAMMPS_JPEG)
|
|
if(CMAKE_VERSION VERSION_LESS 3.12)
|
|
target_include_directories(lammps PRIVATE ${JPEG_INCLUDE_DIR})
|
|
target_link_libraries(lammps PRIVATE ${JPEG_LIBRARIES})
|
|
else()
|
|
target_link_libraries(lammps PRIVATE JPEG::JPEG)
|
|
endif()
|
|
endif()
|
|
|
|
find_package(PNG QUIET)
|
|
find_package(ZLIB QUIET)
|
|
if(PNG_FOUND AND ZLIB_FOUND)
|
|
option(WITH_PNG "Enable PNG support" ON)
|
|
else()
|
|
option(WITH_PNG "Enable PNG support" OFF)
|
|
endif()
|
|
if(WITH_PNG)
|
|
find_package(PNG REQUIRED)
|
|
find_package(ZLIB REQUIRED)
|
|
target_link_libraries(lammps PRIVATE PNG::PNG ZLIB::ZLIB)
|
|
target_compile_definitions(lammps PRIVATE -DLAMMPS_PNG)
|
|
endif()
|
|
|
|
find_program(GZIP_EXECUTABLE gzip)
|
|
find_package_handle_standard_args(GZIP REQUIRED_VARS GZIP_EXECUTABLE)
|
|
option(WITH_GZIP "Enable GZIP support" ${GZIP_FOUND})
|
|
if(WITH_GZIP)
|
|
if(NOT GZIP_FOUND)
|
|
message(FATAL_ERROR "gzip executable not found")
|
|
endif()
|
|
target_compile_definitions(lammps PRIVATE -DLAMMPS_GZIP)
|
|
endif()
|
|
|
|
find_program(FFMPEG_EXECUTABLE ffmpeg)
|
|
find_package_handle_standard_args(FFMPEG REQUIRED_VARS FFMPEG_EXECUTABLE)
|
|
option(WITH_FFMPEG "Enable FFMPEG support" ${FFMPEG_FOUND})
|
|
if(WITH_FFMPEG)
|
|
if(NOT FFMPEG_FOUND)
|
|
message(FATAL_ERROR "ffmpeg executable not found")
|
|
endif()
|
|
target_compile_definitions(lammps PRIVATE -DLAMMPS_FFMPEG)
|
|
endif()
|
|
|
|
if(BUILD_SHARED_LIBS)
|
|
set(CONFIGURE_REQUEST_PIC "--with-pic")
|
|
set(CMAKE_REQUEST_PIC "-DCMAKE_POSITION_INDEPENDENT_CODE=${CMAKE_POSITION_INDEPENDENT_CODE}")
|
|
set(CUDA_REQUEST_PIC "-Xcompiler ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}")
|
|
else()
|
|
set(CONFIGURE_REQUEST_PIC)
|
|
set(CMAKE_REQUEST_PIC)
|
|
set(CUDA_REQUEST_PIC)
|
|
endif()
|
|
|
|
foreach(PKG_WITH_INCL KSPACE PYTHON VORONOI USER-COLVARS USER-MOLFILE USER-NETCDF USER-PLUMED USER-QMMM
|
|
USER-QUIP USER-SCAFACOS USER-SMD USER-VTK KIM LATTE MESSAGE MSCG COMPRESS)
|
|
if(PKG_${PKG_WITH_INCL})
|
|
include(Packages/${PKG_WITH_INCL})
|
|
endif()
|
|
endforeach()
|
|
|
|
set(CMAKE_TUNE_FLAGS "${CMAKE_TUNE_DEFAULT}" CACHE STRING "Compiler specific optimization or instrumentation")
|
|
separate_arguments(CMAKE_TUNE_FLAGS)
|
|
include(CheckCXXCompilerFlag)
|
|
foreach(_FLAG ${CMAKE_TUNE_FLAGS})
|
|
string(REGEX REPLACE "[=\"]" "" _FLAGX ${_FLAG})
|
|
check_cxx_compiler_flag("${_FLAG}" COMPILER_SUPPORTS${_FLAGX})
|
|
if(COMPILER_SUPPORTS${_FLAGX})
|
|
target_compile_options(lammps PRIVATE ${_FLAG})
|
|
else()
|
|
message(WARNING "${_FLAG} found in CMAKE_TUNE_FLAGS, but not supported by the compiler, skipping")
|
|
endif()
|
|
endforeach()
|
|
########################################################################
|
|
# Basic system tests (standard libraries, headers, functions, types) #
|
|
########################################################################
|
|
foreach(HEADER cmath)
|
|
check_include_file_cxx(${HEADER} FOUND_${HEADER})
|
|
if(NOT FOUND_${HEADER})
|
|
message(FATAL_ERROR "Could not find needed header - ${HEADER}")
|
|
endif(NOT FOUND_${HEADER})
|
|
endforeach(HEADER)
|
|
|
|
set(MATH_LIBRARIES "m" CACHE STRING "math library")
|
|
mark_as_advanced( MATH_LIBRARIES )
|
|
target_link_libraries(lammps PRIVATE ${MATH_LIBRARIES})
|
|
|
|
######################################
|
|
# Generate Basic Style files
|
|
######################################
|
|
include(StyleHeaderUtils)
|
|
RegisterStyles(${LAMMPS_SOURCE_DIR})
|
|
|
|
##############################################
|
|
# add sources of enabled packages
|
|
############################################
|
|
foreach(PKG ${STANDARD_PACKAGES})
|
|
set(${PKG}_SOURCES_DIR ${LAMMPS_SOURCE_DIR}/${PKG})
|
|
|
|
file(GLOB ${PKG}_SOURCES ${${PKG}_SOURCES_DIR}/[^.]*.cpp)
|
|
file(GLOB ${PKG}_HEADERS ${${PKG}_SOURCES_DIR}/[^.]*.h)
|
|
|
|
# check for package files in src directory due to old make system
|
|
DetectBuildSystemConflict(${LAMMPS_SOURCE_DIR} ${${PKG}_SOURCES} ${${PKG}_HEADERS})
|
|
|
|
if(PKG_${PKG})
|
|
# detects styles in package and adds them to global list
|
|
RegisterStyles(${${PKG}_SOURCES_DIR})
|
|
|
|
target_sources(lammps PRIVATE ${${PKG}_SOURCES})
|
|
target_include_directories(lammps PRIVATE ${${PKG}_SOURCES_DIR})
|
|
endif()
|
|
|
|
RegisterPackages(${${PKG}_SOURCES_DIR})
|
|
endforeach()
|
|
|
|
# packages that need defines set
|
|
foreach(PKG MPIIO)
|
|
if(PKG_${PKG})
|
|
target_compile_definitions(lammps PRIVATE -DLMP_${PKG})
|
|
endif()
|
|
endforeach()
|
|
|
|
# dedicated check for entire contents of accelerator packages
|
|
foreach(PKG ${SUFFIX_PACKAGES})
|
|
set(${PKG}_SOURCES_DIR ${LAMMPS_SOURCE_DIR}/${PKG})
|
|
|
|
file(GLOB ${PKG}_SOURCES ${${PKG}_SOURCES_DIR}/[^.]*.cpp)
|
|
file(GLOB ${PKG}_HEADERS ${${PKG}_SOURCES_DIR}/[^.]*.h)
|
|
|
|
# check for package files in src directory due to old make system
|
|
DetectBuildSystemConflict(${LAMMPS_SOURCE_DIR} ${${PKG}_SOURCES} ${${PKG}_HEADERS})
|
|
|
|
RegisterPackages(${${PKG}_SOURCES_DIR})
|
|
endforeach()
|
|
|
|
##############################################
|
|
# add lib sources of (simple) enabled packages
|
|
############################################
|
|
foreach(SIMPLE_LIB POEMS USER-ATC USER-AWPMD USER-H5MD)
|
|
if(PKG_${SIMPLE_LIB})
|
|
string(REGEX REPLACE "^USER-" "" PKG_LIB "${SIMPLE_LIB}")
|
|
string(TOLOWER "${PKG_LIB}" PKG_LIB)
|
|
file(GLOB_RECURSE ${PKG_LIB}_SOURCES
|
|
${LAMMPS_LIB_SOURCE_DIR}/${PKG_LIB}/[^.]*.c
|
|
${LAMMPS_LIB_SOURCE_DIR}/${PKG_LIB}/[^.]*.cpp)
|
|
add_library(${PKG_LIB} STATIC ${${PKG_LIB}_SOURCES})
|
|
set_target_properties(${PKG_LIB} PROPERTIES OUTPUT_NAME lammps_${PKG_LIB}${LAMMPS_MACHINE})
|
|
target_link_libraries(lammps PRIVATE ${PKG_LIB})
|
|
if(PKG_LIB STREQUAL awpmd)
|
|
target_include_directories(awpmd PUBLIC ${LAMMPS_LIB_SOURCE_DIR}/awpmd/systems/interact ${LAMMPS_LIB_SOURCE_DIR}/awpmd/ivutils/include)
|
|
elseif(PKG_LIB STREQUAL h5md)
|
|
target_include_directories(h5md PUBLIC ${LAMMPS_LIB_SOURCE_DIR}/h5md/include ${HDF5_INCLUDE_DIRS})
|
|
else()
|
|
target_include_directories(${PKG_LIB} PUBLIC ${LAMMPS_LIB_SOURCE_DIR}/${PKG_LIB})
|
|
endif()
|
|
endif()
|
|
endforeach()
|
|
|
|
if(PKG_USER-AWPMD)
|
|
target_link_libraries(awpmd PRIVATE ${LAPACK_LIBRARIES})
|
|
endif()
|
|
|
|
if(PKG_USER-ATC)
|
|
if(LAMMPS_SIZES STREQUAL BIGBIG)
|
|
message(FATAL_ERROR "The USER-ATC Package is not compatible with -DLAMMPS_BIGBIG")
|
|
endif()
|
|
target_link_libraries(atc PRIVATE ${LAPACK_LIBRARIES})
|
|
if(BUILD_MPI)
|
|
target_link_libraries(atc PRIVATE MPI::MPI_CXX)
|
|
else()
|
|
target_link_libraries(atc PRIVATE mpi_stubs)
|
|
endif()
|
|
target_include_directories(atc PRIVATE ${LAMMPS_SOURCE_DIR})
|
|
target_compile_definitions(atc PRIVATE -DLAMMPS_${LAMMPS_SIZES})
|
|
endif()
|
|
|
|
if(PKG_USER-H5MD)
|
|
include(Packages/USER-H5MD)
|
|
endif()
|
|
|
|
######################################################################
|
|
# packages which selectively include variants based on enabled styles
|
|
# e.g. accelerator packages
|
|
######################################################################
|
|
foreach(PKG_WITH_INCL CORESHELL QEQ USER-OMP USER-SDPD KOKKOS OPT USER-INTEL GPU)
|
|
if(PKG_${PKG_WITH_INCL})
|
|
include(Packages/${PKG_WITH_INCL})
|
|
endif()
|
|
endforeach()
|
|
|
|
######################################################################
|
|
# the windows version of LAMMPS requires a couple extra libraries
|
|
# and the MPI library - if use - has to be linked right before those
|
|
# and after everything else that is compiled locally
|
|
######################################################################
|
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
|
target_link_libraries(lammps PRIVATE -lwsock32 -lpsapi)
|
|
endif()
|
|
|
|
######################################################
|
|
# Generate style headers based on global list of
|
|
# styles registered during package selection
|
|
# Generate packages headers from all packages
|
|
######################################################
|
|
set(LAMMPS_STYLE_HEADERS_DIR ${CMAKE_CURRENT_BINARY_DIR}/styles)
|
|
|
|
GenerateStyleHeaders(${LAMMPS_STYLE_HEADERS_DIR})
|
|
GeneratePackagesHeaders(${LAMMPS_STYLE_HEADERS_DIR})
|
|
|
|
target_include_directories(lammps PRIVATE ${LAMMPS_STYLE_HEADERS_DIR})
|
|
|
|
######################################
|
|
# Generate lmpinstalledpkgs.h
|
|
######################################
|
|
set(temp "#ifndef LMP_INSTALLED_PKGS_H\n#define LMP_INSTALLED_PKGS_H\n")
|
|
set(temp "${temp}const char * LAMMPS_NS::LAMMPS::installed_packages[] = {\n")
|
|
set(temp_PKG_LIST ${STANDARD_PACKAGES} ${SUFFIX_PACKAGES})
|
|
list(SORT temp_PKG_LIST)
|
|
foreach(PKG ${temp_PKG_LIST})
|
|
if(PKG_${PKG})
|
|
set(temp "${temp} \"${PKG}\",\n")
|
|
endif()
|
|
endforeach()
|
|
set(temp "${temp} NULL\n};\n#endif\n\n")
|
|
message(STATUS "Generating lmpinstalledpkgs.h...")
|
|
file(WRITE "${LAMMPS_STYLE_HEADERS_DIR}/lmpinstalledpkgs.h.tmp" "${temp}" )
|
|
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${LAMMPS_STYLE_HEADERS_DIR}/lmpinstalledpkgs.h.tmp" "${LAMMPS_STYLE_HEADERS_DIR}/lmpinstalledpkgs.h")
|
|
|
|
######################################
|
|
# Generate lmpgitversion.h
|
|
######################################
|
|
add_custom_target(gitversion COMMAND ${CMAKE_COMMAND}
|
|
-DLAMMPS_DIR="${LAMMPS_DIR}"
|
|
-DGIT_EXECUTABLE="${GIT_EXECUTABLE}"
|
|
-DGIT_FOUND="${GIT_FOUND}"
|
|
-DLAMMPS_STYLE_HEADERS_DIR="${LAMMPS_STYLE_HEADERS_DIR}"
|
|
-P ${CMAKE_CURRENT_SOURCE_DIR}/Modules/generate_lmpgitversion.cmake)
|
|
set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${LAMMPS_STYLE_HEADERS_DIR}/gitversion.h)
|
|
add_dependencies(lammps gitversion)
|
|
|
|
###########################################
|
|
# Actually add executable and lib to build
|
|
############################################
|
|
get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
|
|
list (FIND LANGUAGES "Fortran" _index)
|
|
if (${_index} GREATER -1)
|
|
target_link_libraries(lammps PRIVATE ${CMAKE_Fortran_IMPLICIT_LINK_LIBRARIES})
|
|
endif()
|
|
set(LAMMPS_CXX_HEADERS angle.h atom.h bond.h citeme.h comm.h compute.h dihedral.h domain.h error.h fix.h force.h group.h improper.h
|
|
input.h info.h kspace.h lammps.h lattice.h library.h lmppython.h lmptype.h memory.h modify.h neighbor.h neigh_list.h output.h
|
|
pair.h pointers.h region.h timer.h universe.h update.h variable.h)
|
|
if(LAMMPS_EXCEPTIONS)
|
|
list(APPEND LAMMPS_CXX_HEADERS exceptions.h)
|
|
endif()
|
|
|
|
set_target_properties(lammps PROPERTIES OUTPUT_NAME lammps${LAMMPS_MACHINE})
|
|
set_target_properties(lammps PROPERTIES SOVERSION ${SOVERSION})
|
|
target_include_directories(lammps PUBLIC $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
|
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/includes/lammps)
|
|
foreach(_HEADER ${LAMMPS_CXX_HEADERS})
|
|
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/includes/lammps/${_HEADER} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${LAMMPS_SOURCE_DIR}/${_HEADER} ${CMAKE_CURRENT_BINARY_DIR}/includes/lammps/${_HEADER} DEPENDS ${LAMMPS_SOURCE_DIR}/${_HEADER})
|
|
add_custom_target(${_HEADER} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/includes/lammps/${_HEADER})
|
|
add_dependencies(lammps ${_HEADER})
|
|
if(BUILD_SHARED_LIBS)
|
|
install(FILES ${LAMMPS_SOURCE_DIR}/${_HEADER} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/lammps)
|
|
endif()
|
|
endforeach()
|
|
target_include_directories(lammps INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/includes>)
|
|
add_library(LAMMPS::lammps ALIAS lammps)
|
|
get_target_property(LAMMPS_DEFINES lammps INTERFACE_COMPILE_DEFINITIONS)
|
|
set(LAMMPS_API_DEFINES)
|
|
foreach(_DEF ${LAMMPS_DEFINES})
|
|
set(LAMMPS_API_DEFINES "${LAMMPS_API_DEFINES} -D${_DEF}")
|
|
endforeach()
|
|
if(BUILD_SHARED_LIBS)
|
|
install(TARGETS lammps EXPORT LAMMPS_Targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
|
configure_file(pkgconfig/liblammps.pc.in ${CMAKE_CURRENT_BINARY_DIR}/liblammps${LAMMPS_MACHINE}.pc @ONLY)
|
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/liblammps${LAMMPS_MACHINE}.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
|
install(EXPORT LAMMPS_Targets FILE LAMMPS_Targets.cmake NAMESPACE LAMMPS:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/LAMMPS)
|
|
include(CMakePackageConfigHelpers)
|
|
configure_file(LAMMPSConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/LAMMPSConfig.cmake @ONLY)
|
|
write_basic_package_version_file("LAMMPSConfigVersion.cmake" VERSION ${PROJECT_VERSION} COMPATIBILITY ExactVersion)
|
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/LAMMPSConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/LAMMPSConfigVersion.cmake" DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/LAMMPS)
|
|
endif()
|
|
install(FILES ${LAMMPS_DOC_DIR}/lammps.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 RENAME ${LAMMPS_BINARY}.1)
|
|
|
|
if(BUILD_TOOLS)
|
|
add_executable(binary2txt ${LAMMPS_TOOLS_DIR}/binary2txt.cpp)
|
|
target_compile_definitions(binary2txt PRIVATE -DLAMMPS_${LAMMPS_SIZES})
|
|
install(TARGETS binary2txt DESTINATION ${CMAKE_INSTALL_BINDIR})
|
|
|
|
include(CheckGeneratorSupport)
|
|
if(CMAKE_GENERATOR_SUPPORT_FORTRAN)
|
|
include(CheckLanguage)
|
|
check_language(Fortran)
|
|
if(CMAKE_Fortran_COMPILER)
|
|
enable_language(Fortran)
|
|
add_executable(chain.x ${LAMMPS_TOOLS_DIR}/chain.f)
|
|
target_link_libraries(chain.x PRIVATE ${CMAKE_Fortran_IMPLICIT_LINK_LIBRARIES})
|
|
install(TARGETS chain.x DESTINATION ${CMAKE_INSTALL_BINDIR})
|
|
else()
|
|
message(WARNING "No suitable Fortran compiler found, skipping building 'chain.x'")
|
|
endif()
|
|
else()
|
|
message(WARNING "CMake build doesn't support fortran, skipping building 'chain.x'")
|
|
endif()
|
|
|
|
enable_language(C)
|
|
get_filename_component(MSI2LMP_SOURCE_DIR ${LAMMPS_TOOLS_DIR}/msi2lmp/src ABSOLUTE)
|
|
file(GLOB MSI2LMP_SOURCES ${MSI2LMP_SOURCE_DIR}/[^.]*.c)
|
|
add_executable(msi2lmp ${MSI2LMP_SOURCES})
|
|
target_link_libraries(msi2lmp PRIVATE ${MATH_LIBRARIES})
|
|
install(TARGETS msi2lmp DESTINATION ${CMAKE_INSTALL_BINDIR})
|
|
install(FILES ${LAMMPS_DOC_DIR}/msi2lmp.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
|
endif()
|
|
|
|
include(Documentation)
|
|
|
|
###############################################################################
|
|
# Install potential and force field files in data directory
|
|
###############################################################################
|
|
set(LAMMPS_INSTALL_DATADIR ${CMAKE_INSTALL_FULL_DATADIR}/lammps)
|
|
install(DIRECTORY ${LAMMPS_POTENTIALS_DIR} DESTINATION ${LAMMPS_INSTALL_DATADIR})
|
|
if(BUILD_TOOLS)
|
|
install(DIRECTORY ${LAMMPS_TOOLS_DIR}/msi2lmp/frc_files DESTINATION ${LAMMPS_INSTALL_DATADIR})
|
|
endif()
|
|
|
|
configure_file(etc/profile.d/lammps.sh.in ${CMAKE_BINARY_DIR}/etc/profile.d/lammps.sh @ONLY)
|
|
configure_file(etc/profile.d/lammps.csh.in ${CMAKE_BINARY_DIR}/etc/profile.d/lammps.csh @ONLY)
|
|
install(
|
|
FILES ${CMAKE_BINARY_DIR}/etc/profile.d/lammps.sh
|
|
${CMAKE_BINARY_DIR}/etc/profile.d/lammps.csh
|
|
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/profile.d
|
|
)
|
|
|
|
###############################################################################
|
|
# Install LAMMPS lib and python module into site-packages folder with
|
|
# "install-python" target. Behaves exactly like "make install-python" for
|
|
# conventional build. Only available, if a shared library is built.
|
|
# This is primarily for people that only want to use the Python wrapper.
|
|
###############################################################################
|
|
if(BUILD_SHARED_LIBS)
|
|
if(CMAKE_VERSION VERSION_LESS 3.12)
|
|
find_package(PythonInterp) # Deprecated since version 3.12
|
|
if(PYTHONINTERP_FOUND)
|
|
set(Python_EXECUTABLE ${PYTHON_EXECUTABLE})
|
|
endif()
|
|
else()
|
|
find_package(Python COMPONENTS Interpreter)
|
|
endif()
|
|
if (Python_EXECUTABLE)
|
|
add_custom_target(
|
|
install-python
|
|
${Python_EXECUTABLE} install.py -v ${LAMMPS_SOURCE_DIR}/version.h
|
|
-m ${LAMMPS_PYTHON_DIR}/lammps.py
|
|
-l ${CMAKE_BINARY_DIR}/liblammps${CMAKE_SHARED_LIBRARY_SUFFIX}
|
|
WORKING_DIRECTORY ${LAMMPS_PYTHON_DIR}
|
|
COMMENT "Installing LAMMPS Python module")
|
|
else()
|
|
add_custom_target(
|
|
install-python
|
|
${CMAKE_COMMAND} -E echo "Must have Python installed to install the LAMMPS Python module")
|
|
endif()
|
|
else()
|
|
add_custom_target(
|
|
install-python
|
|
${CMAKE_COMMAND} -E echo "Must build LAMMPS as a shared library to use the Python module")
|
|
endif()
|
|
|
|
###############################################################################
|
|
# Add LAMMPS python module to "install" target. This is taylored for building
|
|
# LAMMPS for package managers and with different prefix settings.
|
|
# This requires either a shared library or that the PYTHON package is included.
|
|
###############################################################################
|
|
if(BUILD_SHARED_LIBS OR PKG_PYTHON)
|
|
if(CMAKE_VERSION VERSION_LESS 3.12)
|
|
find_package(PythonInterp) # Deprecated since version 3.12
|
|
if(PYTHONINTERP_FOUND)
|
|
set(Python_EXECUTABLE ${PYTHON_EXECUTABLE})
|
|
endif()
|
|
else()
|
|
find_package(Python COMPONENTS Interpreter)
|
|
endif()
|
|
if (Python_EXECUTABLE)
|
|
execute_process(COMMAND ${Python_EXECUTABLE}
|
|
-c "import distutils.sysconfig as cg; print(cg.get_python_lib(1,0,prefix='${CMAKE_INSTALL_PREFIX}'))"
|
|
OUTPUT_VARIABLE PYTHON_DEFAULT_INSTDIR OUTPUT_STRIP_TRAILING_WHITESPACE)
|
|
set(PYTHON_INSTDIR ${PYTHON_DEFAULT_INSTDIR} CACHE PATH "Installation folder for LAMMPS Python module")
|
|
install(FILES ${LAMMPS_PYTHON_DIR}/lammps.py DESTINATION ${PYTHON_INSTDIR})
|
|
endif()
|
|
endif()
|
|
|
|
include(Testing)
|
|
include(CodeCoverage)
|
|
|
|
###############################################################################
|
|
# Print package summary
|
|
###############################################################################
|
|
foreach(PKG ${STANDARD_PACKAGES} ${SUFFIX_PACKAGES})
|
|
if(PKG_${PKG})
|
|
message(STATUS "Building package: ${PKG}")
|
|
endif()
|
|
endforeach()
|
|
|
|
get_target_property(DEFINES lammps COMPILE_DEFINITIONS)
|
|
include(FeatureSummary)
|
|
feature_summary(DESCRIPTION "The following tools and libraries have been found and configured:" WHAT PACKAGES_FOUND)
|
|
message(STATUS "<<< Build configuration >>>
|
|
Build type: ${CMAKE_BUILD_TYPE}
|
|
Install path: ${CMAKE_INSTALL_PREFIX}
|
|
Generator: ${CMAKE_GENERATOR} using ${CMAKE_MAKE_PROGRAM}
|
|
-- <<< Compilers and Flags: >>>
|
|
-- C++ Compiler: ${CMAKE_CXX_COMPILER}
|
|
Type: ${CMAKE_CXX_COMPILER_ID}
|
|
Version: ${CMAKE_CXX_COMPILER_VERSION}
|
|
C++ Flags: ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${BTYPE}}
|
|
Defines: ${DEFINES}")
|
|
get_target_property(OPTIONS lammps COMPILE_OPTIONS)
|
|
if(OPTIONS)
|
|
message(" Options: ${OPTIONS}")
|
|
endif()
|
|
get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
|
|
list (FIND LANGUAGES "Fortran" _index)
|
|
if (${_index} GREATER -1)
|
|
message(STATUS "Fortran Compiler: ${CMAKE_Fortran_COMPILER}
|
|
Type: ${CMAKE_Fortran_COMPILER_ID}
|
|
Version: ${CMAKE_Fortran_COMPILER_VERSION}
|
|
Fortran Flags:${CMAKE_Fortran_FLAGS} ${CMAKE_Fortran_FLAGS_${BTYPE}}")
|
|
endif()
|
|
list (FIND LANGUAGES "C" _index)
|
|
if (${_index} GREATER -1)
|
|
message(STATUS "C compiler: ${CMAKE_C_COMPILER}
|
|
Type: ${CMAKE_C_COMPILER_ID}
|
|
Version: ${CMAKE_C_COMPILER_VERSION}
|
|
C Flags: ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BTYPE}}")
|
|
endif()
|
|
message(STATUS "<<< Linker flags: >>>")
|
|
message(STATUS "Executable name: ${LAMMPS_BINARY}")
|
|
if(CMAKE_EXE_LINKER_FLAGS)
|
|
message(STATUS "Executable linker flags: ${CMAKE_EXE_LINKER_FLAGS}")
|
|
endif()
|
|
if(BUILD_SHARED_LIBS)
|
|
message(STATUS "Shared library flags: ${CMAKE_SHARED_LINKER_FLAGS}")
|
|
else()
|
|
message(STATUS "Static library flags: ${CMAKE_STATIC_LINKER_FLAGS}")
|
|
endif()
|
|
if(BUILD_MPI)
|
|
message(STATUS "<<< MPI flags >>>
|
|
-- MPI_defines: ${MPI_CXX_COMPILE_DEFINITIONS}
|
|
-- MPI includes: ${MPI_CXX_INCLUDE_PATH}
|
|
-- MPI libraries: ${MPI_CXX_LIBRARIES};${MPI_Fortran_LIBRARIES}")
|
|
endif()
|
|
if(PKG_GPU)
|
|
message(STATUS "<<< GPU package settings >>>
|
|
-- GPU API: ${GPU_API}")
|
|
if(GPU_API STREQUAL "CUDA")
|
|
message(STATUS "GPU architecture: ${GPU_ARCH}")
|
|
elseif(GPU_API STREQUAL "OPENCL")
|
|
message(STATUS "OpenCL tuning: ${OCL_TUNE}")
|
|
elseif(GPU_API STREQUAL "HIP")
|
|
message(STATUS "HIP platform: ${HIP_PLATFORM}")
|
|
message(STATUS "HIP architecture: ${HIP_ARCH}")
|
|
if(HIP_USE_DEVICE_SORT)
|
|
message(STATUS "HIP GPU sorting: on")
|
|
else()
|
|
message(STATUS "HIP GPU sorting: off")
|
|
endif()
|
|
endif()
|
|
message(STATUS "GPU precision: ${GPU_PREC}")
|
|
endif()
|
|
if(PKG_KOKKOS)
|
|
message(STATUS "Kokkos Arch: ${KOKKOS_ARCH}")
|
|
endif()
|
|
if(PKG_KSPACE)
|
|
message(STATUS "<<< FFT settings >>>
|
|
-- Primary FFT lib: ${FFT}")
|
|
if(FFT_SINGLE)
|
|
message(STATUS "Using single precision FFTs")
|
|
else()
|
|
message(STATUS "Using double precision FFTs")
|
|
endif()
|
|
if(FFT_FFTW_THREADS OR FFT_MKL_THREADS)
|
|
message(STATUS "Using threaded FFTs")
|
|
else()
|
|
message(STATUS "Using non-threaded FFTs")
|
|
endif()
|
|
if(PKG_KOKKOS)
|
|
if(Kokkos_ENABLE_CUDA)
|
|
if (${FFT} STREQUAL "KISS")
|
|
message(STATUS "Kokkos FFT: KISS")
|
|
else()
|
|
message(STATUS "Kokkos FFT: cuFFT")
|
|
endif()
|
|
else()
|
|
message(STATUS "Kokkos FFT: ${FFT}")
|
|
endif()
|
|
endif()
|
|
endif()
|