cmake_minimum_required(VERSION 3.5...3.18)
#------------------------------------------------------------------------------
# Version information
#------------------------------------------------------------------------------

set(DFTRACER_VERSION_MAJOR "4")
set(DFTRACER_VERSION_MINOR "0")
set(DFTRACER_VERSION_PATCH "0")
set(DFTRACER_PACKAGE ${PROJECT_NAME})
set(DFTRACER_PACKAGE_NAME ${PROJECT_NAME})
set(DFTRACER_PACKAGE_VERSION "${DFTRACER_VERSION_MAJOR}.${DFTRACER_VERSION_MINOR}.${DFTRACER_VERSION_PATCH}")
set(DFTRACER_PACKAGE_VERSION_MAJOR "${DFTRACER_VERSION_MAJOR}.${DFTRACER_VERSION_MINOR}")
set(DFTRACER_PACKAGE_VERSION_MINOR "${DFTRACER_VERSION_PATCH}")
set(DFTRACER_PACKAGE_STRING "${DFTRACER_PACKAGE_NAME} ${DFTRACER_PACKAGE_VERSION}")
set(DFTRACER_PACKAGE_TARNAME "${DFTRACER_PACKAGE}")

set(DFTRACER_VERSION "(2, 0, 0)")

project(dftracer LANGUAGES C CXX)

# Convenience defines
string(TOUPPER "${PROJECT_NAME}" UPPER_PROJECT_NAME)
string(TOLOWER "${PROJECT_NAME}" LOWER_PROJECT_NAME)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-empty-body -Wno-format-extra-args")
#------------------------------------------------------------------------------
# Internal Includes for header and libraries
#------------------------------------------------------------------------------
# Get installation directories -- these get used in various places;
# best to just make them available
option (DFTRACER_LIBDIR_AS_LIB OFF)
if (NOT DFTRACER_LIBDIR_AS_LIB)
    include(GNUInstallDirs)
endif ()
#
if (CMAKE_INSTALL_LIBDIR)
   message(STATUS "CMAKE_INSTALL_LIBDIR ${CMAKE_INSTALL_LIBDIR}")
   set(DFTRACER_LIBDIR ${CMAKE_INSTALL_LIBDIR})
   set(DFTRACER_INSTALL_INCLUDE_DIR
       ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR})
   set(DFTRACER_INSTALL_DOCDIR
       ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DOCDIR})
   set(DFTRACER_INSTALL_SYSCONFDIR
       ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_SYSCONFDIR}/modulefiles)
   set(DFTRACER_INSTALL_BINFDIR
           ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR})
else ()
   set(DFTRACER_LIBDIR "lib")
   set(DFTRACER_INSTALL_INCLUDE_DIR "${CMAKE_INSTALL_PREFIX}/include")
   set(DFTRACER_INSTALL_DOCDIR "${CMAKE_INSTALL_PREFIX}/doc")
   set(DFTRACER_INSTALL_SYSCONFDIR "${CMAKE_INSTALL_PREFIX}/etc/modulefiles")
   set(DFTRACER_INSTALL_BINARYDIR "${CMAKE_INSTALL_PREFIX}/bin")
   message(STATUS "DFTRACER_LIBDIR set to ${DFTRACER_LIBDIR}")
endif ()

#------------------------------------------------------------------------------
# Internal Paths for cmake libraries and Setup install and output Directories
#------------------------------------------------------------------------------
# This sets where to look for dependent libraries
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${CMAKE_BINARY_DIR} ${CMAKE_INSTALL_PREFIX})
# This sets where to look for dependent library's cmake files
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}/${DFTRACER_LIBDIR}/cmake)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}/share/cmake)

#------------------------------------------------------------------------------
if (NOT DFTRACER_EXTERNALLY_CONFIGURED)
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin CACHE PATH "Single Directory for all Executables.")
    set(CMAKE_INCLUDE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/include CACHE PATH "Store the headers.")
    set(EXECUTABLE_OUTPUT_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${DFTRACER_LIBDIR} CACHE PATH "Single Directory for all Libraries")
    set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${DFTRACER_LIBDIR} CACHE PATH "Single Directory for all static libraries.")
endif ()

