diff --git a/.github/workflows/build_test_cmake.yml b/.github/workflows/build_test_cmake.yml index 7dc5133e07..9e3268f894 100644 --- a/.github/workflows/build_test_cmake.yml +++ b/.github/workflows/build_test_cmake.yml @@ -54,5 +54,6 @@ jobs: export PKG_CONFIG_PATH=${GKLIB_ROOT}/lib/pkgconfig:${METIS32_ROOT}/lib/pkgconfig:${PARMETIS32_ROOT}/lib/pkgconfig:${SUPERLU32_DIST_ROOT}/lib/pkgconfig:${PEXSI32_ROOT}/lib/pkgconfig:${PKG_CONFIG_PATH} export CPATH=${GKLIB_ROOT}/include:${METIS32_ROOT}/include:${PARMETIS32_ROOT}/include:${SUPERLU32_DIST_ROOT}/include:${PEXSI32_ROOT}/include:${CPATH} export CMAKE_PREFIX_PATH=${PEXSI32_ROOT}:${SUPERLU_DIST32_ROOT}:${PARMETIS32_ROOT}:${METIS32_ROOT}:${GKLIB_ROOT}:${CMAKE_PREFIX_PATH} + rm -rf build cmake -B build ${{ matrix.build_args }} cmake --build build -j2 diff --git a/.github/workflows/build_test_makefile.yml b/.github/workflows/build_test_makefile.yml index 237d25ebf8..8ab90ed4d4 100644 --- a/.github/workflows/build_test_makefile.yml +++ b/.github/workflows/build_test_makefile.yml @@ -20,6 +20,8 @@ jobs: - name: Build run: | export I_MPI_CXX=icpx + chmod a+x generate_build_info.sh cd source - make -j2 ${{ matrix.build_args }} + mkdir build && cd build + make -f ../Makefile -j2 ${{ matrix.build_args }} diff --git a/.github/workflows/cuda.yml b/.github/workflows/cuda.yml index 14e829e546..89bca0c8aa 100644 --- a/.github/workflows/cuda.yml +++ b/.github/workflows/cuda.yml @@ -33,6 +33,7 @@ jobs: - name: Build run: | nvidia-smi + rm -rf build cmake -B build -DUSE_CUDA=ON -DBUILD_TESTING=ON cmake --build build -j4 cmake --install build diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index df1dfb29dc..b836b8d079 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -55,135 +55,135 @@ jobs: cmake --build build -j8 cmake --install build - - name: Integrated Tests Preparation +- name: Integrated Tests Preparation env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R integrated_test" - + ctest --test-dir build -V --timeout 1700 -R integrated_test + - name: Module_Base Unittests env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R MODULE_BASE" - + ctest --test-dir build -V --timeout 1700 -R MODULE_BASE + - name: Module_IO Unittests env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R MODULE_IO" - + ctest --test-dir build -V --timeout 1700 -R MODULE_IO + - name: Module_HSolver Unittests env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R MODULE_HSOLVER" - + ctest --test-dir build -V --timeout 1700 -R MODULE_HSOLVER + - name: Module_Cell Unittests env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R MODULE_CELL" - + ctest --test-dir build -V --timeout 1700 -R MODULE_CELL + - name: Module_MD Unittests env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R MODULE_MD" - + ctest --test-dir build -V --timeout 1700 -R MODULE_MD + - name: source_psi Unittests env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R source_psi" - + ctest --test-dir build -V --timeout 1700 -R source_psi + - name: Module_RI Unittests env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R MODULE_RI" - + ctest --test-dir build -V --timeout 1700 -R MODULE_RI + - name: 01_PW Test env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R 01_PW" - + ctest --test-dir build -V --timeout 1700 -R 01_PW + - name: 02_NAO_Gamma Test env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R 02_NAO_Gamma" - + ctest --test-dir build -V --timeout 1700 -R 02_NAO_Gamma + - name: 03_NAO_multik Test env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R 03_NAO_multik" - + ctest --test-dir build -V --timeout 1700 -R 03_NAO_multik + - name: 04_FF Test env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R 04_FF" - + ctest --test-dir build -V --timeout 1700 -R 04_FF + - name: 05_rtTDDFT Test env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R 05_rtTDDFT" - + ctest --test-dir build -V --timeout 1700 -R 05_rtTDDFT + - name: 06_SDFT Test env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R 06_SDFT" - + ctest --test-dir build -V --timeout 1700 -R 06_SDFT + - name: 07_OFDFT Test env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R 07_OFDFT" - + ctest --test-dir build -V --timeout 1700 -R 07_OFDFT + - name: 08_EXX Test env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R 08_EXX" - + ctest --test-dir build -V --timeout 1700 -R 08_EXX + - name: 09_DeePKS Test env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R 09_DeePKS" - + ctest --test-dir build -V --timeout 1700 -R 09_DeePKS + - name: 10_others Test env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -R 10_others" - + ctest --test-dir build -V --timeout 1700 -R 10_others + - name: Other Unittests env: GTEST_COLOR: 'yes' OMP_NUM_THREADS: '2' run: | - cmake --build build --target test ARGS="-V --timeout 1700 -E 'integrate_test|01_PW|02_NAO_Gamma|03_NAO_multik|04_FF|05_rtTDDFT|06_SDFT|07_OFDFT|08_EXX|09_DeePKS|10_others|11_PW_GPU|12_NAO_Gamma_GPU|13_NAO_multik_GPU|15_rtTDDFT_GPU|16_SDFT_GPU|MODULE_BASE|MODULE_IO|MODULE_HSOLVER|MODULE_CELL|MODULE_MD|source_psi|MODULE_RI'" + ctest --test-dir build -V --timeout 1700 -E 'integrate_test|01_PW|02_NAO_Gamma|03_NAO_multik|04_FF|05_rtTDDFT|06_SDFT|07_OFDFT|08_EXX|09_DeePKS|10_others|11_PW_GPU|12_NAO_Gamma_GPU|13_NAO_multik_GPU|15_rtTDDFT_GPU|16_SDFT_GPU|MODULE_BASE|MODULE_IO|MODULE_HSOLVER|MODULE_CELL|MODULE_MD|source_psi|MODULE_RI' diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b28936399..8358d47aff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,8 +74,8 @@ if(ENABLE_RAPIDJSON) OFF CACHE INTERNAL "") FetchContent_MakeAvailable(rapidjson) - set(RapidJSON_INCLUDE_PATH "${rapidjson_SOURCE_DIR}/include") endif() + set(RapidJSON_INCLUDE_PATH "${rapidjson_SOURCE_DIR}/include") add_compile_definitions(__RAPIDJSON) add_definitions(-DRAPIDJSON_HAS_CXX11_NOEXCEPT=0) include_directories(${RapidJSON_INCLUDE_PATH}) @@ -182,6 +182,7 @@ if(ENABLE_COVERAGE) add_coverage(${ABACUS_BIN_NAME}) endif() + macro(set_if_higher VARIABLE VALUE) if(${VARIABLE} LESS ${VALUE}) set(${VARIABLE} ${VALUE}) @@ -673,73 +674,14 @@ if(INFO) # modifications on blas_connector and lapack_connector endif() -# Add performance test in abacus -if(ENABLE_GOOGLEBENCH) - set(BUILD_TESTING ON) - find_package(benchmark HINTS ${BENCHMARK_DIR}) - if(NOT ${benchmark_FOUND}) - set(BENCHMARK_USE_BUNDLED_GTEST OFF) - include(FetchContent) - FetchContent_Declare( - benchmark - GIT_REPOSITORY https://github.com/google/benchmark.git - GIT_TAG "origin/main" - GIT_SHALLOW TRUE - GIT_PROGRESS TRUE) - set(BENCHMARK_ENABLE_TESTING OFF) - FetchContent_MakeAvailable(benchmark) - endif() -endif() - -if(BUILD_TESTING) - set_if_higher(CMAKE_CXX_STANDARD 14) # Required in orbital - include(CTest) - enable_testing() - find_package(GTest HINTS /usr/local/lib/ ${GTEST_DIR}) - if(NOT ${GTest_FOUND}) - include(FetchContent) - FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG "origin/main" - GIT_SHALLOW TRUE - GIT_PROGRESS TRUE) - FetchContent_MakeAvailable(googletest) - endif() - # TODO: Try the GoogleTest module. - # https://cmake.org/cmake/help/latest/module/GoogleTest.html - add_subdirectory(tests) # Contains integration tests - - function(AddTest) # function for UT - cmake_parse_arguments(UT "DYN" "TARGET" - "LIBS;DYN_LIBS;STATIC_LIBS;SOURCES;DEPENDS" ${ARGN}) - add_executable(${UT_TARGET} ${UT_SOURCES}) - - if(ENABLE_COVERAGE) - add_coverage(${UT_TARGET}) - endif() - - # dependencies & link library - target_link_libraries(${UT_TARGET} ${UT_LIBS} Threads::Threads - GTest::gtest_main GTest::gmock_main) - if(ENABLE_GOOGLEBENCH) - target_link_libraries( - ${UT_TARGET} benchmark::benchmark) - endif() - - if(USE_OPENMP) - target_link_libraries(${UT_TARGET} OpenMP::OpenMP_CXX) - endif() - install(TARGETS ${UT_TARGET} DESTINATION ${CMAKE_BINARY_DIR}/tests) - add_test( - NAME ${UT_TARGET} - COMMAND ${UT_TARGET} - WORKING_DIRECTORY $) - endfunction(AddTest) -endif() +include(cmake/Testing.cmake) +setup_testing() add_subdirectory(source) +include(cmake/BuildInfo.cmake) +setup_build_info() + target_link_libraries( ${ABACUS_BIN_NAME} base diff --git a/cmake/BuildInfo.cmake b/cmake/BuildInfo.cmake new file mode 100644 index 0000000000..94a267e9c6 --- /dev/null +++ b/cmake/BuildInfo.cmake @@ -0,0 +1,28 @@ +# ============================================================================= +# Generate Build Information Header (Including Information Collection) +# ============================================================================== + +# include_guard(GLOBAL) + +function(setup_build_info) + message(STATUS "Setting up build information...") + + include(cmake/CollectBuildInfoVars.cmake) + +set(BUILD_INFO_TEMPLATE "${CMAKE_SOURCE_DIR}/source/source_io/build_info.h.in") +set(BUILD_INFO_OUTPUT "${CMAKE_BINARY_DIR}/source/source_io/build_info.h") + +configure_file( + ${BUILD_INFO_TEMPLATE} + ${BUILD_INFO_OUTPUT} + @ONLY +) + + # add_library(BuildInfo::Headers INTERFACE IMPORTED GLOBAL) + # target_include_directories(BuildInfo::Headers + # INTERFACE + # ${CMAKE_BINARY_DIR}/source/source_io + # ) + + message(STATUS "Build info header configured: ${BUILD_INFO_OUTPUT}") +endfunction() diff --git a/cmake/CollectBuildInfoVars.cmake b/cmake/CollectBuildInfoVars.cmake new file mode 100644 index 0000000000..5a76bfdbf8 --- /dev/null +++ b/cmake/CollectBuildInfoVars.cmake @@ -0,0 +1,373 @@ +# ============================================================================= +# This script is included to set all build information variables in the +# calling scope. It does not use a function to avoid variable scope issues. +# ============================================================================== + +# --- 1. Collect Basic Build Information --- +if(NOT CMAKE_BUILD_TYPE) + set(ABACUS_BUILD_TYPE "Custom (no CMAKE_BUILD_TYPE)") +else() + set(ABACUS_BUILD_TYPE ${CMAKE_BUILD_TYPE}) +endif() +if(DEFINED ENV{USER}) + set(ABACUS_BUILD_USER "$ENV{USER}") +else() + set(ABACUS_BUILD_USER "Unknown") +endif() +site_name(ABACUS_BUILD_HOST) +set(ABACUS_CXX_COMPILER_ID ${CMAKE_CXX_COMPILER_ID}) +set(ABACUS_CXX_COMPILER_PATH ${CMAKE_CXX_COMPILER}) +set(ABACUS_CXX_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION}) +set(TEMP_CXX_FLAGS_LIST "${CMAKE_CXX_FLAGS}") +#if(DEFINED CMAKE_BUILD_TYPE) +# string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UPPER) +# list(APPEND TEMP_CXX_FLAGS_LIST "${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}") +#endif() +string(REPLACE ";" " " ABACUS_CXX_FLAGS "${TEMP_CXX_FLAGS_LIST}") +set(TEMP_LINKER_FLAGS_LIST "${CMAKE_EXE_LINKER_FLAGS}") +#if(DEFINED CMAKE_BUILD_TYPE) +# string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UPPER) +# list(APPEND TEMP_LINKER_FLAGS_LIST "${CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}") +#endif() +string(REPLACE ";" " " ABACUS_LINKER_FLAGS "${TEMP_LINKER_FLAGS_LIST}") +set(TEMP_CUDA_FLAGS_LIST "${CMAKE_EXE_CUDA_FLAGS}") +string(REPLACE ";" " " ABACUS_CUDA_FLAGS "${TEMP_CUDA_FLAGS_LIST}") + +# --- 2. Determine Target Platform --- +if(USE_DSP) + set(ABACUS_PLATFORM_NAME "DSP") +elseif(USE_SW) + set(ABACUS_PLATFORM_NAME "CPU + SW-MATH") +elseif(USE_CUDA_ON_DCU) + set(ABACUS_PLATFORM_NAME "CPU + DCU (Hygon CUDA)") +elseif(USE_CUDA) + set(ABACUS_PLATFORM_NAME "CPU + NVIDIA CUDA") +elseif(USE_ROCM) + set(ABACUS_PLATFORM_NAME "CPU + AMD ROCm") +else() + set(ABACUS_PLATFORM_NAME "CPU") +endif() + +# --- 3. Sanitizers & Debugging --- +if(ENABLE_ASAN) + set(ABACUS_ASAN_STATUS "yes") +else() + set(ABACUS_ASAN_STATUS "no") +endif() +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(ABACUS_DEBUG_SYMBOLS "yes") +else() + set(ABACUS_DEBUG_SYMBOLS "no") +endif() + +# --- 4. Get Dependency Versions (Enhanced with path fallback) --- +# MPI Implementation and Version +set(ABACUS_MPI_IMPLEMENTATION "no") +set(ABACUS_MPI_VERSION "no") +set(ABACUS_CUDA_AWARE_MPI "no") +if(ENABLE_MPI) + execute_process(COMMAND ${MPI_CXX_COMPILER} --version OUTPUT_VARIABLE MPI_VERSION_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) + if(MPI_VERSION_OUTPUT MATCHES "Intel") + set(ABACUS_MPI_IMPLEMENTATION "Intel MPI") + string(REGEX MATCH "Intel\\(R\\) oneAPI DPC\\+\\+/C\\+\\+ Compiler ([0-9]+\\.[0-9]+\\.[0-9]+)" _ "${MPI_VERSION_OUTPUT}") + if(CMAKE_MATCH_1) + set(MPI_DETECTED_VERSION "${CMAKE_MATCH_1}") + else() + string(REGEX MATCH "icpc \\(ICC\\) ([0-9]+\\.[0-9]+\\.[0-9]+)" _ "${MPI_VERSION_OUTPUT}") + if(CMAKE_MATCH_1) + set(MPI_DETECTED_VERSION "${CMAKE_MATCH_1}") +endif() + endif() + elseif(MPI_VERSION_OUTPUT MATCHES "Open MPI") + set(ABACUS_MPI_IMPLEMENTATION "OpenMPI") + elseif(MPI_VERSION_OUTPUT MATCHES "MPICH") + set(ABACUS_MPI_IMPLEMENTATION "MPICH") + else() + set(ABACUS_MPI_IMPLEMENTATION "Unknown") +endif() + if(MPI_DETECTED_VERSION) + set(ABACUS_MPI_VERSION "yes (v${MPI_DETECTED_VERSION})") + elseif(MPI_VERSION) + set(ABACUS_MPI_VERSION "yes (v${MPI_VERSION})") + else() + set(ABACUS_MPI_VERSION "yes (version unknown)") +endif() + if(USE_CUDA) + execute_process(COMMAND ${MPI_CXX_COMPILER} -showme:compile OUTPUT_VARIABLE MPI_COMPILE_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) + if(MPI_COMPILE_FLAGS MATCHES "cuda" OR MPI_COMPILE_FLAGS MATCHES "CUDA") + set(ABACUS_CUDA_AWARE_MPI "yes (likely)") + else() + set(ABACUS_CUDA_AWARE_MPI "no (or undetectable)") +endif() + else() + set(ABACUS_CUDA_AWARE_MPI "no (CUDA not enabled)") +endif() +endif() + +# OpenMP Version +if(USE_OPENMP AND OpenMP_CXX_VERSION) + set(ABACUS_OPENMP_VERSION "yes (v${OpenMP_CXX_VERSION})") +elseif(USE_OPENMP) + set(ABACUS_OPENMP_VERSION "yes (version unknown)") +else() + set(ABACUS_OPENMP_VERSION "no") +endif() + +# Core Math Libraries +if(ENABLE_LCAO AND USE_ELPA) + set(ABACUS_ELPA_VERSION "yes (version unknown)") + if(ELPA_VERSION) + set(ABACUS_ELPA_VERSION "yes (v${ELPA_VERSION})") + else() + find_program(ELPA_VERSION_EXE elpa2_print_version PATHS ${ELPA_DIR}/bin NO_DEFAULT_PATH) + if(ELPA_VERSION_EXE) + execute_process(COMMAND ${ELPA_VERSION_EXE} OUTPUT_VARIABLE ELPA_VER_RAW OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) + if(ELPA_VER_RAW) + set(ABACUS_ELPA_VERSION "yes (${ELPA_VER_RAW})") +endif() + endif() + endif() + if(ABACUS_ELPA_VERSION STREQUAL "yes (version unknown)" AND ELPA_DIR) + set(ABACUS_ELPA_VERSION "yes (path: ${ELPA_DIR})") + endif() +else() + set(ABACUS_ELPA_VERSION "no") +endif() + +if(MKLROOT) + set(ABACUS_MKL_SUPPORT "yes (version unknown)") + find_path(MKL_VERSION_HEADER mkl_version.h PATHS ${MKL_INCLUDE} NO_DEFAULT_PATH) + if(MKL_VERSION_HEADER) + file(STRINGS "${MKL_VERSION_HEADER}" MKL_VERSION_LINE REGEX "^#define INTEL_MKL_VERSION [0-9]+") + string(REGEX REPLACE "^#define INTEL_MKL_VERSION ([0-9]+)" "\\1" MKL_VER_NUM "${MKL_VERSION_LINE}") + if(MKL_VER_NUM) + math(EXPR MKL_MAJOR "${MKL_VER_NUM} / 10000") + math(EXPR MKL_MINOR "(${MKL_VER_NUM} % 10000) / 100") + math(EXPR MKL_PATCH "${MKL_VER_NUM} % 100") + set(ABACUS_MKL_SUPPORT "yes (v${MKL_MAJOR}.${MKL_MINOR}.${MKL_PATCH})") + endif() + endif() +else() + set(ABACUS_MKL_SUPPORT "no") +endif() + +if(ENABLE_LIBXC AND Libxc_VERSION) + set(ABACUS_LIBXC_VERSION "yes (v${Libxc_VERSION})") +elseif(ENABLE_LIBXC) + set(ABACUS_LIBXC_VERSION "yes (path: ${Libxc_DIR})") +else() + set(ABACUS_LIBXC_VERSION "no") +endif() + +if(NOT USE_SW AND NOT MKLROOT AND FFTW3_VERSION) + set(ABACUS_FFTW_VERSION "yes (v${FFTW3_VERSION})") +elseif(NOT USE_SW AND NOT MKLROOT) + set(ABACUS_FFTW_VERSION "yes (version unknown)") +else() + set(ABACUS_FFTW_VERSION "no (using MKL or SW)") +endif() + +# Cereal Version (Enhanced) +if(ENABLE_LCAO) + set(ABACUS_CEREAL_VERSION "yes (version unknown)") + find_path(CEREAL_INCLUDE_DIR cereal/version.hpp PATHS ${CEREAL_INCLUDE_DIR}) + if(EXISTS CEREAL_INCLUDE_DIR) + file(STRINGS "${CEREAL_INCLUDE_DIR}/cereal/version.hpp" CEREAL_MAJOR_LINE REGEX "^#define CEREAL_VERSION_MAJOR") + file(STRINGS "${CEREAL_INCLUDE_DIR}/cereal/version.hpp" CEREAL_MINOR_LINE REGEX "^#define CEREAL_VERSION_MINOR") + file(STRINGS "${CEREAL_INCLUDE_DIR}/cereal/version.hpp" CEREAL_PATCH_LINE REGEX "^#define CEREAL_VERSION_PATCH") + string(REGEX REPLACE "^#define CEREAL_VERSION_MAJOR +([0-9]+).*" "\\1" CEREAL_MAJOR "${CEREAL_MAJOR_LINE}") + string(REGEX REPLACE "^#define CEREAL_VERSION_MINOR +([0-9]+).*" "\\1" CEREAL_MINOR "${CEREAL_MINOR_LINE}") + string(REGEX REPLACE "^#define CEREAL_VERSION_PATCH +([0-9]+).*" "\\1" CEREAL_PATCH "${CEREAL_PATCH_LINE}") + if(CEREAL_MAJOR AND CEREAL_MINOR AND CEREAL_PATCH) + set(ABACUS_CEREAL_VERSION "yes (v${CEREAL_MAJOR}.${CEREAL_MINOR}.${CEREAL_PATCH})") + endif() + endif() + if(ABACUS_CEREAL_VERSION STREQUAL "yes (version unknown)" AND CEREAL_INCLUDE_DIR) + set(ABACUS_CEREAL_VERSION "yes (path: ${CEREAL_INCLUDE_DIR})") + endif() +else() + set(ABACUS_CEREAL_VERSION "no") +endif() + +# Accelerators +if(USE_CUDA AND CUDAToolkit_VERSION) + set(ABACUS_CUDA_VERSION "yes (v${CUDAToolkit_VERSION})") +elseif(USE_CUDA) + set(ABACUS_CUDA_VERSION "yes (version unknown)") +else() + set(ABACUS_CUDA_VERSION "no") +endif() + +if(USE_ROCM) + set(ABACUS_ROCM_VERSION "yes (version unknown)") + if(HIP_VERSION) + set(ABACUS_ROCM_VERSION "yes (v${HIP_VERSION})") + else() + find_program(HIPCC_EXECUTABLE hipcc) + if(HIPCC_EXECUTABLE) + execute_process(COMMAND ${HIPCC_EXECUTABLE} --version OUTPUT_VARIABLE HIPCC_VERSION_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) + string(REGEX MATCH "HIP version: ([0-9]+\\.[0-9]+\\.[0-9]+)" _ "${HIPCC_VERSION_OUTPUT}") + if(CMAKE_MATCH_1) + set(ABACUS_ROCM_VERSION "yes (v${CMAKE_MATCH_1})") +endif() + endif() + endif() + if(ABACUS_ROCM_VERSION STREQUAL "yes (version unknown)" AND ROCM_PATH) + set(ABACUS_ROCM_VERSION "yes (path: ${ROCM_PATH})") + endif() +else() + set(ABACUS_ROCM_VERSION "no") +endif() + +if(DEFINED CAL_CUSOLVERMP_PATH AND ENABLE_CUSOLVERMP) + set(ABACUS_CUSOLVERMP_VERSION "yes (path: ${CAL_CUSOLVERMP_PATH})") +elseif(ENABLE_CUSOLVERMP) + set(ABACUS_CUSOLVERMP_VERSION "yes (version unknown)") +else() + set(ABACUS_CUSOLVERMP_VERSION "no") +endif() + +# EXX Libraries +if(ENABLE_LIBRI) + set(ABACUS_LIBRI_VERSION "yes (version unknown)") + set(LIBRI_VERSION_HEADER "${LIBRI_DIR}/include/RI/version.h") + if(EXISTS ${LIBRI_VERSION_HEADER}) + file(STRINGS "${LIBRI_VERSION_HEADER}" LIBRI_MAJOR_LINE REGEX "__LIBRI_VERSION_MAJOR") + file(STRINGS "${LIBRI_VERSION_HEADER}" LIBRI_MINOR_LINE REGEX "__LIBRI_VERSION_MINOR") + file(STRINGS "${LIBRI_VERSION_HEADER}" LIBRI_PATCH_LINE REGEX "__LIBRI_VERSION_PATCH") + string(REGEX MATCH "([0-9]+)" LIBRI_MAJOR "${LIBRI_MAJOR_LINE}") + string(REGEX MATCH "([0-9]+)" LIBRI_MINOR "${LIBRI_MINOR_LINE}") + string(REGEX MATCH "([0-9]+)" LIBRI_PATCH "${LIBRI_PATCH_LINE}") + if(LIBRI_MAJOR AND LIBRI_MINOR AND LIBRI_PATCH) + set(ABACUS_LIBRI_VERSION "yes (v${LIBRI_MAJOR}.${LIBRI_MINOR}.${LIBRI_PATCH})") + endif() + endif() + if(ABACUS_LIBRI_VERSION STREQUAL "yes (version unknown)" AND LIBRI_DIR) + set(ABACUS_LIBRI_VERSION "yes (path: ${LIBRI_DIR})") + endif() +else() + set(ABACUS_LIBRI_VERSION "no") +endif() + +if(ENABLE_LIBCOMM AND LIBCOMM_DIR) + set(ABACUS_LIBCOMM_VERSION "yes (path: ${LIBCOMM_DIR})") +elseif(ENABLE_LIBCOMM) + set(ABACUS_LIBCOMM_VERSION "yes (version unknown)") +else() + set(ABACUS_LIBCOMM_VERSION "no") +endif() + +# ML & AI Libraries +if((DEFINED Torch_DIR OR ENABLE_MLALGO) AND Torch_VERSION) + set(ABACUS_LIBTORCH_VERSION "yes (v${Torch_VERSION})") +elseif(DEFINED Torch_DIR OR ENABLE_MLALGO) + set(ABACUS_LIBTORCH_VERSION "yes (path: ${Torch_DIR})") +else() + set(ABACUS_LIBTORCH_VERSION "no") +endif() + +if(DEFINED DeePMD_DIR) + set(ABACUS_DEEPMD_VERSION "yes (version unknown)") + set(DEEPMD_VERSION_HEADER "${DeePMD_DIR}/include/deepmd/version.h") + if(EXISTS ${DEEPMD_VERSION_HEADER}) + file(STRINGS "${DEEPMD_VERSION_HEADER}" DEEPMD_VERSION_LINE REGEX "global_install_prefix") + if(DEEPMD_VERSION_LINE) + string(REGEX MATCH "global_install_prefix=\".*deepmd-kit-([0-9]+\\.[0-9]+\\.[0-9]+)\"" _ "${DEEPMD_VERSION_LINE}") + if(CMAKE_MATCH_1) + set(ABACUS_DEEPMD_VERSION "yes (v${CMAKE_MATCH_1})") +endif() + endif() + endif() + if(ABACUS_DEEPMD_VERSION STREQUAL "yes (version unknown)") + set(ABACUS_DEEPMD_VERSION "yes (path: ${DeePMD_DIR})") + endif() +else() + set(ABACUS_DEEPMD_VERSION "no") +endif() + +if(DEFINED NEP_DIR AND NEP_VERSION) + set(ABACUS_NEP_VERSION "yes (v${NEP_VERSION})") +elseif(DEFINED NEP_DIR) + set(ABACUS_NEP_VERSION "yes (path: ${NEP_DIR})") +else() + set(ABACUS_NEP_VERSION "no") +endif() + +if(DEFINED TensorFlow_DIR) + set(ABACUS_TENSORFLOW_VERSION "yes (path: ${TensorFlow_DIR})") +else() + set(ABACUS_TENSORFLOW_VERSION "no") +endif() + +# Testing & Other Libraries +if(BUILD_TESTING) + set(ABACUS_GTEST_VERSION "yes (from git origin/main)") +else() + set(ABACUS_GTEST_VERSION "no") +endif() + +if(ENABLE_GOOGLEBENCH) + set(ABACUS_GOOGLEBENCH_VERSION "yes (from git origin/main)") +else() + set(ABACUS_GOOGLEBENCH_VERSION "no") +endif() + +if(DEFINED RapidJSON_DIR AND ENABLE_RAPIDJSON) + set(ABACUS_RAPIDJSON_VERSION "yes (path: ${RapidJSON_DIR})") +elseif(ENABLE_RAPIDJSON) + set(ABACUS_RAPIDJSON_VERSION "yes (from git origin/master)") +else() + set(ABACUS_RAPIDJSON_VERSION "no") +endif() + +if(ENABLE_MLALGO) + set(ABACUS_LIBNPY_VERSION "yes (path: ${libnpy_SOURCE_DIR})") +else() + set(ABACUS_LIBNPY_VERSION "no") +endif() + +if(ENABLE_PEXSI) + set(ABACUS_PEXSI_VERSION "yes (version unknown)") + if(PEXSI_VERSION) + set(ABACUS_PEXSI_VERSION "yes (v${PEXSI_VERSION})") + elseif(PEXSI_DIR) + set(ABACUS_PEXSI_VERSION "yes (path: ${PEXSI_DIR})") + endif() +else() + set(ABACUS_PEXSI_VERSION "no") +endif() + +if(ENABLE_CNPY) + set(ABACUS_CNPY_VERSION "yes (path: ${cnpy_SOURCE_DIR})") +else() + set(ABACUS_CNPY_VERSION "no") +endif() + +# --- 5. Collect CMake Configuration Summary --- +set(ABACUS_CMAKE_OPTIONS "Build Options:") +list(APPEND CMAKE_OPTIONS_LIST " ENABLE_MPI=${ENABLE_MPI}") +list(APPEND CMAKE_OPTIONS_LIST " USE_OPENMP=${USE_OPENMP}") +list(APPEND CMAKE_OPTIONS_LIST " USE_CUDA=${USE_CUDA}") +list(APPEND CMAKE_OPTIONS_LIST " USE_ROCM=${USE_ROCM}") +list(APPEND CMAKE_OPTIONS_LIST " ENABLE_LCAO=${ENABLE_LCAO}") +list(APPEND CMAKE_OPTIONS_LIST " USE_ELPA=${USE_ELPA}") +list(APPEND CMAKE_OPTIONS_LIST " ENABLE_LIBXC=${ENABLE_LIBXC}") +list(APPEND CMAKE_OPTIONS_LIST " ENABLE_MLALGO=${ENABLE_MLALGO}") +list(APPEND CMAKE_OPTIONS_LIST " ENABLE_ASAN=${ENABLE_ASAN}") +list(APPEND CMAKE_OPTIONS_LIST " BUILD_TESTING=${BUILD_TESTING}") +foreach(option_line ${CMAKE_OPTIONS_LIST}) + set(ABACUS_CMAKE_OPTIONS "${ABACUS_CMAKE_OPTIONS}${option_line}") +endforeach() + +set(ABACUS_CMAKE_FIND_PACKAGES "FindPackage Results:") +list(APPEND CMAKE_FIND_PACKAGES_LIST " MPI Found=${MPI_FOUND}") +list(APPEND CMAKE_FIND_PACKAGES_LIST " OpenMP Found=${OpenMP_CXX_FOUND}") +list(APPEND CMAKE_FIND_PACKAGES_LIST " CUDA Found=${CUDA_FOUND}") +list(APPEND CMAKE_FIND_PACKAGES_LIST " ROCm Found=${ROCM_FOUND}") +list(APPEND CMAKE_FIND_PACKAGES_LIST " ELPA Found=${ELPA_FOUND}") +list(APPEND CMAKE_FIND_PACKAGES_LIST " Libxc Found=${Libxc_FOUND}") +list(APPEND CMAKE_FIND_PACKAGES_LIST " PEXSI Found=${PEXSI_FOUND}") +list(APPEND CMAKE_FIND_PACKAGES_LIST " LibRI Found=${LIBRI_FOUND}") +foreach(package_line ${CMAKE_FIND_PACKAGES_LIST}) + set(ABACUS_CMAKE_FIND_PACKAGES "${ABACUS_CMAKE_FIND_PACKAGES}${package_line}") +endforeach() diff --git a/cmake/Testing.cmake b/cmake/Testing.cmake new file mode 100644 index 0000000000..76786ad13c --- /dev/null +++ b/cmake/Testing.cmake @@ -0,0 +1,108 @@ +# ============================================================================= +# Setup Testing Environment (GTest, CTest, AddTest function) +# ============================================================================== + +# include_guard(GLOBAL) + +# --- Helper Macro: Ensure a minimum C++ standard version --- +macro(set_if_higher VARIABLE VALUE) + if(${VARIABLE} LESS ${VALUE}) + set(${VARIABLE} ${VALUE}) + endif() +endmacro() + +# --- Main function to configure everything related to testing --- +function(setup_testing) + # Add performance test in abacus + if(ENABLE_GOOGLEBENCH) + set(BUILD_TESTING ON) + find_package(benchmark HINTS ${BENCHMARK_DIR}) + if(NOT ${benchmark_FOUND}) + set(BENCHMARK_USE_BUNDLED_GTEST OFF) + include(FetchContent) + FetchContent_Declare( + benchmark + GIT_REPOSITORY https://github.com/google/benchmark.git + GIT_TAG "origin/main" + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE) + set(BENCHMARK_ENABLE_TESTING OFF) + FetchContent_MakeAvailable(benchmark) + endif() + endif() + + if(NOT BUILD_TESTING) + message(STATUS "Testing is disabled via BUILD_TESTING=OFF.") + return() + endif() + + message(STATUS "Setting up testing environment...") + + # Set a minimum C++ standard for orbital + set_if_higher(CMAKE_CXX_STANDARD 14) + + include(CTest) + enable_testing() + + find_package(GTest QUIET HINTS /usr/local/lib/ ${GTEST_DIR}) + if(NOT GTest_FOUND) + message(STATUS "GTest not found. Fetching from GitHub...") + include(FetchContent) + + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG "origin/main" # Consider pinning to a stable tag + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE + ) + # Prevent GTest from overriding our project's compiler flags + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) + endif() + + # Define the AddTest function for creating unit tests + function(AddTest) + cmake_parse_arguments(UT "DYN" "TARGET" + "LIBS;DYN_LIBS;STATIC_LIBS;SOURCES;DEPENDS" ${ARGN}) + add_executable(${UT_TARGET} ${UT_SOURCES}) + + if(ENABLE_COVERAGE) + add_coverage(${UT_TARGET}) + endif() + + target_link_libraries(${UT_TARGET} ${UT_LIBS} Threads::Threads + GTest::gtest_main GTest::gmock_main) + + if(ENABLE_GOOGLEBENCH) + target_link_libraries(${UT_TARGET} benchmark::benchmark) + endif() + + if(USE_OPENMP) + target_link_libraries(${UT_TARGET} OpenMP::OpenMP_CXX) + endif() + + # Link to build info if needed + if("${UT_SOURCES}" MATCHES "parse_args.cpp") + target_include_directories(${UT_TARGET} PUBLIC ${CMAKE_BINARY_DIR}/source/source_io) + endif() + + install(TARGETS ${UT_TARGET} DESTINATION ${CMAKE_BINARY_DIR}/tests) + add_test( + NAME ${UT_TARGET} + COMMAND ${UT_TARGET} + WORKING_DIRECTORY $) + endfunction() + + # Add the test subdirectory + if(EXISTS ${CMAKE_SOURCE_DIR}/tests/CMakeLists.txt) + add_subdirectory(tests) + elseif(EXISTS ${CMAKE_SOURCE_DIR}/test/CMakeLists.txt) + add_subdirectory(test) + else() + message(WARNING "BUILD_TESTING is ON, but no 'tests/' or 'test/' directory found.") + endif() + + message(STATUS "Testing environment configured successfully.") + +endfunction() diff --git a/generate_build_info.sh b/generate_build_info.sh new file mode 100644 index 0000000000..2246b579d3 --- /dev/null +++ b/generate_build_info.sh @@ -0,0 +1,251 @@ +#!/bin/bash + +# ============================================================================== +# generate_build_info.sh +# +# A script to generate build_info.h for Makefile-based builds. +# It attempts to detect system information, compiler flags, and library versions +# to provide a feature-rich build report similar to the CMake system. +# ============================================================================== + +# set -e # Exit immediately if a command exits with a non-zero status. + +if [ -z "$1" ]; then + echo "Usage: $0 " + exit 1 +fi +OUTPUT_FILE=$1 + +# --- Helper Functions --- + +# Function to get version from a command's output +# Usage: get_version_from_command "" "" +get_version_from_command() { + local cmd="$1" + local regex="$2" + if command -v "$cmd" > /dev/null; then + local output=$($cmd 2>&1) + if [[ $output =~ $regex ]]; then + echo "${BASH_REMATCH[1]}" + return 0 + fi + fi + return 1 +} + +# Function to get version from a header file +# Usage: get_version_from_header "" "" +get_version_from_header() { + local header_path="$1" + local regex="$2" + if [ -f "$header_path" ]; then + local line=$(grep -E "$regex" "$header_path" | head -n 1) + if [[ $line =~ $regex ]]; then + echo "${BASH_REMATCH[1]}" + return 0 + fi + fi + return 1 +} + +# --- Main Detection Logic --- + +# 1. Basic Info +PLATFORM_NAME="CPU" +BUILD_USER=$(whoami) +BUILD_HOST=$(hostname) +CXX_COMPILER_PATH="${CXX:-g++}" +CXX_COMPILER_VERSION=$($CXX_COMPILER_PATH --version 2>&1 | head -n 1) +CXX_FLAGS="${CXXFLAGS:-}" +LINKER_FLAGS="${LDFLAGS:-}" +CUDA_FLAGS="${CUDAFLAGS:-}" + +# Detect Platform based on environment variables +if [ "${USE_ROCM}" == "ON" ]; then PLATFORM_NAME="CPU + AMD ROCm"; fi +if [ "${USE_CUDA}" == "ON" ]; then PLATFORM_NAME="CPU + NVIDIA CUDA"; fi +if [ "${USE_ELPA}" == "ON" ] && [ "${ENABLE_LCAO}" == "ON" ]; then PLATFORM_NAME="${PLATFORM_NAME} + ELPA"; fi + +# 2. MPI +MPI_IMPLEMENTATION="no" +MPI_VERSION="no" +if [ "${ENABLE_MPI}" == "ON" ]; then + MPI_IMPLEMENTATION="Unknown" + MPI_COMPILER="${MPI_CXX_COMPILER:-mpicxx}" + if command -v "$MPI_COMPILER" > /dev/null; then + # Intel MPI (oneAPI) + version=$(get_version_from_command "$MPI_COMPILER --version" "Intel\(R\) oneAPI DPC\+\+/C\+\+ Compiler ([0-9]+\.[0-9]+\.[0-9]+)") + if [ -n "$version" ]; then MPI_IMPLEMENTATION="Intel MPI"; MPI_VERSION="$version"; fi + # Intel MPI (classic) + if [ -z "$version" ]; then + version=$(get_version_from_command "$MPI_COMPILER --version" "icpc \(ICC\) ([0-9]+\.[0-9]+\.[0-9]+)") + if [ -n "$version" ]; then MPI_IMPLEMENTATION="Intel MPI"; MPI_VERSION="$version"; fi + fi + # OpenMPI + if [ -z "$version" ]; then + version=$(get_version_from_command "$MPI_COMPILER --version" "Open MPI ([0-9]+\.[0-9]+\.[0-9]+)") + if [ -n "$version" ]; then MPI_IMPLEMENTATION="OpenMPI"; MPI_VERSION="$version"; fi + fi + # MPICH + if [ -z "$version" ]; then + version=$(get_version_from_command "$MPI_COMPILER --version" "MPICH Version: ([0-9]+\.[0-9]+\.[0-9]+)") + if [ -n "$version" ]; then MPI_IMPLEMENTATION="MPICH"; MPI_VERSION="$version"; fi + fi + fi + if [ "$MPI_VERSION" == "no" ]; then MPI_VERSION="yes (version unknown)"; else MPI_VERSION="yes (v$MPI_VERSION)"; fi +else + MPI_IMPLEMENTATION="no" + MPI_VERSION="no" +fi + +# 3. OpenMP +OPENMP_VERSION="no" +if [[ "$CXX_FLAGS" == *"-fopenmp"* ]]; then + OPENMP_VERSION="yes (version unknown)" + # Try to get version from compiler + if command -v "$CXX_COMPILER_PATH" > /dev/null; then + version=$($CXX_COMPILER_PATH -dM -E - < /dev/null 2>&1 | grep '#define _OPENMP ' | awk '{print $3}') + if [ -n "$version" ]; then + # Convert YYYYMM to a more readable format if possible + year=$((version / 10000)) + month=$((version % 10000)) + OPENMP_VERSION="yes (v${year}.${month})" + fi + fi +fi + +# 4. MKL +MKL_SUPPORT="no" +if [ -n "$MKLROOT" ]; then + MKL_SUPPORT="yes (version unknown)" + version=$(get_version_from_header "$MKLROOT/include/mkl_version.h" "INTEL_MKL_VERSION ([0-9]+)") + if [ -n "$version" ]; then + major=$((version / 10000)) + minor=$(( (version % 10000) / 100 )) + patch=$((version % 100)) + MKL_SUPPORT="yes (v${major}.${minor}.${patch})" + fi +fi + +# 5. Libxc +LIBXC_VERSION="no" +if [ "${ENABLE_LIBXC}" == "ON" ]; then + LIBXC_VERSION="yes (version unknown)" + # Try pkg-config first + if command -v pkg-config > /dev/null; then + version=$(pkg-config --modversion libxc 2>/dev/null) + if [ -n "$version" ]; then LIBXC_VERSION="yes (v$version)"; fi + fi + # Fallback to header + if [[ "$LIBXC_VERSION" == *"unknown"* ]]; then + # Assuming standard include paths + version=$(get_version_from_header "/usr/include/libxc/libxc.h" "LIBXC_VERSION_ \"([0-9]+\.[0-9]+\.[0-9]+)\"") + if [ -n "$version" ]; then LIBXC_VERSION="yes (v$version)"; fi + fi +fi + +# 6. CUDA +CUDA_VERSION="no" +if [ "${USE_CUDA}" == "ON" ]; then + CUDA_VERSION="yes (version unknown)" + if [ -n "$CUDA_HOME" ]; then + version=$(get_version_from_command "$CUDA_HOME/bin/nvcc --version" "release ([0-9]+\.[0-9]+)") + if [ -n "$version" ]; then CUDA_VERSION="yes (v$version)"; fi + elif command -v nvcc > /dev/null; then + version=$(get_version_from_command "nvcc --version" "release ([0-9]+\.[0-9]+)") + if [ -n "$version" ]; then CUDA_VERSION="yes (v$version)"; fi + fi +fi + +# 7. ROCm/HIP +ROCM_VERSION="no" +if [ "${USE_ROCM}" == "ON" ]; then + ROCM_VERSION="yes (version unknown)" + if [ -n "$ROCM_PATH" ]; then + version=$(get_version_from_command "$ROCM_PATH/bin/hipcc --version" "HIP version: ([0-9]+\.[0-9]+\.[0-9]+)") + if [ -n "$version" ]; then ROCM_VERSION="yes (v$version)"; fi + elif command -v hipcc > /dev/null; then + version=$(get_version_from_command "hipcc --version" "HIP version: ([0-9]+\.[0-9]+\.[0-9]+)") + if [ -n "$version" ]; then ROCM_VERSION="yes (v$version)"; fi + fi +fi + +# 8. DeePMD-kit +DEEPMD_VERSION="no" +if [ -n "$DeePMD_DIR" ]; then + DEEPMD_VERSION="yes (version unknown)" + version=$(get_version_from_header "$DeePMD_DIR/include/deepmd/version.h" "global_install_prefix=\".*deepmd-kit-([0-9]+\.[0-9]+\.[0-9]+)\"") + if [ -n "$version" ]; then DEEPMD_VERSION="yes (v$version)"; else DEEPMD_VERSION="yes (path: $DeePMD_DIR)"; fi +fi + +# 9. ELPA +ELPA_VERSION="no" +if [ "${USE_ELPA}" == "ON" ] && [ -n "$ELPA_DIR" ]; then + ELPA_VERSION="yes (version unknown)" + if [ -f "$ELPA_DIR/bin/elpa2_print_version" ]; then + version=$("$ELPA_DIR/bin/elpa2_print_version" 2>/dev/null) + if [ -n "$version" ]; then ELPA_VERSION="yes ($version)"; fi + else + ELPA_VERSION="yes (path: $ELPA_DIR)" + fi +fi + +# 10. LibRI +CEREAL_VERSION="no" +if [ "${ENABLE_CEREAL}" == "ON" ] && [ -n "$CEREAL_DIR" ]; then + CEREAL_VERSION="yes (version unknown)" + major=$(get_version_from_header "$CEREAL_DIR/include/RI/version.h" "__CEREAL_VERSION_MAJOR[[:space:]]+([0-9]+)") + minor=$(get_version_from_header "$CEREAL_DIR/include/RI/version.h" "__CEREAL_VERSION_MINOR[[:space:]]+([0-9]+)") + patch=$(get_version_from_header "$CEREAL_DIR/include/RI/version.h" "__CEREAL_VERSION_PATCH[[:space:]]+([0-9]+)") + if [ -n "$major" ] && [ -n "$minor" ] && [ -n "$patch" ]; then + CEREAL_VERSION="yes (v${major}.${minor}.${patch})" + else + CEREAL_VERSION="yes (path: $CEREAL_DIR)" + fi +fi + +# 11. Cereal +CEREAL_VERSION="no" +if [ "${ENABLE_CEREAL}" == "ON" ] && [ -n "$CEREAL_DIR" ]; then + CEREAL_VERSION="yes (version unknown)" + major=$(get_version_from_header "$CEREAL_DIR/include/cereal/version.hpp" "__CEREAL_VERSION_MAJOR[[:space:]]+([0-9]+)") + minor=$(get_version_from_header "$CEREAL_DIR/include/cereal/version.hpp" "__CEREAL_VERSION_MINOR[[:space:]]+([0-9]+)") + patch=$(get_version_from_header "$CEREAL_DIR/include/cereal/version.hpp" "__CEREAL_VERSION_PATCH[[:space:]]+([0-9]+)") + if [ -n "$major" ] && [ -n "$minor" ] && [ -n "$patch" ]; then + CEREAL_VERSION="yes (v${major}.${minor}.${patch})" + else + CEREAL_VERSION="yes (path: $CEREAL_DIR)" + fi +fi +# --- Final File Generation --- + +INPUT_FILE="source_io/build_info.h.in" + +# Use sed to replace all placeholders with detected values +# Note the use of different delimiters (#) for paths to avoid conflicts with / +sed \ + -e "s#@ABACUS_PLATFORM_NAME@#$PLATFORM_NAME#g" \ + -e "s#@ABACUS_BUILD_TYPE@#${BUILD_TYPE:-Release}#g" \ + -e "s#@ABACUS_BUILD_USER@#$BUILD_USER#g" \ + -e "s#@ABACUS_BUILD_HOST@#$BUILD_HOST#g" \ + -e "s#@ABACUS_CXX_COMPILER_ID@#${CXX_COMPILER_ID:-Unknown}#g" \ + -e "s#@ABACUS_CXX_COMPILER_PATH@#$CXX_COMPILER_PATH#g" \ + -e "s#@ABACUS_CXX_COMPILER_VERSION@#$CXX_COMPILER_VERSION#g" \ + -e "s#@ABACUS_CXX_FLAGS@#$CXX_FLAGS#g" \ + -e "s#@ABACUS_LINKER_FLAGS@#$LINKER_FLAGS#g" \ + -e "s#@ABACUS_CUDA_FLAGS@#$CUDA_FLAGS#g" \ + -e "s#@ABACUS_MPI_IMPLEMENTATION@#$MPI_IMPLEMENTATION#g" \ + -e "s#@ABACUS_MPI_VERSION@#$MPI_VERSION#g" \ + -e "s#@ABACUS_OPENMP_VERSION@#$OPENMP_VERSION#g" \ + -e "s#@ABACUS_MKL_SUPPORT@#$MKL_SUPPORT#g" \ + -e "s#@ABACUS_LIBXC_VERSION@#$LIBXC_VERSION#g" \ + -e "s#@ABACUS_CUDA_VERSION@#$CUDA_VERSION#g" \ + -e "s#@ABACUS_ROCM_VERSION@#$ROCM_VERSION#g" \ + -e "s#@ABACUS_DEEPMD_VERSION@#$DEEPMD_VERSION#g" \ + -e "s#@ABACUS_ELPA_VERSION@#$ELPA_VERSION#g" \ + -e "s#@ABACUS_CEREAL_VERSION@#$CEREAL_VERSION#g" \ + -e "s#@ABACUS_ASAN_STATUS@#${ENABLE_ASAN:-no}#g" \ + -e "s#@ABACUS_CMAKE_OPTIONS@#Not available with Makefile#g" \ + -e "s#@ABACUS_CMAKE_FIND_PACKAGES@#Not available with Makefile#g" \ + "$INPUT_FILE" > "$OUTPUT_FILE" + +echo "Generated $OUTPUT_FILE successfully." diff --git a/source/Makefile b/source/Makefile index 6d072ef714..5cc3ce9907 100644 --- a/source/Makefile +++ b/source/Makefile @@ -1,10 +1,17 @@ # This is the Makefile of ABACUS -include Makefile.vars +ABACUS_ROOT := $(dir $(firstword $(MAKEFILE_LIST))) + +BUILD_DIR ?= build +OBJ_DIR = $(BUILD_DIR)/obj +BIN_DIR = $(BUILD_DIR)/bin +BUILD_INFO_DIR = $(BUILD_DIR)/source_io + +include $(ABACUS_ROOT)Makefile.vars + #========================== # Compiler information #========================== - -INCLUDES = -I. -Isource_main -Isource_main/commands -Icommands -I../ -Isource_base/module_container +INCLUDES = -I$(ABACUS_ROOT) -I$(ABACUS_ROOT)source_main -I$(ABACUS_ROOT)source_main/commands -I$(ABACUS_ROOT)commands -I$(BUILD_INFO_DIR) -I$(ABACUS_ROOT)../ -I$(ABACUS_ROOT)source_base/module_container LIBS = -lm -lpthread OPTS = -std=c++14 -pedantic -m64 ${INCLUDES} @@ -21,9 +28,8 @@ ifeq ($(OPENMP), ON) else ELPA_NAME = elpa endif -OBJ_DIR = obj -BIN_DIR = ../bin -SOURCE_DIR = . + +SOURCE_DIR = $(ABACUS_ROOT) ifeq ($(findstring mpi, $(CXX)), mpi) # We do not support EXX in sequential version temporarily. HONG += -D__MPI -DUSE_CEREAL_SERIALIZATION @@ -186,7 +192,9 @@ ifdef PEXSI_DIR HONG += -D__PEXSI endif -include Makefile.Objects +include $(ABACUS_ROOT)Makefile.Objects +VPATH := $(patsubst ./%,$(ABACUS_ROOT)%,$(VPATH)) +HEADERS := $(patsubst %,$(ABACUS_ROOT)%,$(HEADERS)) #========================== # Optional HONG @@ -208,6 +216,44 @@ endif FP_OBJS=$(patsubst %.o, ${OBJ_DIR}/%.o, ${OBJS_ABACUS}) +###### START of ABACUS INFO PART ###### + +# Rule to generate the build_info.h file using the shell script. +# This rule is executed whenever build_info.h.in, the script, or any of the +# relevant Makefile variables change. +$(BUILD_INFO_DIR)/build_info.h:$(ABACUS_ROOT)source_io/build_info.h.in $(ABACUS_ROOT)../generate_build_info.sh + @echo "Generating build information header..." + @# Make sure directory is available + @mkdir -p $(dir $@) + @# Export all necessary Makefile variables as environment for the script + @export BUILD_TYPE="Release"; \ + if [ "$(DEBUG)" = "ON" ]; then export BUILD_TYPE="Debug"; export ENABLE_ASAN="yes"; else export ENABLE_ASAN="no"; fi; \ + export CXX="${CXX}"; \ + export CXXFLAGS="${OPTS} ${HONG}"; \ + export MKLROOT="${MKLROOT}"; \ + export ELPA_DIR="${ELPA_DIR}"; \ + export FFTW_DIR="${FFTW_DIR}"; \ + export CEREAL_DIR="${CEREAL_DIR}"; \ + export LIBXC_DIR="${LIBXC_DIR}"; \ + export LIBRI_DIR="${LIBRI_DIR}"; \ + export DeePMD_DIR="${DeePMD_DIR}"; \ + export TensorFlow_DIR="${TensorFlow_DIR}"; \ + export PEXSI_DIR="${PEXSI_DIR}"; \ + export ENABLE_MPI="no"; \ + if [ "$(MPI)" = "ON" ]; then export ENABLE_MPI="ON"; fi; \ + export USE_CUDA="no"; \ + if [ "$(GPU)" = "CUDA" ]; then export USE_CUDA="ON"; fi; \ + $(ABACUS_ROOT)../generate_build_info.sh $@ + @echo "Build information header generated." + + # Specific rule for parse_args.o to enforce dependency on build_info.h. + # This overrides the generic %.o:%.cpp rule for this specific file. +${OBJ_DIR}/parse_args.o: $(ABACUS_ROOT)source_io/parse_args.cpp $(BUILD_INFO_DIR)/build_info.h + @mkdir -p $(dir $@) + ${CXX} ${OPTS} ${OPTS_MPI} -c ${HONG} $< -o $@ + +###### END of ABACUS INFO PART ###### + #========================== # MAKING OPTIONS #========================== @@ -218,25 +264,26 @@ abacus: test: @ $(MAKE) abacus - @ cd ../tests/integrate/;sh Autotest.sh -a ../../../bin/ABACUS.mpi -n $(TESTNP) + @ cd $(ABACUS_ROOT)../tests/integrate/;sh Autotest.sh -a $(realpath $(BIN_DIR))/ABACUS.mpi -n $(TESTNP) pw $(BIN_DIR)/${VERSION}-PW.x: @ if [ ! -d $(BIN_DIR) ]; then mkdir $(BIN_DIR); fi - @ cd source_pw/module_pwdft; $(MAKE) CXX=${CXX} GPU=${GPU} DEBUG=$(DEBUG) FFTW_DIR=$(FFTW_DIR) OPENBLAS_LIB_DIR=$(OPENBLAS_LIB_DIR) ${PWTAG} - @ cp source_pw/module_pwdft/${VERSION}-PW.x $(BIN_DIR)/${VERSION}-PW.x + @ cd $(ABACUS_ROOT)source_pw/module_pwdft; $(MAKE) CXX=${CXX} GPU=${GPU} DEBUG=$(DEBUG) FFTW_DIR=$(FFTW_DIR) OPENBLAS_LIB_DIR=$(OPENBLAS_LIB_DIR) ${PWTAG} + @ cp $(ABACUS_ROOT)source_pw/module_pwdft/${VERSION}-PW.x $(realpath $(BIN_DIR))/${VERSION}-PW.x $(BIN_DIR)/${VERSION}.$(suffix) : ${FP_OBJS} ${PDIAG_OBJS} ${HEADERS} + @mkdir -p $(dir $@) ${CXX} ${OPTS} ${OPTS_MPI} $(FP_OBJS) ${PDIAG_OBJS} ${LIBS} -o $(BIN_DIR)/${VERSION}.$(suffix) - #========================== # rules #========================== -${OBJ_DIR}/%.o:%.cpp +# Note: The specific rule for parse_args.o above is more precise. +# This generic rule will apply to all other .cpp files. +${OBJ_DIR}/%.o:$(realpath $(BIN_DIR))%.cpp ${CXX} ${OPTS} ${OPTS_MPI} -c ${HONG} $< -o $@ .PHONY:clean test clean: - @ if [ -d $(OBJ_DIR) ]; then rm -rf $(OBJ_DIR); fi - @ if [ -d $(BIN_DIR) ]; then rm -rf $(BIN_DIR); fi - @ cd source_pw/module_pwdft; make clean + @ echo "Cleaning build directory: $(BUILD_DIR)" + @ rm -rf $(BUILD_DIR) diff --git a/source/source_io/CMakeLists.txt b/source/source_io/CMakeLists.txt index 6844f1cfb7..3322add8bb 100644 --- a/source/source_io/CMakeLists.txt +++ b/source/source_io/CMakeLists.txt @@ -116,6 +116,12 @@ add_library( ${objects} ) +target_include_directories( + io_basic + PUBLIC + ${CMAKE_BINARY_DIR}/source/source_io +) + add_library( io_advanced OBJECT diff --git a/source/source_io/build_info.h.in b/source/source_io/build_info.h.in new file mode 100644 index 0000000000..b0959240f4 --- /dev/null +++ b/source/source_io/build_info.h.in @@ -0,0 +1,60 @@ +#ifndef SOURCE_IO_BUILD_INFO_H +#define SOURCE_IO_BUILD_INFO_H + +// --- Platform & Environment --- +#define ABACUS_PLATFORM_NAME "@ABACUS_PLATFORM_NAME@" +#define ABACUS_BUILD_TYPE "@ABACUS_BUILD_TYPE@" +#define ABACUS_BUILD_USER "@ABACUS_BUILD_USER@" +#define ABACUS_BUILD_HOST "@ABACUS_BUILD_HOST@" +#define ABACUS_CXX_COMPILER_ID "@ABACUS_CXX_COMPILER_ID@" +#define ABACUS_CXX_COMPILER_PATH "@ABACUS_CXX_COMPILER_PATH@" +#define ABACUS_CXX_COMPILER_VERSION "@ABACUS_CXX_COMPILER_VERSION@" +#define ABACUS_CXX_FLAGS "@ABACUS_CXX_FLAGS@" +#define ABACUS_LINKER_FLAGS "@ABACUS_LINKER_FLAGS@" +#define ABACUS_CUDA_FLAGS "@ABACUS_CUDA_FLAGS@" + +// --- Sanitizers & Debugging --- +#define ABACUS_ASAN_STATUS "@ABACUS_ASAN_STATUS@" +#define ABACUS_DEBUG_SYMBOLS "@ABACUS_DEBUG_SYMBOLS@" + +// --- CMake Configuration Summary --- +#define ABACUS_CMAKE_OPTIONS "@ABACUS_CMAKE_OPTIONS@" +#define ABACUS_CMAKE_FIND_PACKAGES "@ABACUS_CMAKE_FIND_PACKAGES@" + +// --- Parallelism & Communication --- +#define ABACUS_MPI_IMPLEMENTATION "@ABACUS_MPI_IMPLEMENTATION@" +#define ABACUS_MPI_VERSION "@ABACUS_MPI_VERSION@" +#define ABACUS_CUDA_AWARE_MPI "@ABACUS_CUDA_AWARE_MPI@" +#define ABACUS_OPENMP_VERSION "@ABACUS_OPENMP_VERSION@" + +// --- Core Math Libraries --- +#define ABACUS_ELPA_VERSION "@ABACUS_ELPA_VERSION@" +#define ABACUS_MKL_SUPPORT "@ABACUS_MKL_SUPPORT@" +#define ABACUS_LIBXC_VERSION "@ABACUS_LIBXC_VERSION@" +#define ABACUS_FFTW_VERSION "@ABACUS_FFTW_VERSION@" + +// --- Accelerators & Specific Hardware --- +#define ABACUS_CUDA_VERSION "@ABACUS_CUDA_VERSION@" +#define ABACUS_ROCM_VERSION "@ABACUS_ROCM_VERSION@" +#define ABACUS_CUSOLVERMP_VERSION "@ABACUS_CUSOLVERMP_VERSION@" + +// --- Hybrid Functional Libraries --- +#define ABACUS_CEREAL_VERSION "@ABACUS_CEREAL_VERSION@" +#define ABACUS_LIBRI_VERSION "@ABACUS_LIBRI_VERSION@" +#define ABACUS_LIBCOMM_VERSION "@ABACUS_LIBCOMM_VERSION@" + +// --- AI & Machine Learning --- +#define ABACUS_LIBTORCH_VERSION "@ABACUS_LIBTORCH_VERSION@" +#define ABACUS_LIBNPY_VERSION "@ABACUS_LIBNPY_VERSION@" +#define ABACUS_DEEPMD_VERSION "@ABACUS_DEEPMD_VERSION@" +#define ABACUS_NEP_VERSION "@ABACUS_NEP_VERSION@" +#define ABACUS_TENSORFLOW_VERSION "@ABACUS_TENSORFLOW_VERSION@" + +// --- Testing & Other Libraries --- +#define ABACUS_GTEST_VERSION "@ABACUS_GTEST_VERSION@" +#define ABACUS_GOOGLEBENCH_VERSION "@ABACUS_GOOGLEBENCH_VERSION@" +#define ABACUS_RAPIDJSON_VERSION "@ABACUS_RAPIDJSON_VERSION@" +#define ABACUS_PEXSI_VERSION "@ABACUS_PEXSI_VERSION@" +#define ABACUS_CNPY_VERSION "@ABACUS_CNPY_VERSION@" + +#endif // SOURCE_IO_BUILD_INFO_H diff --git a/source/source_io/parse_args.cpp b/source/source_io/parse_args.cpp index 9d172e2469..4d24a35597 100644 --- a/source/source_io/parse_args.cpp +++ b/source/source_io/parse_args.cpp @@ -1,14 +1,121 @@ #include "parse_args.h" +#include "build_info.h" #include #include +#include +#include #include "source_io/read_input.h" #include "source_main/version.h" +#if defined(COMMIT_INFO) +#include "commit.h" +#endif + namespace ModuleIO { +void print_build_info() +{ + const int label_width = 30; + + auto print_section = [](const std::string& title) { + std::cout << std::endl; + std::cout << "----------------- " << title << " -----------------" << std::endl; + }; + + auto print_info = [label_width](const std::string& label, const std::string& value) { + std::cout << std::left << std::setw(label_width) << (label + ":") << " " << value << std::endl; + }; + + // --- 1. Core & Platform Info --- + print_section("ABACUS Core & Platform"); + print_info("ABACUS Version", VERSION); +#if defined(COMMIT) + print_info("Git Commit", COMMIT); +#else + print_info("Git Commit", "N/A"); +#endif + print_info("Target Platform", ABACUS_PLATFORM_NAME); + print_info("Build Type", ABACUS_BUILD_TYPE); + + // --- 2. Build Environment --- + print_section("Build Environment"); + print_info("Built By", ABACUS_BUILD_USER); + print_info("Built On", ABACUS_BUILD_HOST); + print_info("Built At", std::string(__DATE__) + " " + __TIME__); + + // --- 3. Compiler Info --- + print_section("Compiler & Flags"); + print_info("C++ Compiler", std::string(ABACUS_CXX_COMPILER_ID) + " (" + ABACUS_CXX_COMPILER_PATH + ")"); + print_info("C++ Compiler Ver", ABACUS_CXX_COMPILER_VERSION); + print_info("C++ Flags", ABACUS_CXX_FLAGS); + print_info("Linker Flags", ABACUS_LINKER_FLAGS); + print_info("CUDA Flags", ABACUS_CUDA_FLAGS); + + // --- 4. Sanitizers & Debugging --- + print_section("Sanitizers & Debugging"); + print_info("AddressSanitizer", ABACUS_ASAN_STATUS); + print_info("Debug Symbols", ABACUS_DEBUG_SYMBOLS); + + // --- 5. CMake Configuration Summary --- + print_section("CMake Configuration Summary"); + std::cout << ABACUS_CMAKE_OPTIONS << std::endl; + std::cout << std::endl; + std::cout << ABACUS_CMAKE_FIND_PACKAGES << std::endl; + + // --- 6. Parallelism & Communication --- + print_section("Parallelism & Communication"); + print_info("MPI Implementation", ABACUS_MPI_IMPLEMENTATION); + print_info("MPI Version", ABACUS_MPI_VERSION); + print_info("CUDA-aware MPI", ABACUS_CUDA_AWARE_MPI); + print_info("OpenMP Support", ABACUS_OPENMP_VERSION); + + // --- 7. Core Math Libraries --- + print_section("Core Math Libraries"); +#if defined(__LCAO) + print_info("LCAO Algorithm", "yes"); +#else + print_info("LCAO Algorithm", "no"); +#endif + print_info("ELPA Support", ABACUS_ELPA_VERSION); + print_info("MKL Support", ABACUS_MKL_SUPPORT); + print_info("LibXC Support", ABACUS_LIBXC_VERSION); + print_info("FFTW Support", ABACUS_FFTW_VERSION); + + // --- 8. Accelerators & Specific Hardware --- + print_section("Accelerators & Hardware"); + print_info("NVIDIA CUDA Support", ABACUS_CUDA_VERSION); + print_info("AMD ROCm Support", ABACUS_ROCM_VERSION); + print_info("CUSOLVERMP Support", ABACUS_CUSOLVERMP_VERSION); + + // --- 9. Hybrid Functional Libraries --- + print_section("Hybrid Functional Libraries"); + print_info("Cereal Serialization", ABACUS_CEREAL_VERSION); + print_info("LibRI Support", ABACUS_LIBRI_VERSION); + print_info("LibComm Support", ABACUS_LIBCOMM_VERSION); + + // --- 10. AI & Machine Learning --- + print_section("AI & Machine Learning"); + print_info("LibTorch Support", ABACUS_LIBTORCH_VERSION); + print_info("Libnpy Support", ABACUS_LIBNPY_VERSION); + print_info("DeePMD-kit Support", ABACUS_DEEPMD_VERSION); + print_info("NEP Support", ABACUS_NEP_VERSION); + print_info("TensorFlow Support", ABACUS_TENSORFLOW_VERSION); + + // --- 11. Testing & Other Libraries --- + print_section("Testing & Other Libraries"); + print_info("GTest Support", ABACUS_GTEST_VERSION); + print_info("Google Benchmark", ABACUS_GOOGLEBENCH_VERSION); + print_info("RapidJSON Support", ABACUS_RAPIDJSON_VERSION); + print_info("PEXSI Support", ABACUS_PEXSI_VERSION); + print_info("cnpy Support", ABACUS_CNPY_VERSION); + + std::cout << "----------------------------------------------------" << std::endl; + std::cout << std::endl; +} + void parse_args(int argc, char** argv) { for (int i = 1; i < argc; ++i) // Start from 1 to skip the program name @@ -16,7 +123,7 @@ void parse_args(int argc, char** argv) std::string arg = argv[i]; if (arg == "--version" || arg == "-v" || arg == "-V") { -#ifdef VERSION +#if defined(VERSION) const char* version = VERSION; #else const char* version = "unknown"; @@ -24,16 +131,25 @@ void parse_args(int argc, char** argv) std::cout << "ABACUS version " << version << std::endl; std::exit(0); } + else if (arg == "--info" || arg == "-i" || arg == "-I") + { + print_build_info(); + std::exit(0); + } else if (arg == "--check-input") { ModuleIO::ReadInput::check_mode = true; } else { - std::cerr << "Unknown argument: " << arg << std::endl; + std::cerr << "Error: Unknown argument: " << arg << std::endl; + std::cerr << "Usage: abacus [options]" << std::endl; + std::cerr << " -v, -V, --version Display version information." << std::endl; + std::cerr << " -i, -I, --info Display detailed build information." << std::endl; + std::cerr << " --check-input Check input file syntax and exit." << std::endl; std::exit(1); } } } -} // namespace ModuleIO \ No newline at end of file +} // namespace ModuleIO diff --git a/source/source_io/parse_args.h b/source/source_io/parse_args.h index c2a246946e..24d99ec5e6 100644 --- a/source/source_io/parse_args.h +++ b/source/source_io/parse_args.h @@ -4,8 +4,10 @@ namespace ModuleIO { /** - * @brief This function reture the version information when using command - * "abacus --version", "abacus -v" or "abacus -V". Otherwise, it does nothing. + * @brief This function returns the version information when using command + * "abacus --version", "abacus -v" or "abacus -V"; returns the compilation + * details when using command "abacus --info", "abacus -i" or "abacus -I"; + * otherwise, it returns usage. * * @param [in] argc (ARGument Count) is an integer variable that stores the number * of command-line arguments passed by the user including the name of the program. @@ -18,6 +20,7 @@ namespace ModuleIO * arguments. */ void parse_args(int argc, char** argv); +void print_build_info(); } // namespace ModuleIO -#endif \ No newline at end of file +#endif diff --git a/source/source_io/test/parse_args_test.cpp b/source/source_io/test/parse_args_test.cpp index 1ff3c4d543..526abaa0c5 100644 --- a/source/source_io/test/parse_args_test.cpp +++ b/source/source_io/test/parse_args_test.cpp @@ -1,63 +1,108 @@ #include "source_io/parse_args.h" - #include "gtest/gtest.h" #include "source_io/read_input.h" #include "source_main/version.h" -bool ModuleIO::ReadInput::check_mode = false; +// Already deal with Testing.cmake +// #include "build_info.h" + +// Refresh test status +class ParseArgsTest : public ::testing::Test { +protected: + void SetUp() override { + ModuleIO::ReadInput::check_mode = false; + } +}; -TEST(ParseArgsTest, OutVersionTest) -{ - // Test case 1: no arguments +// Test for no argument +TEST_F(ParseArgsTest, NoArguments) { char arg0[] = "test"; char* argv[] = {arg0}; int argc = 1; + testing::internal::CaptureStdout(); ModuleIO::parse_args(argc, argv); std::string output = testing::internal::GetCapturedStdout(); - EXPECT_EQ("", output); - // No output expected + EXPECT_TRUE(output.empty()) << "Expected no output for no arguments."; +} +// Test for abacus ersion +TEST_F(ParseArgsTest, VersionFlags) { #ifdef VERSION std::string output_ref = "ABACUS version " + std::string(VERSION) + "\n"; #else std::string output_ref = "ABACUS version unknown\n"; #endif - // Test case 2: --version argument - char arg1[] = "--version"; - char* argv1[] = {arg0, arg1}; - argc = 2; - testing::internal::CaptureStdout(); - EXPECT_EXIT(ModuleIO::parse_args(argc, argv1), ::testing::ExitedWithCode(0), ""); - output = testing::internal::GetCapturedStdout(); - EXPECT_EQ(output_ref, output); - - // Test case 3: -v argument - char arg2[] = "-v"; - char* argv2[] = {arg0, arg2}; - argc = 2; - testing::internal::CaptureStdout(); - EXPECT_EXIT(ModuleIO::parse_args(argc, argv2), ::testing::ExitedWithCode(0), ""); - output = testing::internal::GetCapturedStdout(); - EXPECT_EQ(output_ref, output); - - // Test case 4: -V argument - char arg3[] = "-V"; - char* argv3[] = {arg0, arg3}; - argc = 2; - testing::internal::CaptureStdout(); - EXPECT_EXIT(ModuleIO::parse_args(argc, argv3), ::testing::ExitedWithCode(0), ""); - output = testing::internal::GetCapturedStdout(); - EXPECT_EQ(output_ref, output); + std::vector version_args = {"--version", "-v", "-V"}; + + for (const auto& arg : version_args) { + char arg0[] = "test"; + std::vector argv = {arg0, const_cast(arg.c_str())}; + int argc = argv.size(); + + EXPECT_EXIT( + { ModuleIO::parse_args(argc, argv.data()); }, + ::testing::ExitedWithCode(0), + output_ref.c_str() + ) << "Failed for argument: " << arg; + } +} + +// Test for abacus info +TEST_F(ParseArgsTest, InfoFlags) { + std::vector info_args = {"--info", "-i", "-I"}; + + for (const auto& arg : info_args) { + char arg0[] = "test"; + std::vector argv = {arg0, const_cast(arg.c_str())}; + int argc = argv.size(); + + EXPECT_EXIT( + { ModuleIO::parse_args(argc, argv.data()); }, + ::testing::ExitedWithCode(0), + "ABACUS Core" + ) << "Failed for argument: " << arg; + } } -TEST(ParseArgsTest, CheckInput) -{ +// Test for unavailable arguments +TEST_F(ParseArgsTest, UnknownArgument) { + char arg0[] = "test"; + char arg1[] = "--nonexistent-option"; + char* argv[] = {arg0, arg1}; + int argc = 2; + + EXPECT_EXIT( + { ModuleIO::parse_args(argc, argv); }, + ::testing::ExitedWithCode(1), + "Usage: abacus" + ); +} + +// Test for --check-input +TEST_F(ParseArgsTest, CheckInputFlag) { char arg0[] = "test"; char arg1[] = "--check-input"; char* argv[] = {arg0, arg1}; int argc = 2; + ModuleIO::parse_args(argc, argv); + EXPECT_TRUE(ModuleIO::ReadInput::check_mode); -} \ No newline at end of file +} + +TEST_F(ParseArgsTest, PriorityVersionOverCheckInput) { + char arg0[] = "test"; + char arg1[] = "--version"; + char arg2[] = "--check-input"; + char* argv[] = {arg0, arg1, arg2}; + int argc = 3; + + EXPECT_EXIT( + { ModuleIO::parse_args(argc, argv); }, + ::testing::ExitedWithCode(0), + "ABACUS version" + ); + +}