cmake_minimum_required(VERSION 3.20)

# ------------------------------------------------------------------------------
# Version information - Read from VERSION and PACKAGE_VERSION files
# ------------------------------------------------------------------------------
# Read library version from VERSION file (e.g., 4.0.0)
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" DFTRACER_LIBRARY_VERSION_STRING)
string(STRIP "${DFTRACER_LIBRARY_VERSION_STRING}" DFTRACER_LIBRARY_VERSION_STRING)

# Parse library version components
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)" _ ${DFTRACER_LIBRARY_VERSION_STRING})
set(DFTRACER_VERSION_MAJOR "${CMAKE_MATCH_1}")
set(DFTRACER_VERSION_MINOR "${CMAKE_MATCH_2}")
set(DFTRACER_VERSION_PATCH "${CMAKE_MATCH_3}")
set(DFTRACER_LIBRARY_VERSION "${DFTRACER_VERSION_MAJOR}.${DFTRACER_VERSION_MINOR}.${DFTRACER_VERSION_PATCH}")

# Read Python package version from PACKAGE_VERSION file (e.g., 2.0.2)
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/PACKAGE_VERSION" DFTRACER_PACKAGE_VERSION_STRING)
string(STRIP "${DFTRACER_PACKAGE_VERSION_STRING}" DFTRACER_PACKAGE_VERSION_STRING)

# Parse package version components
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)" _ ${DFTRACER_PACKAGE_VERSION_STRING})
set(DFTRACER_PACKAGE_VERSION_MAJOR "${CMAKE_MATCH_1}")
set(DFTRACER_PACKAGE_VERSION_MINOR "${CMAKE_MATCH_2}")
set(DFTRACER_PACKAGE_VERSION_PATCH "${CMAKE_MATCH_3}")

# Set package version as tuple string (used by setuptools-scm and Python bindings)
set(DFTRACER_PACKAGE_VERSION "(${DFTRACER_PACKAGE_VERSION_MAJOR}, ${DFTRACER_PACKAGE_VERSION_MINOR}, ${DFTRACER_PACKAGE_VERSION_PATCH})")

# Set legacy package variables (for backward compatibility)
set(DFTRACER_PACKAGE ${PROJECT_NAME})
set(DFTRACER_PACKAGE_NAME ${PROJECT_NAME})
set(DFTRACER_PACKAGE_VERSION_FULL "${DFTRACER_PACKAGE_VERSION_MAJOR}.${DFTRACER_PACKAGE_VERSION_MINOR}.${DFTRACER_PACKAGE_VERSION_PATCH}")
set(DFTRACER_PACKAGE_VERSION_SHORT "${DFTRACER_PACKAGE_VERSION_MAJOR}.${DFTRACER_PACKAGE_VERSION_MINOR}")
set(DFTRACER_PACKAGE_STRING "${DFTRACER_PACKAGE_NAME} ${DFTRACER_PACKAGE_VERSION_FULL}")
set(DFTRACER_PACKAGE_TARNAME "${DFTRACER_PACKAGE}")

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_LIB_DIR
                ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
        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_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
        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_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_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()

# Check optional dependencies
option (DFTRACER_DISABLE_HWLOC "Disable HWLOC" ON)
if (NOT DFTRACER_DISABLE_HWLOC)
    set(DFTRACER_HWLOC_ENABLE 1)
endif()
option (DFTRACER_ENABLE_HIP_TRACING "Enable HIP Tracing" OFF)
if (DFTRACER_ENABLE_HIP_TRACING)
    set(DFTRACER_HIP_TRACING_ENABLE 1)
endif()
option (DFTRACER_ENABLE_MPI "Enable MPI" OFF)
if (DFTRACER_ENABLE_MPI)
    set(DFTRACER_MPI_ENABLE 1)
endif()
option (DFTRACER_ENABLE_DYNAMIC_DETECTION "Enable Dynamic Detection" OFF)