include_directories(${CMAKE_BINARY_DIR}/include)
link_directories(${CMAKE_BINARY_DIR}/${DFTRACER_LIBDIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)

#-----------------------------------------------------------------------------
# Targets built within this project are exported at Install time for use
# by other projects.
#-----------------------------------------------------------------------------
if (NOT DFTRACER_EXPORTED_TARGETS)
    set(DFTRACER_EXPORTED_TARGETS "dftracer-targets")
endif ()

include(dftracer-utils)
#------------------------------------------------------------------------------
# Build options
#------------------------------------------------------------------------------
# option(ENABLE_DFTRACER_DEBUG "Include debugging prints and logging" OFF) # This is not needed as we have CMAKE_BUILD_TYPE
# This is verbose, maybe an alternate might help simplify
#option(BUILD_URPC "Build DFTRACER's URPC code" OFF)
#option(ENABLE_PERFFLOW "Build with PerfFlow Aspect support" OFF)
#option(ENABLE_UCX_DTL "Build DFTRACER's UCX data transport layer" OFF)

# This option is off by default because non-developers should not use
# this option under normal circumstances.
option(DFTRACER_WARNINGS_AS_ERRORS "Build with warnings promoted to errors." OFF)
mark_as_advanced(DFTRACER_WARNINGS_AS_ERRORS)

option (DFTRACER_USE_CLANG_LIBCXX OFF)
if (DFTRACER_USE_CLANG_LIBCXX)
    set (GCC_TOOLCHAIN "")
endif (DFTRACER_USE_CLANG_LIBCXX)

# Options
option (DFTRACER_DISABLE_HWLOC "Disable HWLOC" On)
if (NOT DFTRACER_DISABLE_HWLOC)
    set(DFTRACER_HWLOC_ENABLE 1)
endif()

# Options
option (DFTRACER_ENABLE_FTRACING "Enable Function Tracing" OFF)
if (DFTRACER_ENABLE_FTRACING)
    set(DFTRACER_FTRACING_ENABLE 1)
    set(DFTRACER_FUNCTION_FLAGS "-g" "-finstrument-functions" "-Wl,-E" "-fvisibility=default")
else()
    set(DFTRACER_FUNCTION_FLAGS )
endif()

option (DFTRACER_ENABLE_MPI "Enable MPI" OFF)
if (DFTRACER_ENABLE_MPI)
    set(DFTRACER_MPI_ENABLE 1)
endif()
option (DFTRACER_BUILD_PYTHON_BINDINGS "Build python bindings." ON)
set(DFTRACER_PYTHON_EXE "python3" CACHE STRING "Python executable to use for building.")
set(DFTRACER_PYTHON_SITE $ENV{DFTRACER_PYTHON_SITE} CACHE STRING "Python site packages to use for building.")

if(NOT DFTRACER_PYTHON_EXEC STREQUAL "OFF")
        set(PYTHON_EXECUTABLE ${DFTRACER_PYTHON_EXEC})
endif()

if (DFTRACER_BUILD_PYTHON_BINDINGS)
    if (DFTRACER_PYTHON_SITE)
        set(PYTHON_SITE_PACKAGES "${DFTRACER_PYTHON_SITE}")
        set(MAIN_PYTHON_SITE_PACKAGES ${PYTHON_SITE_PACKAGES})
    else()
        set(PYTHON_SITE_PACKAGES ${CMAKE_INSTALL_PREFIX})
        execute_process (COMMAND ${DFTRACER_PYTHON_EXE} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())" OUTPUT_VARIABLE MAIN_PYTHON_SITE_PACKAGES OUTPUT_STRIP_TRAILING_WHITESPACE)
    endif()
    include_directories(${PYTHON_SITE_PACKAGES}/dftracer/include)
    link_directories(${PYTHON_SITE_PACKAGES}/dftracer/lib)
endif()

# install dependency
option (DFTRACER_INSTALL_DEPENDENCIES "Install DFTracer dependencies" OFF)

# testing
option (DFTRACER_ENABLE_TESTS "Enable tests for DFTRACER." OFF)
option (DFTRACER_ENABLE_DLIO_BENCHMARK_TESTS "Enable dlio_benchmark tests" OFF)
option (DFTRACER_ENABLE_PAPER_TESTS "Enable paper tests" OFF)
set (DFTRACER_TEST_LD_LIBRARY_PATH "" CACHE STRING "Additional LD_LIBRARY_PATH to be included on testing")


option(DFTRACER_ENABLE_NATIVE_SCRIPT "Build with native scripting support." OFF)

#------------------------------------------------------------------------------
# Compiler setup
#------------------------------------------------------------------------------
include(ExternalProject)
# It requires include(GNUInstallDirs) before if that is needed
include(SetupCompiler)

#------------------------------------------------------------------------------
# Build Type based configurations
#------------------------------------------------------------------------------

if(CMAKE_BUILD_TYPE STREQUAL "")
    set(CMAKE_BUILD_TYPE "Release") # Setting default as Release
endif()

if (NOT DEFINED BUILD_SHARED_LIBS)
    set(BUILD_SHARED_LIBS ON)
endif ()
#------------------------------------------------------------------------------
# Disallow in-source build
#------------------------------------------------------------------------------
if ("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
    message(FATAL_ERROR
            "${PROJECT_NAME} requires an out of source Build. "
            "Please create a separate binary directory and run CMake there.")
endif ()
#------------------------------------------------------------------------------
# Set a default build type if none was specified
#------------------------------------------------------------------------------
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified.")
    set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build." FORCE)
    # Set the possible values of build type for cmake-gui
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
            "MinSizeRel" "RelWithDebInfo")
endif ()
#------------------------------------------------------------------------------
# Dependencies
#------------------------------------------------------------------------------

if (DFTRACER_INSTALL_DEPENDENCIES)
    message(STATUS "[${PROJECT_NAME}] downloading dependencies. Please run make for downloading depedencies and then do reconfigure without dependency flag.")
    message(STATUS "[${PROJECT_NAME}] Installing depedencies at ${CMAKE_INSTALL_PREFIX}")
    add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/dependency)
else()
# Required Dependencies
# =============================================================================
set(DEPENDENCY_LIB ${CMAKE_EXE_LINKER_FLAGS} $ENV{LDFLAGS}  -lpthread)
set(DEPENDENCY_LIBRARY_DIRS "")

