# -*- CMake -*- master configuration file for building LAMMPS
########################################
# CMake build system
# This file is part of LAMMPS
cmake_minimum_required(VERSION 3.20)
########################################
# initialize version variables with project command
if(POLICY CMP0048)
  cmake_policy(SET CMP0048 NEW)
endif()
# set policy to silence warnings about ignoring <PackageName>_ROOT but use it
if(POLICY CMP0074)
  cmake_policy(SET CMP0074 NEW)
endif()
# set policy to silence warnings about ignoring  ${CMAKE_REQUIRED_LIBRARIES} but use it
if(POLICY CMP0075)
  cmake_policy(SET CMP0075 NEW)
endif()
# set policy to silence warnings about requiring execute permission for find_program
# we use OLD because the python-config script for the Fedora MinGW cross-compiler requires it currently
if(POLICY CMP0109)
  cmake_policy(SET CMP0109 OLD)
endif()
# set policy so set(CACHE) command does not remove any normal variable of the same name from the current scope
if(POLICY CMP0126)
  cmake_policy(SET CMP0126 NEW)
endif()
# set policy to use the time of extraction as timestamps of files unpacked from downloaded
# archives, so that updating an archive version triggers rebuilding all dependent objects
if(POLICY CMP0135)
  cmake_policy(SET CMP0135 NEW)
endif()

# set policy to ignore CMAKE_WARN_DEPRECATED and CMAKE_ERROR_DEPRECATED if available.
# we are not using those AFAIK anyway.
if(POLICY CMP0218)
  cmake_policy(SET CMP0218 NEW)
endif()

########################################
# General project settings
project(lammps
        DESCRIPTION "The LAMMPS Molecular Dynamics Simulator"
        HOMEPAGE_URL "https://www.lammps.org"
        LANGUAGES CXX C)
set(SOVERSION 0)
get_property(BUILD_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)

include(GNUInstallDirs)
get_filename_component(LAMMPS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/.. ABSOLUTE)
get_filename_component(LAMMPS_LIB_BINARY_DIR ${CMAKE_BINARY_DIR}/lib ABSOLUTE)
# collect all executables and shared libs in the top level build folder
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})

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_PACKAGING_DIR  ${LAMMPS_DIR}/cmake/packaging)
set(LAMMPS_POTENTIALS_DIR ${LAMMPS_DIR}/potentials)
set(LAMMPS_PYTHON_DIR     ${LAMMPS_DIR}/python)
set(LAMMPS_THIRDPARTY_DIR ${LAMMPS_DIR}/third_party)

set(LAMMPS_DOWNLOADS_URL "https://download.lammps.org" CACHE STRING "Base URL for LAMMPS downloads")
set(LAMMPS_POTENTIALS_URL "${LAMMPS_DOWNLOADS_URL}/potentials")
set(LAMMPS_THIRDPARTY_URL "${LAMMPS_DOWNLOADS_URL}/thirdparty")
mark_as_advanced(LAMMPS_DOWNLOADS_URL)

# 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)
  if((CMAKE_SYSTEM_NAME STREQUAL "Windows") AND (NOT CMAKE_CROSSCOMPILING))
    set(CMAKE_INSTALL_PREFIX "$ENV{USERPROFILE}/LAMMPS" CACHE PATH "Default install path" FORCE)
  else()
    set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/.local" CACHE PATH "Default install path" FORCE)
  endif()
endif()

# If enabled, no need to use LD_LIBRARY_PATH / DYLD_LIBRARY_PATH when installed
option(LAMMPS_INSTALL_RPATH "Set runtime path for shared libraries linked to LAMMPS binaries" OFF)
if(LAMMPS_INSTALL_RPATH)
  set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR})
  set(CMAKE_INSTALL_RPATH_USE_LINK_PATH ON)
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(CheckIncludeFileCXX)