# 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 3.0.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()
        find_package(MPI COMPONENTS CXX QUIET)
        if (MPI_CXX_FOUND AND (DFTRACER_ENABLE_MPI OR DFTRACER_ENABLE_DYNAMIC_DETECTION))
                message(STATUS "[${PROJECT_NAME}] MPI found at ${MPI_CXX_INCLUDE_DIRS}.")
                set(DFTRACER_MPI_ENABLE 1)
                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(STATUS "[${PROJECT_NAME}] MPI not found.")
                set(DFTRACER_MPI_ENABLE 0)
        endif ()


        find_package(HWLOC QUIET)

        if (HWLOC_FOUND AND (NOT DFTRACER_DISABLE_HWLOC OR DFTRACER_ENABLE_DYNAMIC_DETECTION))
                set(DFTRACER_HWLOC_ENABLE 1)
                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(STATUS "[DFTRACER] hwloc not found")
                set(DFTRACER_HWLOC_ENABLE 0)
        endif ()


        find_package(rocprofiler-sdk QUIET)
        if (rocprofiler-sdk_FOUND AND (DFTRACER_ENABLE_HIP_TRACING OR DFTRACER_ENABLE_DYNAMIC_DETECTION))
                set(DFTRACER_HIP_TRACING_ENABLE 1)
                message(STATUS "[DFTRACER] found rocprofiler-sdk at ${rocprofiler-sdk_INCLUDE_DIR}")
                set(HIP_TRACING_LIBRARIES rocprofiler-sdk::rocprofiler-sdk)
                set(DEPENDENCY_LIB ${DEPENDENCY_LIB} ${HIP_TRACING_LIBRARIES})
        else()
                message(STATUS "-- [DFTRACER] rocprofiler-sdk is not found.")
        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 ()

        # print_all_variables()

        # 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/function/hip/intercept.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
                ${CMAKE_CURRENT_SOURCE_DIR}/include/dftracer/core/common/cpp_typedefs.h
                ${CMAKE_CURRENT_SOURCE_DIR}/include/dftracer/core/common/enumeration.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/function/generic_function.h
                ${CMAKE_CURRENT_SOURCE_DIR}/src/dftracer/core/function/hip/intercept.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)
        add_library(dftracer::dftracer_core ALIAS ${PROJECT_NAME}_core)
        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)
        add_library(dftracer::dftracer_core_dbg ALIAS ${PROJECT_NAME}_core_dbg)
        target_link_libraries(${PROJECT_NAME}_core_dbg ${DEPENDENCY_LIB})
        add_library(${PROJECT_NAME}_preload SHARED)
        add_library(dftracer::dftracer_preload ALIAS ${PROJECT_NAME}_preload)
        target_link_libraries(${PROJECT_NAME}_preload ${DEPENDENCY_LIB} ${PROJECT_NAME}_core)
        add_library(${PROJECT_NAME}_preload_dbg SHARED)
        add_library(dftracer::dftracer_preload_dbg ALIAS ${PROJECT_NAME}_preload_dbg)
        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})
        add_executable(dftracer::dftracer_service ALIAS ${PROJECT_NAME}_service)
        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_LIBRARY_VERSION}
                SOVERSION ${DFTRACER_LIBRARY_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 "mkdir -p ${MAIN_PYTHON_SITE_PACKAGES}/dftracer;echo -- Installing: symlink ${MAIN_PYTHON_SITE_PACKAGES}/dftracer/$<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 "mkdir -p ${MAIN_PYTHON_SITE_PACKAGES}/dftracer;echo -- Installing: symlink ${MAIN_PYTHON_SITE_PACKAGES}/dftracer/$<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)

                        # Find gcovr for coverage reporting
                        find_program(GCOVR_EXECUTABLE gcovr)

                        if(GCOVR_EXECUTABLE)
                                message(STATUS "Found gcovr: ${GCOVR_EXECUTABLE}")
                        else()
                                message(WARNING "gcovr not found. Coverage reports will not be available.")
                        endif()
                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)

                        # Add coverage target for easy local C/C++ coverage generation
                        if(GCOVR_EXECUTABLE)
                                add_custom_target(coverage
                                        COMMAND ${CMAKE_COMMAND} -E echo "Generating C/C++ coverage reports..."
                                        COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/coverage
                                        COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/coverage/html

                                        # Generate HTML report with details
                                        COMMAND ${GCOVR_EXECUTABLE}
                                        -r ${CMAKE_SOURCE_DIR}
                                        ${CMAKE_BINARY_DIR}
                                        --html-details ${CMAKE_BINARY_DIR}/coverage/html/index.html
                                        -e ${CMAKE_SOURCE_DIR}/test/
                                        -e ${CMAKE_SOURCE_DIR}/examples/
                                        -e ${CMAKE_SOURCE_DIR}/dependency/
                                        -e ${CMAKE_SOURCE_DIR}/build/
                                        --exclude-unreachable-branches
                                        --exclude-throw-branches

                                        # Generate XML for CI
                                        COMMAND ${GCOVR_EXECUTABLE}
                                        -r ${CMAKE_SOURCE_DIR}
                                        ${CMAKE_BINARY_DIR}
                                        --xml ${CMAKE_BINARY_DIR}/coverage/coverage.xml
                                        -e ${CMAKE_SOURCE_DIR}/test/
                                        -e ${CMAKE_SOURCE_DIR}/examples/
                                        -e ${CMAKE_SOURCE_DIR}/dependency/
                                        -e ${CMAKE_SOURCE_DIR}/build/
                                        --exclude-unreachable-branches
                                        --exclude-throw-branches

                                        # Generate Coveralls JSON for CI
                                        COMMAND ${GCOVR_EXECUTABLE}
                                        -r ${CMAKE_SOURCE_DIR}
                                        ${CMAKE_BINARY_DIR}
                                        --coveralls ${CMAKE_BINARY_DIR}/coverage/coveralls.json
                                        -e ${CMAKE_SOURCE_DIR}/test/
                                        -e ${CMAKE_SOURCE_DIR}/examples/
                                        -e ${CMAKE_SOURCE_DIR}/dependency/
                                        -e ${CMAKE_SOURCE_DIR}/build/
                                        --exclude-unreachable-branches
                                        --exclude-throw-branches

                                        # Print summary
                                        COMMAND ${GCOVR_EXECUTABLE}
                                        -r ${CMAKE_SOURCE_DIR}
                                        ${CMAKE_BINARY_DIR}
                                        -e ${CMAKE_SOURCE_DIR}/test/
                                        -e ${CMAKE_SOURCE_DIR}/examples/
                                        -e ${CMAKE_SOURCE_DIR}/dependency/
                                        -e ${CMAKE_SOURCE_DIR}/build/
                                        --exclude-unreachable-branches
                                        --exclude-throw-branches
                                        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
                                        COMMENT "Generating coverage reports in ${CMAKE_BINARY_DIR}/coverage/"
                                        VERBATIM
                                )

                                message(STATUS "Coverage target added. Run 'make coverage' after running tests.")
                        endif()
                endif()
        endif()

        # -----------------------------------------------------------------------------
        # Configure the unified config.cmake file for both build and install trees
        # -----------------------------------------------------------------------------
        include(CMakePackageConfigHelpers)

        # Single unified config file that auto-detects build vs install tree
        # Generate directly in build tree location: lib/cmake/dftracer/
        configure_package_config_file(
                ${CMAKE_CURRENT_SOURCE_DIR}/cmake/configure_files/${PROJECT_NAME}-config.cmake.in
                "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/cmake/${PROJECT_NAME}/${PROJECT_NAME}-config.cmake"
                INSTALL_DESTINATION ${DFTRACER_LIBDIR}/cmake/${PROJECT_NAME}
                PATH_VARS
                DFTRACER_INSTALL_INCLUDE_DIR
                DFTRACER_INSTALL_LIB_DIR
                DFTRACER_LIBDIR
        )

        # Install the config file to install tree: lib/cmake/dftracer/
        install(
                FILES "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/cmake/${PROJECT_NAME}/${PROJECT_NAME}-config.cmake"
                DESTINATION ${DFTRACER_LIBDIR}/cmake/${PROJECT_NAME}
        )

        # Copy dftracer-utils.cmake and dftracer-config-version.cmake to build and install locations
        file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/dftracer-utils.cmake"
                DESTINATION "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/cmake/dftracer")

        install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/dftracer-utils.cmake"
                DESTINATION "${DFTRACER_LIBDIR}/cmake/dftracer")
        install(FILES "${CMAKE_BINARY_DIR}/${DFTRACER_LIBDIR}/cmake/${PROJECT_NAME}/dftracer-config-version.cmake"
                DESTINATION "${DFTRACER_LIBDIR}/cmake/dftracer")

        # -----------------------------------------------------------------------------
        # Configure the ${PROJECT_NAME}-config-version .cmake file for the install directory
        # -----------------------------------------------------------------------------

        # Always export targets as dftracer-targets.cmake for downstream find_package
        export(EXPORT ${DFTRACER_EXPORTED_TARGETS}
                FILE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/cmake/${PROJECT_NAME}/dftracer-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(
                ${CMAKE_BINARY_DIR}/${DFTRACER_LIBDIR}/cmake/${PROJECT_NAME}/dftracer-config-version.cmake
                VERSION "${DFTRACER_LIBRARY_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-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_HWLOC_ENABLE
        DFTRACER_HIP_TRACING_ENABLE
        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_LIBRARY_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()