find_package(cpp-logger 1.0.0 REQUIRED)
if (${cpp-logger_FOUND})
        include_directories(${CPP_LOGGER_INCLUDE_DIRS})
        set(DEPENDENCY_LIB ${DEPENDENCY_LIB} ${CPP_LOGGER_LIBRARIES})
        set(CPP_LOGGER_LIBRARY_DIR ${cpp-logger_DIR}/../../)
        # Get absolute path for CPP_LOGGER_LIBRARY_DIR
        get_filename_component(CPP_LOGGER_LIBRARY_DIR "${CPP_LOGGER_LIBRARY_DIR}" ABSOLUTE)
        if (DEFINED CPP_LOGGER_LIBRARY_DIR)
                list(APPEND DEPENDENCY_LIBRARY_DIRS ${CPP_LOGGER_LIBRARY_DIR})
        endif()
        message(STATUS "[CPP_LOGGER] found cpp-logger at ${CPP_LOGGER_INCLUDE_DIRS}")
else ()
        message(FATAL_ERROR "-- [CPP_LOGGER] is needed for ${PROJECT_NAME} build")
endif ()

find_package(brahma 2.1.0 REQUIRED)
if (${brahma_FOUND})
        message(STATUS "[BRAHMA] found brahma at ${BRAHMA_INCLUDE_DIRS}")
        include_directories(${BRAHMA_INCLUDE_DIRS})
        set(DEPENDENCY_LIB ${DEPENDENCY_LIB} ${BRAHMA_LIBRARIES})
        set(BRAHMA_LIBRARY_DIR ${brahma_DIR}/../../)
        # Get absolute path for BRAHMA_LIBRARY_DIR
        get_filename_component(BRAHMA_LIBRARY_DIR "${BRAHMA_LIBRARY_DIR}" ABSOLUTE)
        if (DEFINED BRAHMA_LIBRARY_DIR)
                list(APPEND DEPENDENCY_LIBRARY_DIRS ${BRAHMA_LIBRARY_DIR})
        endif()
else ()
        message(FATAL_ERROR "-- [BRAHMA] is needed for ${PROJECT_NAME} build")
endif ()

find_package(yaml-cpp REQUIRED)
if (${yaml-cpp_FOUND})
        message(STATUS "[DFTRACER] found yaml-cpp at ${YAML_CPP_INCLUDE_DIR}")
        include_directories(${YAML_CPP_INCLUDE_DIR})
        set(YAML_CPP_LIBRARY_DIR ${yaml-cpp_DIR}/../../)
        # Get absolute path for YAML_CPP_LIBRARY_DIR
        get_filename_component(YAML_CPP_LIBRARY_DIR "${YAML_CPP_LIBRARY_DIR}" ABSOLUTE)
        if (DEFINED YAML_CPP_LIBRARY_DIR)
                list(APPEND DEPENDENCY_LIBRARY_DIRS ${YAML_CPP_LIBRARY_DIR})
        endif()
        set(DEPENDENCY_LIB ${DEPENDENCY_LIB} -L${YAML_CPP_LIBRARY_DIR} ${YAML_CPP_LIBRARIES})
        list(APPEND DEPENDENCY_LIBRARY_DIRS ${YAML_CPP_LIBRARY_DIR})
else ()
        message(FATAL_ERROR "-- [DFTRACER] yaml-cpp is needed for ${PROJECT_NAME} build")
endif ()

# Optional Dependencies
# =============================================================================
if (DFTRACER_BUILD_PYTHON_BINDINGS)
        find_package(pybind11 REQUIRED)
        #add_subdirectory(dependency/py11bind)
endif()


if (DFTRACER_ENABLE_MPI)
  find_package(MPI COMPONENTS CXX REQUIRED)
  if (MPI_FOUND)
          message(STATUS "[DFTRACER] found mpi.h at ${MPI_CXX_INCLUDE_DIRS}")
          include_directories(${MPI_CXX_INCLUDE_DIRS})
          set(DEPENDENCY_LIB ${DEPENDENCY_LIB} ${MPI_CXX_LIBRARIES})
          # Get the directory of each MPI_CXX_LIBRARY and append to MPI_CXX_LIBRARY_DIRS
          set(MPI_CXX_LIBRARY_DIRS "")
          foreach(_lib ${MPI_CXX_LIBRARIES})
                  get_filename_component(_lib_dir "${_lib}" DIRECTORY)
                  if (_lib_dir)
                          list(APPEND MPI_CXX_LIBRARY_DIRS "${_lib_dir}")
                  endif()
          endforeach()
          list(REMOVE_DUPLICATES MPI_CXX_LIBRARY_DIRS)
          if (DEFINED MPI_CXX_LIBRARY_DIRS)
                  list(APPEND DEPENDENCY_LIBRARY_DIRS ${MPI_CXX_LIBRARY_DIRS})
          endif()
  else ()
          message(FATAL_ERROR "-- [DFTRACER] mpi is needed for ${PROJECT_NAME} build")
  endif ()
endif()