# set required compiler flags and compiler/CPU arch specific optimizations
if(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
  if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
    if(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Qrestrict")
    endif()
    if(CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 17.3 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 17.4)
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /QxCOMMON-AVX512")
    else()
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /QxHost")
    endif()
  else()
    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_CXX_FLAGS "${CMAKE_CXX_FLAGS} -xCOMMON-AVX512")
    else()
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -xHost -fp-model fast=2 -no-prec-div -qoverride-limits -diag-disable=10441 -diag-disable=11074 -diag-disable=11076 -diag-disable=2196")
    endif()
  endif()
endif()

# silence excessive warnings for new Intel Compilers
if(CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fp-model precise -Wno-tautological-constant-compare -Wno-unused-command-line-argument")
endif()

# silence excessive warnings for PGI/NVHPC compilers
if((CMAKE_CXX_COMPILER_ID STREQUAL "NVHPC") OR (CMAKE_CXX_COMPILER_ID STREQUAL "PGI"))
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Minform=severe")
endif()

# silence nvcc warnings when using nvcc_wrapper
get_filename_component(LAMMPS_CXX_COMPILER_NAME "${CMAKE_CXX_COMPILER}" NAME CACHE)
if((PKG_KOKKOS) AND (Kokkos_ENABLE_CUDA) AND (LAMMPS_CXX_COMPILER_NAME STREQUAL "nvcc_wrapper"))
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xcudafe --diag_suppress=unrecognized_pragma,--diag_suppress=128,--diag_suppress=186")
endif()

# we *require* C++17 without extensions
# Kokkos also requires at least C++20 (currently)
if(NOT CMAKE_CXX_STANDARD)
# uncomment in case we plan to switch to C++20 as minimum standard
#  if(cxx_std_20 IN_LIST CMAKE_CXX_COMPILE_FEATURES)
#    set(CMAKE_CXX_STANDARD 20)
#  else()
    set(CMAKE_CXX_STANDARD 17)
#  endif()
endif()
if(CMAKE_CXX_STANDARD LESS 17)
  message(FATAL_ERROR "C++ standard must be set to at least 17")
endif()
if(PKG_KOKKOS AND (CMAKE_CXX_STANDARD LESS 20))
  set(CMAKE_CXX_STANDARD 20)
endif()
# turn off C++20 check in lmptype.h
#if(LAMMPS_CXX17)
#  add_compile_definitions(LAMMPS_CXX17)
#endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL "Use compiler extensions")
# ugly hacks for MSVC which by default always reports an old C++ standard in the __cplusplus macro
# and prints lots of pointless warnings about "unsafe" functions
if(MSVC)
  if((CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") OR (CMAKE_CXX_COMPILER_ID STREQUAL "Intel"))
    add_compile_options(/Zc:__cplusplus)
    add_compile_options(/wd4244)
    add_compile_options(/wd4267)
    add_compile_options(/wd4250)
    add_compile_options(/EHsc)
    add_compile_options(/utf-8)
  endif()
  add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
endif()

# warn about potentially problematic GCC compiler versions
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
  if (CMAKE_CXX_STANDARD GREATER_EQUAL 17)
    if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.3)
      message(WARNING "Using the GNU compilers version ${CMAKE_CXX_COMPILER_VERSION} with C++17 "
              "or later is not recommended. Please use GNU compilers version 9.3 or later")
    endif()
  endif()
endif()

# export all symbols when building a .dll file on windows
if((CMAKE_SYSTEM_NAME STREQUAL "Windows") AND BUILD_SHARED_LIBS)
  set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif()

# do not include the (obsolete) MPI C++ bindings which makes for leaner object files
# and avoids namespace conflicts. Put this early to increase its visbility.
# do not apply when compiling SCAFACOS since it uses the obsolete bindings
if (NOT PKG_SCAFACOS)
  set(MPI_CXX_SKIP_MPICXX TRUE CACHE BOOL "Skip MPI C++ Bindings" FORCE)
endif()

########################################################################
# User input options                                                   #
########################################################################
# backward compatibility with older LAMMPS documentation
if (PYTHON_EXECUTABLE)
  set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}")
endif()
# set path to python interpreter and thus enforcing python version when
# in a virtual environment and Python_EXECUTABLE is not set on command line
if(DEFINED ENV{VIRTUAL_ENV} AND NOT Python_EXECUTABLE)
  if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
    set(Python_EXECUTABLE "$ENV{VIRTUAL_ENV}/Scripts/python.exe")
  else()
    set(Python_EXECUTABLE "$ENV{VIRTUAL_ENV}/bin/python")
  endif()
  message(STATUS "Running in virtual environment: $ENV{VIRTUAL_ENV}\n"
    "   Setting Python interpreter to: ${Python_EXECUTABLE}")
endif()

find_package(Python COMPONENTS Interpreter QUIET)
# NOTE: RHEL 8.0 and Ubuntu 18.04LTS ship with Python 3.6, Python 3.8 was EOL in 2024
if(Python_VERSION VERSION_LESS 3.6)
  message(FATAL_ERROR "LAMMPS requires Python 3.6 or later")
endif()

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)
option(CMAKE_POSITION_INDEPENDENT_CODE "Create object compatible with shared libraries" ON)
option(BUILD_TOOLS "Build and install LAMMPS tools (msi2lmp, binary2txt, chain)" OFF)
option(BUILD_LAMMPS_GUI "Build and install the LAMMPS GUI" OFF)

file(GLOB ALL_SOURCES CONFIGURE_DEPENDS ${LAMMPS_SOURCE_DIR}/[^.]*.cpp)
file(GLOB MAIN_SOURCES CONFIGURE_DEPENDS ${LAMMPS_SOURCE_DIR}/main.cpp)
list(REMOVE_ITEM ALL_SOURCES ${MAIN_SOURCES})
add_library(lammps ${ALL_SOURCES})

# add extra libraries for std::filesystem with older compilers
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.1)
  target_link_libraries(lammps PRIVATE stdc++fs)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0)
  target_link_libraries(lammps PRIVATE c++fs)
endif()

# zlib is used by multiple packages and by the VTK file writer in the core, so
# we look for it once here instead of repeating the search in each package file.
find_package(ZLIB QUIET)
option(WITH_ZLIB "Enable compression support via the zlib library" ${ZLIB_FOUND})
if(WITH_ZLIB)
  find_package(ZLIB REQUIRED)
  target_compile_definitions(lammps PRIVATE -DLAMMPS_ZLIB)
  target_link_libraries(lammps PRIVATE ZLIB::ZLIB)