if (NOT DFTRACER_DISABLE_HWLOC)
        find_package(HWLOC REQUIRED)
        if (${HWLOC_FOUND})
                include_directories(${HWLOC_INCLUDE_DIRS})
                set(DEPENDENCY_LIB ${DEPENDENCY_LIB} ${HWLOC_LIBRARIES})
                if (DEFINED HWLOC_LIBDIR)
                        list(APPEND DEPENDENCY_LIBRARY_DIRS ${HWLOC_LIBDIR})
                endif()
                message(STATUS "[DFTRACER] found hwloc at ${HWLOC_INCLUDE_DIRS}")
        else ()
                message(FATAL_ERROR "-- [DFTRACER] hwloc is needed for ${PROJECT_NAME} build with -DDFTRACER_DISABLE_HWLOC-ON option.")
        endif ()
endif()

find_package(ZLIB REQUIRED)
if (ZLIB_FOUND)
                message(STATUS "[DFTRACER] found zlib at ${ZLIB_INCLUDE_DIRS}")
                include_directories(${ZLIB_INCLUDE_DIRS})
                set(DEPENDENCY_LIB ${DEPENDENCY_LIB} ${ZLIB_LIBRARIES})
                get_filename_component(ZLIB_LIBRARY "${ZLIB_LIBRARIES}" DIRECTORY)
                set(ZLIB_LIBRARY_DIR "${ZLIB_LIBRARY}")
                # Get absolute path for ZLIB_LIBRARY_DIR
                get_filename_component(ZLIB_LIBRARY_DIR "${ZLIB_LIBRARY_DIR}" ABSOLUTE)
                if (DEFINED ZLIB_LIBRARY_DIR)
                        list(APPEND DEPENDENCY_LIBRARY_DIRS ${ZLIB_LIBRARY_DIR})
                endif()
                if (DEFINED ZLIB_LIBRARY_DIRS)
                        list(APPEND DEPENDENCY_LIBRARY_DIRS ${ZLIB_LIBRARY_DIRS})
                endif()
else ()
                message(FATAL_ERROR "-- [DFTRACER] zlib is needed for ${PROJECT_NAME} build")
endif ()

# Set RPATH to all dependency library dirs
list(REMOVE_DUPLICATES DEPENDENCY_LIBRARY_DIRS)
set(CMAKE_INSTALL_RPATH "${DEPENDENCY_LIBRARY_DIRS}")
set(CMAKE_BUILD_RPATH "${DEPENDENCY_LIBRARY_DIRS}")
set(CMAKE_SKIP_BUILD_RPATH OFF)
set(CMAKE_BUILD_WITH_INSTALL_RPATH ON)
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH ON)

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
set(DFTRACER_PRELOAD_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/dftracer_preload.cpp)
set(DFTRACER_PRELOAD_PRIVATE_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/dftracer_preload.h)

set(DFTRACER_CORE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/brahma/posix.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/brahma/stdio.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/finstrument/functions.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/writer/chrome_writer.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/utils/posix_internal.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/dftracer.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/common/dftracer_main.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/utils/configuration_manager.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/utils/md5.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/df_logger.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/utils/utils.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/serialization/json_line.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/compression/zlib_compression.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/buffer/buffer.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/writer/stdio_writer.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/aggregator/aggregator.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/common/datastructure.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/aggregator/rules.cpp)
set(DFTRACER_CORE_PUBLIC_INCLUDE
        ${CMAKE_CURRENT_SOURCE_DIR}/include/dftracer/dftracer.h
        ${CMAKE_CURRENT_SOURCE_DIR}/include/dftracer/core/common/constants.h
        ${CMAKE_CURRENT_SOURCE_DIR}/include/dftracer/core/common/typedef.h)
set(DFTRACER_CORE_PRIVATE_INCLUDE
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/utils/posix_internal.h
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/utils/utils.h
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/utils/md5.h
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/finstrument/functions.h
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/brahma/posix.h
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/brahma/stdio.h
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/common/dftracer_main.h
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/common/logging.h
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/common/singleton.h
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/utils/configuration_manager.h
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/aggregator/aggregator.h
        ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/common/datastructure.h)
add_library(${PROJECT_NAME}_core SHARED)
target_compile_definitions(${PROJECT_NAME}_core PUBLIC DFTRACER_LOGGER_LEVEL_ERROR)
target_link_libraries(${PROJECT_NAME}_core ${DEPENDENCY_LIB})
add_library(${PROJECT_NAME}_core_dbg SHARED)
target_link_libraries(${PROJECT_NAME}_core_dbg ${DEPENDENCY_LIB})
add_library(${PROJECT_NAME}_preload SHARED)
target_link_libraries(${PROJECT_NAME}_preload ${DEPENDENCY_LIB} ${PROJECT_NAME}_core)
add_library(${PROJECT_NAME}_preload_dbg SHARED)
target_link_libraries(${PROJECT_NAME}_preload_dbg ${DEPENDENCY_LIB} ${PROJECT_NAME}_core_dbg)

set(DFTRACER_DEBUG_TARGETS ${PROJECT_NAME}_core_dbg ${PROJECT_NAME}_preload_dbg)


set(DFTRACER_SERVICE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/service/service.cpp)

add_executable(${PROJECT_NAME}_service ${DFTRACER_SERVICE_SRC})
target_compile_definitions(${PROJECT_NAME}_service PUBLIC DFTRACER_LOGGER_LEVEL_ERROR)
target_link_libraries(${PROJECT_NAME}_service ${DEPENDENCY_LIB} ${PROJECT_NAME}_core)

if (DFTRACER_BUILD_PYTHON_BINDINGS)
    pybind11_add_module(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/python/dftracer.cpp)
    target_compile_definitions(${PROJECT_NAME} PRIVATE VERSION_INFO=${EXAMPLE_VERSION_INFO})
    target_link_libraries(${PROJECT_NAME} PRIVATE ${DEPENDENCY_LIB} ${PROJECT_NAME}_core)

    pybind11_add_module(${PROJECT_NAME}_dbg NO_EXTRAS ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/python/dftracer_dbg.cpp)
    target_compile_definitions(${PROJECT_NAME}_dbg PRIVATE VERSION_INFO=${EXAMPLE_VERSION_INFO})
    target_link_libraries(${PROJECT_NAME}_dbg PRIVATE ${DEPENDENCY_LIB} ${PROJECT_NAME}_core_dbg)
    list(APPEND DFTRACER_DEBUG_TARGETS ${PROJECT_NAME}_dbg)
endif()
target_sources(${PROJECT_NAME}_core
        PRIVATE
        ${DFTRACER_CORE_SRC}
        )
target_sources(${PROJECT_NAME}_core_dbg
        PRIVATE
        ${DFTRACER_CORE_SRC}
        )
target_sources(${PROJECT_NAME}_preload
        PRIVATE
        ${DFTRACER_PRELOAD_SRC}
        )
target_sources(${PROJECT_NAME}_preload_dbg
        PRIVATE
        ${DFTRACER_PRELOAD_SRC}
        )
target_sources(${PROJECT_NAME}_service
        PRIVATE
        ${DFTRACER_SERVICE_SRC}
        )
target_include_directories(${PROJECT_NAME}_core
        PRIVATE
        # where the library itself will look for its internal headers
        ${CMAKE_CURRENT_SOURCE_DIR}/src
        PUBLIC
        # where top-level project will look for the library's public headers
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        # where external projects will look for the library's public headers
        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
        )
target_include_directories(${PROJECT_NAME}_core_dbg
        PRIVATE
        # where the library itself will look for its internal headers
        ${CMAKE_CURRENT_SOURCE_DIR}/src
        PUBLIC
        # where top-level project will look for the library's public headers
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        # where external projects will look for the library's public headers
        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
        )
set_target_properties(${PROJECT_NAME}_core
        PROPERTIES
        DEBUG_OUTPUT_NAME ${PROJECT_NAME}_core
        RELEASE_OUTPUT_NAME ${PROJECT_NAME}_core
        MINSIZEREL_OUTPUT_NAME ${PROJECT_NAME}_core
        RELWITHDEBINFO_OUTPUT_NAME ${PROJECT_NAME}_core
        VERSION ${DFTRACER_PACKAGE_VERSION}
        SOVERSION ${DFTRACER_PACKAGE_VERSION}
        )

dftracer_debug_config("${DFTRACER_DEBUG_TARGETS}")
dftracer_install_headers("${DFTRACER_CORE_PUBLIC_INCLUDE}")
install(
        TARGETS ${PROJECT_NAME}_core
        EXPORT ${DFTRACER_EXPORTED_TARGETS}
        LIBRARY DESTINATION ${DFTRACER_INSTALL_LIB_DIR}
        ARCHIVE DESTINATION ${DFTRACER_INSTALL_LIB_DIR}
        RUNTIME DESTINATION ${DFTRACER_INSTALL_BIN_DIR}
)
install(
          TARGETS ${PROJECT_NAME}_core_dbg
          EXPORT ${DFTRACER_EXPORTED_TARGETS}
          LIBRARY DESTINATION ${DFTRACER_INSTALL_LIB_DIR}
          ARCHIVE DESTINATION ${DFTRACER_INSTALL_LIB_DIR}
          RUNTIME DESTINATION ${DFTRACER_INSTALL_BIN_DIR}
  )
  install(
          TARGETS ${PROJECT_NAME}_service
          EXPORT ${DFTRACER_EXPORTED_TARGETS}
          LIBRARY DESTINATION ${DFTRACER_INSTALL_LIB_DIR}
          ARCHIVE DESTINATION ${DFTRACER_INSTALL_LIB_DIR}
          RUNTIME DESTINATION ${DFTRACER_INSTALL_BIN_DIR}
  )