endif()

# tell CMake to export all symbols to a .dll on Windows with MinGW cross-compilers
if(BUILD_SHARED_LIBS AND (CMAKE_SYSTEM_NAME STREQUAL "Windows") AND CMAKE_CROSSCOMPILING)
  set_target_properties(lammps PROPERTIES LINK_FLAGS "-Wl,--export-all-symbols")
endif()

add_executable(lmp ${MAIN_SOURCES})
target_link_libraries(lmp PRIVATE lammps)
set_target_properties(lmp PROPERTIES OUTPUT_NAME ${LAMMPS_BINARY})
# re-export all symbols for plugins
if(PKG_PLUGIN AND (NOT ((CMAKE_SYSTEM_NAME STREQUAL "Windows"))))
  set_target_properties(lmp PROPERTIES ENABLE_EXPORTS TRUE)
endif()
install(TARGETS lmp EXPORT LAMMPS_Targets DESTINATION ${CMAKE_INSTALL_BINDIR})

option(CMAKE_VERBOSE_MAKEFILE "Generate verbose Makefiles" OFF)

set(STANDARD_PACKAGES
  ADIOS
  AMOEBA
  APIP
  ASPHERE
  BOCS
  BODY
  BPM
  BROWNIAN
  CG-DNA
  CG-SPICA
  CLASS2
  COLLOID
  COLVARS
  COMPRESS
  DIELECTRIC
  DIFFRACTION
  DIPOLE
  DPD-BASIC
  DPD-MESO
  DPD-REACT
  DPD-SMOOTH
  DRUDE
  EFF
  ELECTRODE
  EXTRA-COMMAND
  EXTRA-COMPUTE
  EXTRA-DUMP
  EXTRA-FIX
  EXTRA-MOLECULE
  EXTRA-PAIR
  FENIX
  FEP
  GRAPHICS
  GRANULAR
  GRANSURF
  H5MD
  INTERLAYER
  KIM
  KSPACE
  LATBOLTZ
  LEPTON
  MACHDYN
  MANIFOLD
  MANYBODY
  MBX
  MC
  MDI
  MEAM
  MESONT
  MGPT
  MISC
  ML-HDNNP
  ML-IAP
  ML-PACE
  ML-POD
  ML-QUIP
  ML-RUNNER
  ML-SNAP
  ML-UF3
  MOFFF
  MOLECULE
  MOLFILE
  NETCDF
  ORIENT
  PERI
  PHONON
  PLUGIN
  PLUMED
  PTM
  PYTHON
  QEQ
  QMMM
  QMMM-XTB
  QTB
  REACTION
  REAXFF
  REPLICA
  RHEO
  RIGID
  SCAFACOS
  SHOCK
  SMTBQ
  SPH
  SPIN
  SRD
  TALLY
  UEF
  VORONOI
  YAFF)

set(SUFFIX_PACKAGES CORESHELL GPU KOKKOS OPT INTEL OPENMP)

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}>)
target_include_directories(lammps PUBLIC $<BUILD_INTERFACE:${LAMMPS_THIRDPARTY_DIR}>)

if(PKG_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
  find_package(ADIOS2 REQUIRED)
  if(BUILD_MPI)
    if(NOT ADIOS2_HAVE_MPI)
      message(FATAL_ERROR "ADIOS2 must be built with MPI support when LAMMPS has MPI enabled")
    endif()
  else()
    if(ADIOS2_HAVE_MPI)
      message(FATAL_ERROR "ADIOS2 must be built without MPI support when LAMMPS has MPI disabled")
    endif()
  endif()
  target_link_libraries(lammps PRIVATE adios2::adios2)
endif()

if(NOT CMAKE_CROSSCOMPILING)
  find_package(MPI QUIET COMPONENTS CXX)
  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)
  else()
    find_package(MPI REQUIRED COMPONENTS 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()
  target_link_libraries(lammps PUBLIC MPI::MPI_CXX)
else()
  target_sources(lammps PRIVATE ${LAMMPS_SOURCE_DIR}/STUBS/mpi.cpp)
  add_library(mpi_stubs INTERFACE)
  target_include_directories(mpi_stubs INTERFACE $<BUILD_INTERFACE:${LAMMPS_SOURCE_DIR}/STUBS>)
  target_link_libraries(lammps PUBLIC mpi_stubs)
endif()

set(LAMMPS_SIZES "smallbig" CACHE STRING "LAMMPS integer sizes (smallbig: 64-bit #atoms #timesteps, bigbig: also 64-bit imageint, 64-bit atom ids)")
set(LAMMPS_SIZES_VALUES smallbig bigbig)
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
# with INTEL package and Intel compilers we use TBB's aligned malloc
if((CMAKE_SYSTEM_NAME STREQUAL "Windows")
   AND NOT (PKG_INTEL AND ((CMAKE_CXX_COMPILER_ID STREQUAL "Intel") OR (CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM"))))
  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()

# this hack is required to compile fmt lib with CrayClang version 15.0.2
# CrayClang is only directly recognized by CMake version 3.28 and later
if(CMAKE_VERSION VERSION_LESS 3.28)
  get_filename_component(_exe "${CMAKE_CXX_COMPILER}" NAME)
  if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND (_exe STREQUAL "crayCC"))
    target_compile_definitions(lammps PRIVATE -DFMT_STATIC_THOUSANDS_SEPARATOR)
  endif()
else()
  if(CMAKE_CXX_COMPILER_ID STREQUAL "CrayClang")
    target_compile_definitions(lammps PRIVATE -DFMT_STATIC_THOUSANDS_SEPARATOR)
  endif()
endif()

# "hard" dependencies between packages resulting
# in an error instead of skipping over files
pkg_depends(ML-IAP ML-SNAP)
pkg_depends(LATBOLTZ MPI)
pkg_depends(SCAFACOS MPI)
pkg_depends(AMOEBA KSPACE)
pkg_depends(DIELECTRIC KSPACE)
pkg_depends(DIELECTRIC EXTRA-PAIR)
pkg_depends(CG-DNA MOLECULE)
pkg_depends(CG-DNA ASPHERE)
pkg_depends(ELECTRODE KSPACE)
pkg_depends(EXTRA-MOLECULE MOLECULE)
pkg_depends(MESONT MOLECULE)
pkg_depends(ML-RUNNER KSPACE)
pkg_depends(QMMM-XTB KSPACE)
pkg_depends(RHEO BPM)
pkg_depends(APIP ML-PACE)

# detect if we may enable OpenMP support by default
set(BUILD_OMP_DEFAULT OFF)
find_package(OpenMP COMPONENTS CXX QUIET)
if(OpenMP_CXX_FOUND)
  check_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 COMPONENTS CXX REQUIRED)
  check_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_EQUAL 9.0)) OR
      (CMAKE_CXX_COMPILER_ID STREQUAL "PGI") OR (CMAKE_CXX_COMPILER_ID STREQUAL "NVHPC") OR
      (CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM") OR (CMAKE_CXX_COMPILER_ID STREQUAL "XLClang") OR
      ((CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0)) OR
      ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0)) OR
      ((CMAKE_CXX_COMPILER_ID STREQUAL "CrayClang") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0)) OR
      ((CMAKE_CXX_COMPILER_ID STREQUAL "Intel") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.0)))
    # 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.
    set(LAMMPS_OMP_COMPAT_LEVEL 4)
  else()
    set(LAMMPS_OMP_COMPAT_LEVEL 3)
  endif()
  target_compile_definitions(lammps PRIVATE -DLAMMPS_OMP_COMPAT=${LAMMPS_OMP_COMPAT_LEVEL})
  target_link_libraries(lammps PRIVATE OpenMP::OpenMP_CXX)
  target_link_libraries(lmp PRIVATE OpenMP::OpenMP_CXX)

  # Some Clang-based toolchains - most notably hipcc from ROCm - do not ship
  # omp.h in the compiler's own resource directory, so host code that includes
  # <omp.h> fails to compile even though -fopenmp is accepted (e.g. when
  # building the GPU package with GPU_API=hip and a hipcc host compiler).  If
  # the resource directory has no omp.h, look for a matching-version Clang
  # omp.h and add it as a fallback include path (with -idirafter, so it is
  # searched last and does not shadow any other headers).
  if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
    execute_process(COMMAND ${CMAKE_CXX_COMPILER} -print-resource-dir
                    OUTPUT_VARIABLE _omp_resource_dir
                    OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET
                    RESULT_VARIABLE _omp_resource_rc)
    if((_omp_resource_rc EQUAL 0) AND (NOT EXISTS "${_omp_resource_dir}/include/omp.h"))
      string(REGEX REPLACE "\\..*" "" _omp_clang_major "${CMAKE_CXX_COMPILER_VERSION}")
      find_path(LAMMPS_OMP_H_DIR NAMES omp.h
        PATHS /usr/lib/clang/${_omp_clang_major}/include
              /usr/lib64/clang/${_omp_clang_major}/include
              /usr/local/lib/clang/${_omp_clang_major}/include)
      if(LAMMPS_OMP_H_DIR)
        message(STATUS "Compiler resource directory has no omp.h: "
                "adding ${LAMMPS_OMP_H_DIR} as a fallback include path")
        target_compile_options(lammps PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-idirafter${LAMMPS_OMP_H_DIR}>)
        target_compile_options(lmp PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-idirafter${LAMMPS_OMP_H_DIR}>)
      else()
        message(WARNING "Compiler resource directory has no omp.h and no matching "
                "Clang omp.h was found; OpenMP host compilation may fail")
      endif()
    endif()
  endif()

  # this hack is required to correctly link with OpenMP support when using CrayClang version 15.0.2
  # CrayClang is only directly recognized by version 3.28 and later
  if(CMAKE_VERSION VERSION_LESS 3.28)
    get_filename_component(_exe "${CMAKE_CXX_COMPILER}" NAME)
    if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND (_exe STREQUAL "crayCC"))
      set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fopenmp")
      set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} -fopenmp")
    endif()
  else()
    if(CMAKE_CXX_COMPILER_ID STREQUAL "CrayClang")
      set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fopenmp")
      set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} -fopenmp")
    endif()
  endif()