install(TARGETS ${PROJECT_NAME}_preload
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
        PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(TARGETS ${PROJECT_NAME}_preload_dbg
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
        PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

install(
    TARGETS
    ${PROJECT_NAME}_preload
    EXPORT
    ${DFTRACER_EXPORTED_TARGETS}
    LIBRARY DESTINATION ${DFTRACER_INSTALL_LIB_DIR}
    ARCHIVE DESTINATION ${DFTRACER_INSTALL_LIB_DIR}
    RUNTIME DESTINATION ${DFTRACER_INSTALL_BIN_DIR}
)
install(
    TARGETS
    ${PROJECT_NAME}_preload_dbg
    EXPORT
    ${DFTRACER_EXPORTED_TARGETS}
    LIBRARY DESTINATION ${DFTRACER_INSTALL_LIB_DIR}
    ARCHIVE DESTINATION ${DFTRACER_INSTALL_LIB_DIR}
    RUNTIME DESTINATION ${DFTRACER_INSTALL_BIN_DIR}
)
if (DFTRACER_BUILD_PYTHON_BINDINGS)
    install(TARGETS ${PROJECT_NAME}
            LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
            PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
    file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/symlink.sh CONTENT "echo -- Installing: symlink ${MAIN_PYTHON_SITE_PACKAGES}/$<TARGET_FILE_NAME:${PROJECT_NAME}> from ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/$<TARGET_FILE_NAME:${PROJECT_NAME}>;ln -sf ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/$<TARGET_FILE_NAME:${PROJECT_NAME}> ${MAIN_PYTHON_SITE_PACKAGES}/dftracer/$<TARGET_FILE_NAME:${PROJECT_NAME}>")
    install(CODE "execute_process(
                COMMAND bash -c \"set -e
                chmod +x ${CMAKE_BINARY_DIR}/symlink.sh
                . ${CMAKE_BINARY_DIR}/symlink.sh
                \")")

    install(TARGETS ${PROJECT_NAME}_dbg
            LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
            PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
    file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/symlink_dbg.sh CONTENT "echo -- Installing: symlink ${MAIN_PYTHON_SITE_PACKAGES}/$<TARGET_FILE_NAME:${PROJECT_NAME}_dbg> from ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/$<TARGET_FILE_NAME:${PROJECT_NAME}_dbg>;ln -sf ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/$<TARGET_FILE_NAME:${PROJECT_NAME}_dbg> ${MAIN_PYTHON_SITE_PACKAGES}/dftracer/$<TARGET_FILE_NAME:${PROJECT_NAME}_dbg>")
    install(CODE "execute_process(
                COMMAND bash -c \"set -e
                chmod +x ${CMAKE_BINARY_DIR}/symlink_dbg.sh
                . ${CMAKE_BINARY_DIR}/symlink_dbg.sh
                \")")
endif()

if(DFTRACER_ENABLE_NATIVE_SCRIPT)
        configure_file(${CMAKE_CURRENT_SOURCE_DIR}/script/dftracer_compact ${EXECUTABLE_OUTPUT_PATH}/dftracer_compact COPYONLY)
        install(
                FILES
                ${EXECUTABLE_OUTPUT_PATH}/dftracer_compact
                DESTINATION
                bin
        )

        configure_file(${CMAKE_CURRENT_SOURCE_DIR}/script/dftracer_compact_by_pid ${EXECUTABLE_OUTPUT_PATH}/dftracer_compact_by_pid COPYONLY)
        install(
                FILES
                ${EXECUTABLE_OUTPUT_PATH}/dftracer_compact_by_pid
                DESTINATION
                bin
        )

        configure_file(${CMAKE_CURRENT_SOURCE_DIR}/script/dftracer_merge ${EXECUTABLE_OUTPUT_PATH}/dftracer_merge COPYONLY)
        install(
                FILES
                ${EXECUTABLE_OUTPUT_PATH}/dftracer_merge
                DESTINATION
                bin
        )

        configure_file(${CMAKE_CURRENT_SOURCE_DIR}/script/dftracer_sanitize ${EXECUTABLE_OUTPUT_PATH}/dftracer_sanitize COPYONLY)
        install(
                FILES
                ${EXECUTABLE_OUTPUT_PATH}/dftracer_sanitize
                DESTINATION
                bin
        )

        configure_file(${CMAKE_CURRENT_SOURCE_DIR}/script/dftracer_anonymize ${EXECUTABLE_OUTPUT_PATH}/dftracer_anonymize COPYONLY)
        install(
                FILES
                ${EXECUTABLE_OUTPUT_PATH}/dftracer_anonymize
                DESTINATION
                bin
        )

        configure_file(${CMAKE_CURRENT_SOURCE_DIR}/script/dftracer_create_index ${EXECUTABLE_OUTPUT_PATH}/dftracer_create_index COPYONLY)
        install(
                FILES
                ${EXECUTABLE_OUTPUT_PATH}/dftracer_create_index
                DESTINATION
                bin
        )


        configure_file(${CMAKE_CURRENT_SOURCE_DIR}/script/dftracer_pgzip ${EXECUTABLE_OUTPUT_PATH}/dftracer_pgzip COPYONLY)
        install(
                FILES
                ${EXECUTABLE_OUTPUT_PATH}/dftracer_pgzip
                DESTINATION
                bin
        )

        configure_file(${CMAKE_CURRENT_SOURCE_DIR}/script/dftracer_split ${EXECUTABLE_OUTPUT_PATH}/dftracer_split COPYONLY)
        install(
                FILES
                ${EXECUTABLE_OUTPUT_PATH}/dftracer_split
                DESTINATION
                bin
        )

        configure_file(${CMAKE_CURRENT_SOURCE_DIR}/script/dftracer_event_count ${EXECUTABLE_OUTPUT_PATH}/dftracer_event_count COPYONLY)
        install(
                FILES
                ${EXECUTABLE_OUTPUT_PATH}/dftracer_event_count
                DESTINATION
                bin
        )

        configure_file(${CMAKE_CURRENT_SOURCE_DIR}/script/dftracer_validate ${EXECUTABLE_OUTPUT_PATH}/dftracer_validate COPYONLY)
        install(
                FILES
                ${EXECUTABLE_OUTPUT_PATH}/dftracer_validate
                DESTINATION
                bin
        )
endif()

configure_file(${CMAKE_CURRENT_SOURCE_DIR}/script/git_sparse_clone_interactive ${EXECUTABLE_OUTPUT_PATH}/git_sparse_clone_interactive COPYONLY)
install(
        FILES
        ${EXECUTABLE_OUTPUT_PATH}/git_sparse_clone_interactive
        DESTINATION
        bin
)

configure_file(${CMAKE_CURRENT_SOURCE_DIR}/script/git_sparse_push_interactive ${EXECUTABLE_OUTPUT_PATH}/git_sparse_push_interactive COPYONLY)
install(
        FILES
        ${EXECUTABLE_OUTPUT_PATH}/git_sparse_push_interactive
        DESTINATION
        bin
)

#cmake_policy(SET CMP0079 NEW) # In case that we need more control over the target building order

if(DFTRACER_ENABLE_TESTS)
    message("-- " "[DFTracer] Enabling Testing")
    if(CMAKE_BUILD_TYPE STREQUAL "PROFILE")
        message("-- " "[DFTracer] building with CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE}")
        set(CMAKE_C_FLAGS_PROFILE --coverage)
        set(CMAKE_CXX_FLAGS_PROFILE --coverage)
    endif()
    enable_testing()
    add_subdirectory(test)
    if(CMAKE_BUILD_TYPE STREQUAL "PROFILE")
        get_property(COVERAGE_FILE_LIST GLOBAL PROPERTY COVERAGE_FILES)
        add_custom_command(OUTPUT default.profdata COMMAND llvm-profdata merge -sparse ${COVERAGE_FILE_LIST} -o default.profdata VERBATIM)
        add_custom_target(merged_coverage DEPENDS default.profdata)
    endif()
endif()

#-----------------------------------------------------------------------------
# Configure the config.cmake file for the build directory
#-----------------------------------------------------------------------------
include(CMakePackageConfigHelpers)
configure_package_config_file(
        ${CMAKE_CURRENT_SOURCE_DIR}/cmake/configure_files/${PROJECT_NAME}-config.cmake.build.in
        "${CMAKE_BINARY_DIR}/${PROJECT_NAME}-config.cmake"
        INSTALL_DESTINATION  ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/cmake/${PROJECT_NAME}/${PROJECT_NAME}-config.cmake
        PATH_VARS CMAKE_BINARY_DIR
)

configure_package_config_file(
  ${CMAKE_CURRENT_SOURCE_DIR}/cmake/configure_files/${PROJECT_NAME}-config.cmake.install.in
        "${CMAKE_BINARY_DIR}/install/${PROJECT_NAME}-config.cmake"
        INSTALL_DESTINATION  ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/cmake/${PROJECT_NAME}/install/${PROJECT_NAME}-config.cmake
        PATH_VARS CMAKE_BINARY_DIR
)
install(
        FILES
        ${CMAKE_BINARY_DIR}/install/${PROJECT_NAME}-config.cmake
        DESTINATION
        ${DFTRACER_LIBDIR}/cmake/${PROJECT_NAME}
)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/dftracer-utils.cmake"
        DESTINATION "${DFTRACER_LIBDIR}/cmake/dftracer")
#-----------------------------------------------------------------------------
# Configure the ${PROJECT_NAME}-config-version .cmake file for the install directory
#-----------------------------------------------------------------------------

install(
        FILES
        ${CMAKE_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake
        DESTINATION
        ${DFTRACER_LIBDIR}/cmake/${PROJECT_NAME}
)

export(EXPORT ${DFTRACER_EXPORTED_TARGETS}
        FILE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/cmake/${PROJECT_NAME}/${PROJECT_NAME}Targets.cmake")

# Write the configure file
configure_file("${CMAKE_SOURCE_DIR}/cmake/configure_files/dftracer_config.hpp.in"
               "${CMAKE_INCLUDE_OUTPUT_DIRECTORY}/dftracer/core/dftracer_config.hpp" @ONLY)

configure_file("${CMAKE_SOURCE_DIR}/cmake/configure_files/dftracer_config_dbg.hpp.in"
               "${CMAKE_INCLUDE_OUTPUT_DIRECTORY}/dftracer/core/dftracer_config_dbg.hpp" @ONLY)

################################################################
# Install DFTRACER
################################################################

include(CMakePackageConfigHelpers)

# Write the version file. This is independent of build/install tree.
write_basic_package_version_file(
  dftracer-config-version.cmake
  VERSION "${DFTRACER_PACKAGE_VERSION}"
  COMPATIBILITY SameMajorVersion)

install(FILES
        "${CMAKE_INCLUDE_OUTPUT_DIRECTORY}/dftracer/core/dftracer_config.hpp"
        DESTINATION
        ${DFTRACER_INSTALL_INCLUDE_DIR}/dftracer/core)

install(FILES
        "${CMAKE_INCLUDE_OUTPUT_DIRECTORY}/dftracer/core/dftracer_config_dbg.hpp"
        DESTINATION
        ${DFTRACER_INSTALL_INCLUDE_DIR}/dftracer/core)

install(EXPORT
        ${DFTRACER_EXPORTED_TARGETS}
        DESTINATION
        ${DFTRACER_LIBDIR}/cmake/${PROJECT_NAME}
        FILE
        ${DFTRACER_EXPORTED_TARGETS}.cmake
        )

# Install license and readme
install(FILES
        "${PROJECT_SOURCE_DIR}/LICENSE"
        "${PROJECT_SOURCE_DIR}/README.md"
        DESTINATION
        ${DFTRACER_INSTALL_DOCDIR})

###############################################################################
#                      Print out configuration summary
###############################################################################

if (CMAKE_HOST_SYSTEM_NAME MATCHES "Linux")
  set(DFTRACER_GNU_LINUX TRUE)
endif ()

# NOTE: message() outputs to stderr by default. We now use a string to
# maintain this information and then have cmake echo it to stdout. The
# only side effects are that if you use the CMake GUI, you won't see
# this output anymore (they only report stderr) and that if you add
# something to the list, you must remember your newline!
set(_str "\n== DFTRACER Configuration Summary ==\n\n")
string(APPEND _str "  PROJECT_SOURCE_DIR:   ${PROJECT_SOURCE_DIR}\n"
  "  PROJECT_BINARY_DIR:   ${PROJECT_BINARY_DIR}\n\n"
  "  CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}\n"
  "  CMAKE_BUILD_TYPE:     ${CMAKE_BUILD_TYPE}\n\n")
if (CMAKE_BUILD_TYPE MATCHES None)
  string(APPEND _str
    "  CXX FLAGS:            ${CMAKE_CXX_FLAGS}\n")
elseif (CMAKE_BUILD_TYPE MATCHES Release)
  string(APPEND _str
    "  CXX FLAGS:            ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}\n")
  string(APPEND _str
    "  C FLAGS:              ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_RELEASE}\n")
elseif (CMAKE_BUILD_TYPE MATCHES RelWithDebInfo)
  string(APPEND _str
    "  CXX FLAGS:            ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}\n")
  string(APPEND _str
    "  C FLAGS:              ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_RELWITHDEBINFO}\n")
elseif (CMAKE_BUILD_TYPE MATCHES Debug)
  string(APPEND _str
    "  CXX FLAGS:            ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_DEBUG}\n")
  string(APPEND _str
    "  C FLAGS:              ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_DEBUG}\n")
endif ()
string(APPEND _str "\n")

string(APPEND _str
  "  DFTRACER_PACKAGE_VERSION:        ${DFTRACER_PACKAGE_VERSION}\n")
string(APPEND _str
  "  DFTRACER_GIT_VERSION:            ${DFTRACER_GIT_VERSION}\n")
string(APPEND _str
  "  GLIBC_VERSION:               ${GLIBC_VERSION}\n")
  string(APPEND _str
  "  DFTRACER_PYTHON_EXE:               ${DFTRACER_PYTHON_EXE}\n")
  string(APPEND _str
  "  DFTRACER_PYTHON_SITE:               ${DFTRACER_PYTHON_SITE}\n")
  string(APPEND _str
  "  MAIN_PYTHON_SITE_PACKAGES:               ${MAIN_PYTHON_SITE_PACKAGES}\n")
  string(APPEND _str
  "  DEPENDENCY_LIB:               ${DEPENDENCY_LIB}\n")
  string(APPEND _str
  "  CMAKE_BUILD_RPATH:               ${CMAKE_BUILD_RPATH}\n")
  string(APPEND _str
  "  CMAKE_INSTALL_RPATH:             ${CMAKE_INSTALL_RPATH}\n")
  
  
#Print the true/false variables
append_str_tf(_str
  DFTRACER_GNU_LINUX
  DFTRACER_LIBDIR_AS_LIB
  DFTRACER_ENABLE_FTRACING
  DFTRACER_ENABLE_MPI
  DFTRACER_DISABLE_HWLOC
  DFTRACER_USE_CLANG_LIBCXX
  DFTRACER_WARNINGS_AS_ERRORS
  DFTRACER_BUILD_PYTHON_BINDINGS
  DFTRACER_ENABLE_TESTS
  DFTRACER_ENABLE_DLIO_BENCHMARK_TESTS
  DFTRACER_ENABLE_PAPER_TESTS
)
string(APPEND _str
  "\n== End DFTRACER Configuration Summary ==\n")

# Output to stdout
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${_str}")
set(_str)

###############################################################################
# Write a basic modulefile
###############################################################################

set(DFTRACER_MODULEFILE_NAME "dftracer-${DFTRACER_PACKAGE_VERSION}.lua")
#  CACHE STRING
#  "The name of the DFTRACER modulefile to install. Must end in .lua.")

if (NOT (DFTRACER_MODULEFILE_NAME MATCHES ".+\.lua"))
  message(WARNING
    "DFTRACER_MODULEFILE_NAME must have extension \".lua\". Appending.")
  set(DFTRACER_MODULEFILE_NAME "${DFTRACER_MODULEFILE_NAME}.lua"
    CACHE STRING "" FORCE)
endif ()

configure_file(
  "${CMAKE_SOURCE_DIR}/cmake/configure_files/dftracer_module.lua.in"
  "${CMAKE_BINARY_DIR}/dftracer_module.lua.install"
  @ONLY)

install(FILES "${CMAKE_BINARY_DIR}/dftracer_module.lua.install"
  RENAME "${DFTRACER_MODULEFILE_NAME}"
  DESTINATION
  "${DFTRACER_INSTALL_SYSCONFDIR}")

endif()