endif()

# lower C++ standard for fmtlib sources when using Intel classic compiler
if((CMAKE_CXX_COMPILER_ID STREQUAL "Intel") AND (CMAKE_CXX_STANDARD GREATER_EQUAL 17)
    AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 2021.10))
  message(STATUS "Lowering C++ standard for compiling fmtlib sources with Intel Classic compiler")
  get_filename_component(LMP_UTILS_SRC "${LAMMPS_SOURCE_DIR}/utils.cpp" ABSOLUTE)
  get_filename_component(LMP_VARIABLE_SRC "${LAMMPS_SOURCE_DIR}/variable.cpp" ABSOLUTE)
  get_filename_component(FMT_FORMAT_SRC "${LAMMPS_SOURCE_DIR}/fmtlib_format.cpp" ABSOLUTE)
  get_filename_component(FMT_OS_SRC "${LAMMPS_SOURCE_DIR}/fmtlib_os.cpp" ABSOLUTE)
  set_source_files_properties("${FMT_FORMAT_SRC}" "${FMT_OS_SRC}" "${LMP_VARIABLE_SRC}" "${LMP_UTILS_SRC}"
          PROPERTIES COMPILE_OPTIONS "-std=c++14")
endif()

option(USE_INTERNAL_LINALG "Prefer internal library with BLAS/LAPACK subset over system BLAS/LAPACK" OFF)
if(PKG_ML-QUIP OR PKG_ML-POD OR PKG_ELECTRODE OR PKG_RHEO OR BUILD_TOOLS OR PKG_ML-RUNNER)
  if(NOT USE_INTERNAL_LINALG)
    find_package(LAPACK)
    find_package(BLAS)
  endif()
  if((NOT LAPACK_FOUND) OR (NOT BLAS_FOUND) OR USE_INTERNAL_LINALG)
    set(USE_INTERNAL_LINALG ON)
    file(GLOB LINALG_SOURCES CONFIGURE_DEPENDS ${LAMMPS_LIB_SOURCE_DIR}/linalg/[^.]*.cpp)
    add_library(linalg STATIC ${LINALG_SOURCES})
    set_target_properties(linalg PROPERTIES OUTPUT_NAME lammps_linalg${LAMMPS_MACHINE})
    if(LAMMPS_CXX_COMPILER_NAME STREQUAL "hipcc")
      target_compile_options(linalg PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:SHELL:-x c++>")
    endif()
    set(BLAS_LIBRARIES "$<TARGET_FILE:linalg>")
    set(LAPACK_LIBRARIES "$<TARGET_FILE:linalg>")
    target_link_libraries(lammps PRIVATE linalg)
  else()
    list(APPEND LAPACK_LIBRARIES ${BLAS_LIBRARIES})
  endif()
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(GZIP_FOUND OR ((CMAKE_SYSTEM_NAME STREQUAL "Windows") AND CMAKE_CROSSCOMPILING))
    target_compile_definitions(lammps PRIVATE -DLAMMPS_GZIP)
  else()
    message(FATAL_ERROR "gzip executable not found")
  endif()
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 GRAPHICS KSPACE PYTHON ML-IAP VORONOI COLVARS ML-HDNNP MDI MOLFILE NETCDF
        PLUMED QMMM QMMM-XTB ML-QUIP ML-RUNNER SCAFACOS MACHDYN KIM COMPRESS ML-PACE LEPTON FENIX EXTRA-COMMAND MBX)
  if(PKG_${PKG_WITH_INCL})
    include(Packages/${PKG_WITH_INCL})
  endif()
endforeach()

# settings for misc packages and styles
if(PKG_MISC)
  option(LAMMPS_ASYNC_IMD "Asynchronous IMD processing" OFF)
  mark_as_advanced(LAMMPS_ASYNC_IMD)
  if(LAMMPS_ASYNC_IMD)
    target_compile_definitions(lammps PRIVATE -DLAMMPS_ASYNC_IMD)
    message(STATUS "Using IMD in asynchronous mode")
  endif()
endif()

# optionally enable building script wrappers using swig
option(WITH_SWIG "Build scripting language wrappers with SWIG" OFF)
if(WITH_SWIG)
  get_filename_component(LAMMPS_SWIG_DIR ${LAMMPS_SOURCE_DIR}/../tools/swig ABSOLUTE)
  add_subdirectory(${LAMMPS_SWIG_DIR} swig)
endif()

########################################################################
# make the standard math library overrideable and autodetected (for systems that don't have it)
find_library(STANDARD_MATH_LIB m DOC "Standard Math library")
mark_as_advanced(STANDARD_MATH_LIB)
if(STANDARD_MATH_LIB)
  target_link_libraries(lammps PRIVATE ${STANDARD_MATH_LIB})
endif()

######################################
# Generate Basic Style files
######################################
include(StyleHeaderUtils)
RegisterStyles(${LAMMPS_SOURCE_DIR})

########################################################
# Fetch missing external files and archives for packages
########################################################
option(DOWNLOAD_POTENTIALS "Automatically download large potential files" ON)
mark_as_advanced(DOWNLOAD_POTENTIALS)
foreach(PKG ${STANDARD_PACKAGES} ${SUFFIX_PACKAGES})
  if(PKG_${PKG})
    FetchPotentials(${LAMMPS_SOURCE_DIR}/${PKG} ${LAMMPS_POTENTIALS_DIR})
  endif()
endforeach()

##############################################
# add sources of enabled packages
############################################
foreach(PKG ${STANDARD_PACKAGES})
  set(${PKG}_SOURCES_DIR ${LAMMPS_SOURCE_DIR}/${PKG})

  file(GLOB ${PKG}_SOURCES CONFIGURE_DEPENDS ${${PKG}_SOURCES_DIR}/[^.]*.cpp)
  file(GLOB ${PKG}_HEADERS CONFIGURE_DEPENDS ${${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()

# dedicated check for entire contents of accelerator packages
foreach(PKG ${SUFFIX_PACKAGES})
  set(${PKG}_SOURCES_DIR ${LAMMPS_SOURCE_DIR}/${PKG})

  file(GLOB ${PKG}_SOURCES CONFIGURE_DEPENDS ${${PKG}_SOURCES_DIR}/[^.]*.cpp)
  file(GLOB ${PKG}_HEADERS CONFIGURE_DEPENDS ${${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(PKG_LIB H5MD)
  if(PKG_${PKG_LIB})
    string(TOLOWER "${PKG_LIB}" PKG_LIB)
    file(GLOB_RECURSE ${PKG_LIB}_SOURCES CONFIGURE_DEPENDS
      ${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 "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_ELECTRODE OR PKG_ML-POD)
  target_link_libraries(lammps PRIVATE ${LAPACK_LIBRARIES})
endif()

if(PKG_H5MD)
  include(Packages/H5MD)
endif()

######################################################################
# packages which selectively include variants based on enabled styles
# e.g. accelerator packages
######################################################################
foreach(PKG_WITH_INCL CORESHELL DPD-BASIC DPD-SMOOTH MC MISC PHONON QEQ OPENMP KOKKOS OPT INTEL GPU)
  if(PKG_${PKG_WITH_INCL})
    include(Packages/${PKG_WITH_INCL})
  endif()
endforeach()

if(PKG_PLUGIN)
  target_compile_definitions(lammps PRIVATE -DLMP_PLUGIN)
endif()

# link with -ldl or equivalent for plugin loading; except on Windows
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
   target_link_libraries(lammps PRIVATE ${CMAKE_DL_LIBS})
endif()

######################################################################
# 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 "wsock32;psapi")
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})
GenerateStyleSources(${LAMMPS_STYLE_HEADERS_DIR})
GeneratePackageRegistry(${LAMMPS_STYLE_HEADERS_DIR})

target_include_directories(lammps PRIVATE ${LAMMPS_STYLE_HEADERS_DIR})

# add the generated style_*.cpp registration translation units to the library
get_property(LAMMPS_STYLE_SOURCES GLOBAL PROPERTY LAMMPS_STYLE_SOURCES)
target_sources(lammps PRIVATE ${LAMMPS_STYLE_SOURCES})

######################################
# 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
######################################
find_package(Git)
add_custom_target(gitversion COMMAND ${CMAKE_COMMAND}
  -D LAMMPS_DIR="${LAMMPS_DIR}"
  -D GIT_EXECUTABLE="${GIT_EXECUTABLE}"
  -D GIT_FOUND="${GIT_FOUND}"
  -D LAMMPS_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 command.h compute.h dihedral.h domain.h
  error.h exceptions.h fix.h force.h group.h improper.h input.h info.h json.h json_fwd.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 platform.h pointers.h region.h timer.h universe.h update.h utils.h variable.h)
set(LAMMPS_FMT_HEADERS core.h format.h)
set(LAMMPS_JSON_HEADERS json_fwd.hpp json.hpp)

# The ROCm hipcc wrapper compiles every .cpp in HIP language mode by default,
# which triggers an extra device-code compilation pass (e.g. --offload-arch=gfxNNN)
# and applies HIP host/device call restrictions even to the host pass.  The LAMMPS
# host library and executable contain no HIP device code, but some host-only headers
# - most notably the bundled nlohmann/json - use template metaprogramming that clang
# rejects in HIP language mode: its is_detected SFINAE disables the JSON conversion
# operators, breaking molecule.cpp, info.cpp, library.cpp, and others.  Force these
# translation units to be compiled as plain C++ so hipcc skips the HIP passes for
# them.  The GPU kernel library (lib/gpu) is built by separate targets and keeps HIP.
if(LAMMPS_CXX_COMPILER_NAME STREQUAL "hipcc")
  target_compile_options(lammps PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:SHELL:-x c++>")
  target_compile_options(lmp PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:SHELL:-x c++>")
endif()

set_target_properties(lammps PROPERTIES OUTPUT_NAME lammps${LAMMPS_MACHINE})
set_target_properties(lammps PROPERTIES SOVERSION ${SOVERSION})
set_target_properties(lammps PROPERTIES PREFIX "lib")
target_include_directories(lammps PUBLIC $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/includes/lammps ${CMAKE_CURRENT_BINARY_DIR}/includes/lammps/fmt ${CMAKE_CURRENT_BINARY_DIR}/includes/lammps/nlohmann)
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()
foreach(_HEADER ${LAMMPS_FMT_HEADERS})
  add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/includes/lammps/fmt/${_HEADER} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${LAMMPS_SOURCE_DIR}/fmt/${_HEADER} ${CMAKE_CURRENT_BINARY_DIR}/includes/lammps/fmt/${_HEADER} DEPENDS ${LAMMPS_SOURCE_DIR}/fmt/${_HEADER})
  add_custom_target(fmt_${_HEADER} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/includes/lammps/fmt/${_HEADER})
  add_dependencies(lammps fmt_${_HEADER})
  if(BUILD_SHARED_LIBS)
    install(FILES ${LAMMPS_SOURCE_DIR}/fmt/${_HEADER} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/lammps/fmt)
  endif()
endforeach()
foreach(_HEADER ${LAMMPS_JSON_HEADERS})
  add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/includes/lammps/nlohmann/${_HEADER} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${LAMMPS_SOURCE_DIR}/nlohmann/${_HEADER} ${CMAKE_CURRENT_BINARY_DIR}/includes/lammps/nlohmann/${_HEADER} DEPENDS ${LAMMPS_SOURCE_DIR}/nlohmann/${_HEADER})
  add_custom_target(json_${_HEADER} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/includes/lammps/nlohmann/${_HEADER})
  add_dependencies(lammps json_${_HEADER})
  if(BUILD_SHARED_LIBS)
    install(FILES ${LAMMPS_SOURCE_DIR}/nlohmann/${_HEADER} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/lammps/nlohmann)
  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}
          RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
  if(NOT BUILD_MPI)
    install(TARGETS mpi_stubs EXPORT LAMMPS_Targets
            LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
            ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
            RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
  endif()
  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)

include(Tools)
include(Packaging)
include(Documentation)

###############################################################################
# Install bench, potential and force field files in data directory
###############################################################################
set(LAMMPS_INSTALL_DATADIR ${CMAKE_INSTALL_DATADIR}/lammps)

install(DIRECTORY ${LAMMPS_DIR}/bench DESTINATION ${LAMMPS_INSTALL_DATADIR})
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
)

###############################################################################
# On Linux systems (any others, too?) we may need to update the shared linker cache
###############################################################################
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
  install(CODE "execute_process(COMMAND ldconfig || exit 0)")
endif()

###############################################################################
# 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)
  # backward compatibility
  find_package(Python COMPONENTS Interpreter)
  if(BUILD_IS_MULTI_CONFIG)
    set(MY_BUILD_DIR ${CMAKE_BINARY_DIR}/$<CONFIG>)
  else()
    set(MY_BUILD_DIR ${CMAKE_BINARY_DIR})
  endif()
  set(LIBLAMMPS_SHARED_BINARY ${MY_BUILD_DIR}/liblammps${LAMMPS_MACHINE}${CMAKE_SHARED_LIBRARY_SUFFIX})
  if(Python_EXECUTABLE)
    add_custom_target(
      install-python ${Python_EXECUTABLE} ${LAMMPS_PYTHON_DIR}/install.py -p ${LAMMPS_PYTHON_DIR}/lammps
      -l ${LIBLAMMPS_SHARED_BINARY} -w ${MY_BUILD_DIR} -v ${LAMMPS_SOURCE_DIR}/version.h
      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()

include(Testing)
include(CodeCoverage)
include(CodingStandard)
find_package(ClangFormat 11.0 QUIET)

if(ClangFormat_FOUND)
  add_custom_target(format-src
    COMMAND ${ClangFormat_EXECUTABLE} --verbose -i -style=file *.cpp *.h */*.cpp */*.h
    WORKING_DIRECTORY ${LAMMPS_SOURCE_DIR})
endif()

# extract Kokkos compilation settings
get_cmake_property(_allvars VARIABLES)
foreach(_var ${_allvars})
  if(${_var})
    string(REGEX MATCH "Kokkos_ENABLE_(SERIAL|THREADS|OPENMP|CUDA|HIP|SYCL|OPENMPTARGET|HPX)" _match ${_var})
    if(_match)
      string(REGEX REPLACE "Kokkos_ENABLE_(OPENMP|SERIAL|CUDA|HIP|SYCL)" "\\1" _match ${_var})
      list(APPEND KOKKOS_DEVICE ${_match})
    endif()
    string(REGEX MATCH "Kokkos_ARCH" _match ${_var})
    if(_match)
      string(REGEX REPLACE "Kokkos_ARCH_(.*)" "\\1" _match ${_var})
      list(APPEND KOKKOS_ARCH ${_match})
    endif()
  endif()
endforeach()

get_target_property(DEFINES lammps COMPILE_DEFINITIONS)
if(BUILD_IS_MULTI_CONFIG)
  set(LAMMPS_BUILD_TYPE "Multi-Config")
else()
  set(LAMMPS_BUILD_TYPE ${CMAKE_BUILD_TYPE})
endif()
include(FeatureSummary)
feature_summary(DESCRIPTION "The following tools and libraries have been found and configured:" WHAT PACKAGES_FOUND)
if(GIT_FOUND AND EXISTS ${LAMMPS_DIR}/.git)
  execute_process(COMMAND ${GIT_EXECUTABLE} describe --dirty=-modified --always
    OUTPUT_VARIABLE GIT_DESCRIBE
    ERROR_QUIET
    WORKING_DIRECTORY ${LAMMPS_DIR}
    OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
message(STATUS "<<< Build configuration >>>
   LAMMPS Version:   ${PROJECT_VERSION} ${GIT_DESCRIBE}
   Operating System: ${CMAKE_SYSTEM_NAME} ${CMAKE_LINUX_DISTRO} ${CMAKE_DISTRO_VERSION}
   CMake Version:    ${CMAKE_VERSION}
   Build type:       ${LAMMPS_BUILD_TYPE}
   Install path:     ${CMAKE_INSTALL_PREFIX}
   Generator:        ${CMAKE_GENERATOR} using ${CMAKE_MAKE_PROGRAM}")
if(CMAKE_CROSSCOMPILING)
  message(STATUS "Cross compiling on ${CMAKE_HOST_SYSTEM}")
endif()
###############################################################################
# Print package summary
###############################################################################
set(ENABLED_PACKAGES)
foreach(PKG ${STANDARD_PACKAGES} ${SUFFIX_PACKAGES})
  if(PKG_${PKG})
    list(APPEND ENABLED_PACKAGES ${PKG})
  endif()
endforeach()
if(ENABLED_PACKAGES)
  list(SORT ENABLED_PACKAGES)
else()
  set(ENABLED_PACKAGES "<None>")
endif()
message(STATUS "Enabled packages: ${ENABLED_PACKAGES}")

message(STATUS "<<< Compilers and Flags: >>>
-- C++ Compiler:     ${CMAKE_CXX_COMPILER}
      Type:          ${CMAKE_CXX_COMPILER_ID}
      Version:       ${CMAKE_CXX_COMPILER_VERSION}
      C++ Standard:  ${CMAKE_CXX_STANDARD}
      C++ Flags:    ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${BTYPE}}
      Defines:       ${DEFINES}")
if(CMAKE_CXX_COMPILER_LAUNCHER)
  message(STATUS "      Launcher:      ${CMAKE_CXX_COMPILER_LAUNCHER}")
endif()
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}}")
  if(CMAKE_C_COMPILER_LAUNCHER)
    message(STATUS "      Launcher:      ${CMAKE_C_COMPILER_LAUNCHER}")
  endif()
endif()
message(STATUS "<<< Linker flags: >>>")
message(STATUS "Executable name:  ${LAMMPS_BINARY}")
get_target_property(OPTIONS lammps LINK_OPTIONS)
if(OPTIONS)
  message(STATUS "Linker options:   ${OPTIONS}")
endif()
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 "CUDA Compiler:            ${CUDA_NVCC_EXECUTABLE}")
    message(STATUS "GPU default architecture: ${GPU_ARCH}")
    message(STATUS "GPU binning with CUDPP:   ${CUDPP_OPT}")
    message(STATUS "CUDA MPS support:         ${CUDA_MPS_SUPPORT}")
  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 Devices: ${KOKKOS_DEVICE}")
  if(KOKKOS_ARCH)
    message(STATUS "Kokkos Architecture: ${KOKKOS_ARCH}")
  endif()
endif()
if(PKG_KSPACE)
  if (FFT_USE_HEFFTE)
    message(STATUS "<<< FFT settings >>>
-- Primary FFT lib:  heFFTe")
    if (FFT_HEFFTE_BACKEND)
      message(STATUS "heFFTe backend:  ${FFT_HEFFTE_BACKEND}")
    else()
      message(STATUS "heFFTe backend:  stock (builtin FFT implementation, tested for corrected but not optimized for production)")
    endif()
    message(STATUS "Using distributed FFT algorithms from heFTTe")
    if(FFT_SINGLE)
      message(STATUS "Using single precision FFTs")
    else()
      message(STATUS "Using double precision FFTs")
    endif()
  else()
    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()
    message(STATUS "Using builtin distributed FFT algorithms")
  endif()
  if(PKG_KOKKOS)
    message(STATUS "Kokkos FFT: ${FFT_KOKKOS}")
  endif()
endif()
if(BUILD_DOC)
  message(STATUS "<<< Building HTML Manual >>>")
endif()
if(BUILD_TOOLS)
  message(STATUS "<<< Building Tools >>>")
endif()
if(BUILD_LAMMPS_GUI)
  message(STATUS "<<< Building LAMMPS-GUI >>>")
  if(LAMMPS_GUI_USE_PLUGIN)
    message(STATUS "Loading LAMMPS library as plugin at run time")
  else()
    message(STATUS "Linking LAMMPS library at compile time")
  endif()
  if(BUILD_WHAM)
    message(STATUS "<<< Building WHAM >>>")
  endif()
endif()
if(ENABLE_TESTING)
  message(STATUS "<<< Building Unit Tests >>>")
  if(ENABLE_COVERAGE)
    message(STATUS "Collecting code coverage data")
  endif()
endif()
