###################################################################
# CMake build script for OpenCSD
# Default mode mirrors the full project Unix makefile target set and output layout.
###################################################################
cmake_minimum_required(VERSION 3.10)

set(OPENCSD_VERSION_HEADER "${CMAKE_CURRENT_LIST_DIR}/decoder/include/opencsd/ocsd_if_version.h")
file(READ "${OPENCSD_VERSION_HEADER}" OPENCSD_VERSION_HEADER_CONTENTS)

foreach(component MAJOR MINOR PATCH)
    string(REGEX MATCH "#define[ \t]+OCSD_VER_${component}[ \t]+(0[xX][0-9A-Fa-f]+|[0-9]+)"
        OPENCSD_VER_${component}_MATCH
        "${OPENCSD_VERSION_HEADER_CONTENTS}"
    )
    if(NOT OPENCSD_VER_${component}_MATCH)
        message(FATAL_ERROR "Could not find OCSD_VER_${component} in ${OPENCSD_VERSION_HEADER}")
    endif()
    set(OPENCSD_VER_${component}_RAW "${CMAKE_MATCH_1}")
    math(EXPR OPENCSD_VER_${component}_DEC "${OPENCSD_VER_${component}_RAW}")
endforeach()

set(OPENCSD_PROJECT_VERSION
    "${OPENCSD_VER_MAJOR_DEC}.${OPENCSD_VER_MINOR_DEC}.${OPENCSD_VER_PATCH_DEC}"
)

project(OpenCSD VERSION ${OPENCSD_PROJECT_VERSION} LANGUAGES C CXX)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)

if(UNIX AND CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
    # Match the Unix makefile install layout unless the caller overrides it.
    set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "Install path prefix." FORCE)
endif()

if(UNIX AND NOT DEFINED CMAKE_INSTALL_LIBDIR)
    # Keep the Unix CMake install layout aligned with the existing makefile install tree.
    set(CMAKE_INSTALL_LIBDIR "lib" CACHE PATH "Object code libraries.")
endif()

option(OPENCSD_BUILD_MIN_LIB_STATIC "Build the minimal static-library-oriented target set for integration into larger environments." OFF)

if(NOT DEFINED BUILD_TESTING)
    if(OPENCSD_BUILD_MIN_LIB_STATIC)
        set(BUILD_TESTING OFF CACHE BOOL "Build the testing tree." FORCE)
    else()
        set(BUILD_TESTING ON CACHE BOOL "Build the testing tree." FORCE)
    endif()
endif()

include(GNUInstallDirs)
include(CTest)

option(OPENCSD_FULL_PROJECT_LAYOUT "Write outputs into decoder/lib|tests/*/builddir for the full project build." ON)
option(OPENCSD_BUILD_DEV_TARGETS "Build full project development-only targets." OFF)

if(MSVC)
    if(OPENCSD_BUILD_MIN_LIB_STATIC)
        set(OPENCSD_C_FLAGS)
        set(OPENCSD_CXX_FLAGS
            /EHsc
            /W0
            /clang:-Wno-invalid-token-paste
        )
        set(OPENCSD_LINK_FLAGS)
    else()
        set(OPENCSD_C_FLAGS
            /W0
        )
        set(OPENCSD_CXX_FLAGS
            /EHsc
            /W0
            /clang:-Wno-invalid-token-paste
        )
        set(OPENCSD_LINK_FLAGS)
    endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR
       CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR
       CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
    if(OPENCSD_BUILD_MIN_LIB_STATIC)
        set(OPENCSD_C_FLAGS)
        set(OPENCSD_CXX_FLAGS
            -Wall
            -Wno-switch
            -Wno-deprecated-declarations
            -Wno-unused-variable
            -Wno-reorder
            -Wno-invalid-token-paste
            -fexceptions
        )
        set(OPENCSD_LINK_FLAGS)
    else()
        if(APPLE)
            set(OPENCSD_LOGICAL_OP_FLAG)
        else()
            set(OPENCSD_LOGICAL_OP_FLAG
                -Wlogical-op
            )
        endif()
        set(OPENCSD_C_FLAGS
            -Wall
            -Wno-switch
            ${OPENCSD_LOGICAL_OP_FLAG}
        )
        set(OPENCSD_CXX_FLAGS
            -Wall
            -Wno-switch
            -Wno-deprecated-declarations
            -Wno-unused-variable
            -Wno-reorder
            -Wno-invalid-token-paste
            ${OPENCSD_LOGICAL_OP_FLAG}
            -fexceptions
        )
        if(APPLE)
            set(OPENCSD_LINK_FLAGS)
        else()
            set(OPENCSD_LINK_FLAGS
                -Wl,-z,defs
            )
        endif()
    endif()
else()
    set(OPENCSD_C_FLAGS)
    set(OPENCSD_CXX_FLAGS)
    set(OPENCSD_LINK_FLAGS)
endif()

add_library(opencsd_options INTERFACE)
target_compile_options(opencsd_options INTERFACE
    $<$<COMPILE_LANGUAGE:C>:${OPENCSD_C_FLAGS}>
    $<$<COMPILE_LANGUAGE:CXX>:${OPENCSD_CXX_FLAGS}>
)
target_link_options(opencsd_options INTERFACE ${OPENCSD_LINK_FLAGS})
target_include_directories(opencsd_options INTERFACE
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/decoder/include>
    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)

set(OPENCSD_LIB_OUT "${CMAKE_CURRENT_SOURCE_DIR}/decoder/lib/builddir")
set(OPENCSD_TLIB_OUT "${CMAKE_CURRENT_SOURCE_DIR}/decoder/tests/lib/builddir")
set(OPENCSD_BIN_OUT "${CMAKE_CURRENT_SOURCE_DIR}/decoder/tests/bin/builddir")

function(opencsd_set_shared_lib_versioning target output_name)
    if(APPLE)
        set_target_properties(${target} PROPERTIES
            OUTPUT_NAME "${output_name}.dylib"
            SUFFIX ""
            VERSION ${PROJECT_VERSION}
        )
    else()
        set_target_properties(${target} PROPERTIES
            VERSION ${PROJECT_VERSION}
            SOVERSION ${PROJECT_VERSION_MAJOR}
        )
    endif()
endfunction()

function(opencsd_set_output_dirs target kind)
    if(NOT OPENCSD_FULL_PROJECT_LAYOUT)
        return()
    endif()

    if(kind STREQUAL "lib")
        set(out_dir "${OPENCSD_LIB_OUT}")
    elseif(kind STREQUAL "testlib")
        set(out_dir "${OPENCSD_TLIB_OUT}")
    elseif(kind STREQUAL "bin")
        set(out_dir "${OPENCSD_BIN_OUT}")
    else()
        message(FATAL_ERROR "Unknown output kind '${kind}' for target '${target}'")
    endif()

    set_target_properties(${target} PROPERTIES
        ARCHIVE_OUTPUT_DIRECTORY "${out_dir}"
        LIBRARY_OUTPUT_DIRECTORY "${out_dir}"
        RUNTIME_OUTPUT_DIRECTORY "${out_dir}"
    )
endfunction()

function(opencsd_get_staged_shared_lib_clean_files out_var)
    if(WIN32)
        set(files
            "${OPENCSD_BIN_OUT}/opencsd.dll"
            "${OPENCSD_BIN_OUT}/opencsd_c_api.dll"
        )
    elseif(APPLE)
        set(files
            "${OPENCSD_BIN_OUT}/libopencsd.dylib.${PROJECT_VERSION}"
            "${OPENCSD_BIN_OUT}/libopencsd.dylib"
            "${OPENCSD_BIN_OUT}/libopencsd_c_api.dylib.${PROJECT_VERSION}"
            "${OPENCSD_BIN_OUT}/libopencsd_c_api.dylib"
        )
    else()
        set(files
            "${OPENCSD_BIN_OUT}/libopencsd.so.${PROJECT_VERSION}"
            "${OPENCSD_BIN_OUT}/libopencsd.so.${PROJECT_VERSION_MAJOR}"
            "${OPENCSD_BIN_OUT}/libopencsd.so"
            "${OPENCSD_BIN_OUT}/libopencsd_c_api.so.${PROJECT_VERSION}"
            "${OPENCSD_BIN_OUT}/libopencsd_c_api.so.${PROJECT_VERSION_MAJOR}"
            "${OPENCSD_BIN_OUT}/libopencsd_c_api.so"
        )
    endif()

    set(${out_var} ${files} PARENT_SCOPE)
endfunction()

function(opencsd_stage_shared_libs target)
    if(NOT OPENCSD_FULL_PROJECT_LAYOUT)
        return()
    endif()

    get_property(opencsd_stage_clean_registered GLOBAL PROPERTY OPENCSD_STAGE_SHARED_LIB_CLEAN_REGISTERED)
    if(NOT opencsd_stage_clean_registered)
        opencsd_get_staged_shared_lib_clean_files(opencsd_stage_clean_files)
        set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_CLEAN_FILES ${opencsd_stage_clean_files})
        set_property(GLOBAL PROPERTY OPENCSD_STAGE_SHARED_LIB_CLEAN_REGISTERED TRUE)
    endif()

    if(TARGET stage_shared_libs)
        add_dependencies(${target} stage_shared_libs)
    endif()
endfunction()

function(opencsd_add_test_executable target)
    cmake_parse_arguments(ARG "" "SOURCE" "SOURCES;INCLUDES;LIBS" ${ARGN})

    add_executable(${target} ${ARG_SOURCE} ${ARG_SOURCES})
    target_include_directories(${target} PRIVATE ${ARG_INCLUDES})
    target_link_libraries(${target} PRIVATE opencsd_options ${ARG_LIBS})
    opencsd_set_output_dirs(${target} "bin")
    opencsd_stage_shared_libs(${target})
endfunction()

set(OPENCSD_SRCS
    decoder/source/cs_frame_mux_data.cpp
    decoder/source/ocsd_code_follower.cpp
    decoder/source/ocsd_dcd_tree.cpp
    decoder/source/ocsd_error.cpp
    decoder/source/ocsd_error_logger.cpp
    decoder/source/ocsd_gen_elem_list.cpp
    decoder/source/ocsd_gen_elem_stack.cpp
    decoder/source/ocsd_lib_dcd_register.cpp
    decoder/source/ocsd_msg_logger.cpp
    decoder/source/ocsd_version.cpp
    decoder/source/trc_component.cpp
    decoder/source/trc_core_arch_map.cpp
    decoder/source/trc_frame_deformatter.cpp
    decoder/source/trc_gen_elem.cpp
    decoder/source/trc_printable_elem.cpp
    decoder/source/trc_ret_stack.cpp
    decoder/source/ete/trc_cmp_cfg_ete.cpp
    decoder/source/etmv3/trc_cmp_cfg_etmv3.cpp
    decoder/source/etmv3/trc_pkt_decode_etmv3.cpp
    decoder/source/etmv3/trc_pkt_elem_etmv3.cpp
    decoder/source/etmv3/trc_pkt_proc_etmv3.cpp
    decoder/source/etmv3/trc_pkt_proc_etmv3_impl.cpp
    decoder/source/etmv4/trc_cmp_cfg_etmv4.cpp
    decoder/source/etmv4/trc_etmv4_stack_elem.cpp
    decoder/source/etmv4/trc_pkt_decode_etmv4i.cpp
    decoder/source/etmv4/trc_pkt_elem_etmv4i.cpp
    decoder/source/etmv4/trc_pkt_proc_etmv4i.cpp
    decoder/source/i_dec/trc_i_decode.cpp
    decoder/source/i_dec/trc_idec_arminst.cpp
    decoder/source/itm/trc_pkt_decode_itm.cpp
    decoder/source/itm/trc_pkt_elem_itm.cpp
    decoder/source/itm/trc_pkt_proc_itm.cpp
    decoder/source/mem_acc/trc_mem_acc_base.cpp
    decoder/source/mem_acc/trc_mem_acc_bufptr.cpp
    decoder/source/mem_acc/trc_mem_acc_cache.cpp
    decoder/source/mem_acc/trc_mem_acc_cb.cpp
    decoder/source/mem_acc/trc_mem_acc_file.cpp
    decoder/source/mem_acc/trc_mem_acc_mapper.cpp
    decoder/source/pkt_printers/gen_elem_printer.cpp
    decoder/source/pkt_printers/raw_frame_printer.cpp
    decoder/source/pkt_printers/trc_print_fact.cpp
    decoder/source/ptm/trc_cmp_cfg_ptm.cpp
    decoder/source/ptm/trc_pkt_decode_ptm.cpp
    decoder/source/ptm/trc_pkt_elem_ptm.cpp
    decoder/source/ptm/trc_pkt_proc_ptm.cpp
    decoder/source/stm/trc_pkt_decode_stm.cpp
    decoder/source/stm/trc_pkt_elem_stm.cpp
    decoder/source/stm/trc_pkt_proc_stm.cpp
)

set(OPENCSD_C_API_SRCS
    decoder/source/c_api/ocsd_c_api.cpp
    decoder/source/c_api/ocsd_c_api_custom_obj.cpp
)

set(SNAPSHOT_PARSER_SRCS
    decoder/tests/snapshot_parser_lib/source/device_info.cpp
    decoder/tests/snapshot_parser_lib/source/device_parser.cpp
    decoder/tests/snapshot_parser_lib/source/snapshot_parser.cpp
    decoder/tests/snapshot_parser_lib/source/snapshot_parser_util.cpp
    decoder/tests/snapshot_parser_lib/source/snapshot_reader.cpp
    decoder/tests/snapshot_parser_lib/source/ss_to_dcdtree.cpp
)

if(OPENCSD_BUILD_MIN_LIB_STATIC)
    add_library(opencsd STATIC ${OPENCSD_SRCS})
    add_library(OpenCSD::opencsd ALIAS opencsd)
    target_link_libraries(opencsd PUBLIC opencsd_options)
    target_include_directories(opencsd PRIVATE decoder/source)
    set_target_properties(opencsd PROPERTIES
        VERSION ${PROJECT_VERSION}
        SOVERSION ${PROJECT_VERSION_MAJOR}
    )

    add_library(opencsd_c_api STATIC ${OPENCSD_C_API_SRCS})
    add_library(OpenCSD::opencsd_c_api ALIAS opencsd_c_api)
    target_link_libraries(opencsd_c_api PUBLIC opencsd PRIVATE opencsd_options)
    target_include_directories(opencsd_c_api PRIVATE decoder/source)
    if(WIN32)
        target_compile_definitions(opencsd_c_api PUBLIC OCSD_USE_STATIC_C_API)
    endif()
    set_target_properties(opencsd_c_api PROPERTIES
        VERSION ${PROJECT_VERSION}
        SOVERSION ${PROJECT_VERSION_MAJOR}
    )
else()
    add_library(opencsd_obj OBJECT ${OPENCSD_SRCS})
    target_link_libraries(opencsd_obj PRIVATE opencsd_options)
    target_include_directories(opencsd_obj PRIVATE decoder/source)

    add_library(opencsd SHARED $<TARGET_OBJECTS:opencsd_obj>)
    add_library(OpenCSD::opencsd ALIAS opencsd)
    target_link_libraries(opencsd PUBLIC opencsd_options)
    opencsd_set_shared_lib_versioning(opencsd opencsd)
    if(WIN32)
        set_target_properties(opencsd PROPERTIES
            RUNTIME_OUTPUT_NAME opencsd
            ARCHIVE_OUTPUT_NAME opencsd_dll
        )
    endif()
    opencsd_set_output_dirs(opencsd "lib")

    add_library(opencsd_static STATIC $<TARGET_OBJECTS:opencsd_obj>)
    add_library(OpenCSD::opencsd_static ALIAS opencsd_static)
    target_link_libraries(opencsd_static PUBLIC opencsd_options)
    set_target_properties(opencsd_static PROPERTIES
        OUTPUT_NAME opencsd
    )
    opencsd_set_output_dirs(opencsd_static "lib")

    add_library(opencsd_c_api_obj_shared OBJECT ${OPENCSD_C_API_SRCS})
    target_link_libraries(opencsd_c_api_obj_shared PRIVATE opencsd_options)
    target_include_directories(opencsd_c_api_obj_shared PRIVATE decoder/source)
    if(WIN32)
        target_compile_definitions(opencsd_c_api_obj_shared PRIVATE _OCSD_C_API_DLL_EXPORT)
    endif()

    add_library(opencsd_c_api SHARED $<TARGET_OBJECTS:opencsd_c_api_obj_shared>)
    add_library(OpenCSD::opencsd_c_api ALIAS opencsd_c_api)
    if(WIN32)
        target_link_libraries(opencsd_c_api PRIVATE opencsd_static opencsd_options)
    else()
        target_link_libraries(opencsd_c_api PUBLIC opencsd PRIVATE opencsd_options)
    endif()
    opencsd_set_shared_lib_versioning(opencsd_c_api opencsd_c_api)
    if(WIN32)
        set_target_properties(opencsd_c_api PROPERTIES
            RUNTIME_OUTPUT_NAME opencsd_c_api
            ARCHIVE_OUTPUT_NAME opencsd_c_api_dll
        )
    endif()
    opencsd_set_output_dirs(opencsd_c_api "lib")

    add_library(opencsd_c_api_obj_static OBJECT ${OPENCSD_C_API_SRCS})
    target_link_libraries(opencsd_c_api_obj_static PRIVATE opencsd_options)
    target_include_directories(opencsd_c_api_obj_static PRIVATE decoder/source)
    if(WIN32)
        target_compile_definitions(opencsd_c_api_obj_static PRIVATE OCSD_USE_STATIC_C_API)
    endif()

    add_library(opencsd_c_api_static STATIC $<TARGET_OBJECTS:opencsd_c_api_obj_static>)
    add_library(OpenCSD::opencsd_c_api_static ALIAS opencsd_c_api_static)
    target_link_libraries(opencsd_c_api_static PUBLIC opencsd_static PRIVATE opencsd_options)
    if(WIN32)
        target_compile_definitions(opencsd_c_api_static INTERFACE OCSD_USE_STATIC_C_API)
    endif()
    set_target_properties(opencsd_c_api_static PROPERTIES
        OUTPUT_NAME opencsd_c_api
    )
    opencsd_set_output_dirs(opencsd_c_api_static "lib")

    if(OPENCSD_FULL_PROJECT_LAYOUT)
        if(WIN32)
            add_custom_target(stage_shared_libs
                COMMAND ${CMAKE_COMMAND} -E make_directory "${OPENCSD_BIN_OUT}"
                COMMAND ${CMAKE_COMMAND} -E copy_if_different
                    $<TARGET_FILE:opencsd>
                    "${OPENCSD_BIN_OUT}/$<TARGET_FILE_NAME:opencsd>"
                COMMAND ${CMAKE_COMMAND} -E copy_if_different
                    $<TARGET_FILE:opencsd_c_api>
                    "${OPENCSD_BIN_OUT}/$<TARGET_FILE_NAME:opencsd_c_api>"
                VERBATIM
            )
        else()
            add_custom_target(stage_shared_libs
                COMMAND ${CMAKE_COMMAND} -E make_directory "${OPENCSD_BIN_OUT}"
                COMMAND ${CMAKE_COMMAND} -E copy_if_different
                    $<TARGET_FILE:opencsd>
                    "${OPENCSD_BIN_OUT}/$<TARGET_FILE_NAME:opencsd>"
                COMMAND ${CMAKE_COMMAND} -E cmake_symlink_library
                    $<TARGET_FILE_NAME:opencsd>
                    $<TARGET_SONAME_FILE_NAME:opencsd>
                    $<TARGET_LINKER_FILE_NAME:opencsd>
                COMMAND ${CMAKE_COMMAND} -E copy_if_different
                    $<TARGET_FILE:opencsd_c_api>
                    "${OPENCSD_BIN_OUT}/$<TARGET_FILE_NAME:opencsd_c_api>"
                COMMAND ${CMAKE_COMMAND} -E cmake_symlink_library
                    $<TARGET_FILE_NAME:opencsd_c_api>
                    $<TARGET_SONAME_FILE_NAME:opencsd_c_api>
                    $<TARGET_LINKER_FILE_NAME:opencsd_c_api>
                WORKING_DIRECTORY "${OPENCSD_BIN_OUT}"
                VERBATIM
            )
        endif()
        add_dependencies(stage_shared_libs opencsd opencsd_c_api)
    endif()

    add_custom_target(libs DEPENDS
        opencsd
        opencsd_static
        opencsd_c_api
        opencsd_c_api_static
    )
endif()

if(BUILD_TESTING)
    if(OPENCSD_BUILD_MIN_LIB_STATIC)
        add_library(snapshot_parser STATIC ${SNAPSHOT_PARSER_SRCS})
        target_include_directories(snapshot_parser PRIVATE
            decoder/tests/snapshot_parser_lib/include
            decoder/include
        )
        target_link_libraries(snapshot_parser PRIVATE opencsd_options)

        add_executable(trc_pkt_lister decoder/tests/source/trc_pkt_lister.cpp)
        target_include_directories(trc_pkt_lister PRIVATE
            decoder/tests/snapshot_parser_lib/include
            decoder/tests/source
        )
        target_link_libraries(trc_pkt_lister PRIVATE snapshot_parser opencsd)
        target_compile_options(trc_pkt_lister PRIVATE ${OPENCSD_CXX_FLAGS})
    else()
        add_library(snapshot_parser STATIC ${SNAPSHOT_PARSER_SRCS})
        target_include_directories(snapshot_parser PRIVATE
            decoder/tests/snapshot_parser_lib/include
            decoder/include
        )
        target_link_libraries(snapshot_parser PRIVATE opencsd_options)
        opencsd_set_output_dirs(snapshot_parser "testlib")

        add_library(echo_test_dcd STATIC
            decoder/tests/ext_dcd_test_eg/c_api_echo_test/ext_dcd_echo_test.c
            decoder/tests/ext_dcd_test_eg/c_api_echo_test/ext_dcd_echo_test_fact.c
        )
        target_include_directories(echo_test_dcd PRIVATE
            decoder/tests/ext_dcd_test_eg/c_api_echo_test
            decoder/include
        )
        target_link_libraries(echo_test_dcd PRIVATE opencsd_options)
        opencsd_set_output_dirs(echo_test_dcd "testlib")

        opencsd_add_test_executable(trc_pkt_lister
            SOURCE decoder/tests/source/trc_pkt_lister.cpp
            INCLUDES
                decoder/tests/snapshot_parser_lib/include
                decoder/tests/source
            LIBS
                snapshot_parser
                opencsd
        )

        opencsd_add_test_executable(c_api_pkt_print_test
            SOURCE decoder/tests/source/c_api_pkt_print_test.c
            INCLUDES
                decoder/tests/source
                decoder/tests/ext_dcd_test_eg/c_api_echo_test
            LIBS
                echo_test_dcd
                opencsd
                opencsd_c_api
        )
        set_target_properties(c_api_pkt_print_test PROPERTIES
            LINKER_LANGUAGE CXX
        )

        opencsd_add_test_executable(mem-buffer-eg
            SOURCE decoder/tests/source/mem_buff_demo.cpp
            INCLUDES
                decoder/tests/source
                decoder/tests/snapshot_parser_lib/include
            LIBS
                snapshot_parser
                opencsd
        )

        opencsd_add_test_executable(frame-demux-test
            SOURCE decoder/tests/source/frame_demux_test.cpp
            INCLUDES
                decoder/tests/source
            LIBS
                opencsd
        )

        opencsd_add_test_executable(ocsd-perr
            SOURCE decoder/tests/source/perr.cpp
            INCLUDES
                decoder/tests/source
            LIBS
                opencsd
        )

        opencsd_add_test_executable(mem-acc-test
            SOURCE decoder/tests/source/mem_acc_test.cpp
            INCLUDES
                decoder/tests/source
            LIBS
                opencsd
        )

        opencsd_add_test_executable(itm-decode-test
            SOURCE decoder/tests/source/itm_decode_test.cpp
            SOURCES
                decoder/tests/source/itm_test_data.cpp
            INCLUDES
                decoder/tests/source
            LIBS
                opencsd
        )

        if(OPENCSD_BUILD_DEV_TARGETS)
            add_executable(trc_pkt_lister_s decoder/tests/source/trc_pkt_lister.cpp)
            target_include_directories(trc_pkt_lister_s PRIVATE
                decoder/tests/snapshot_parser_lib/include
                decoder/tests/source
            )
            target_link_libraries(trc_pkt_lister_s PRIVATE
                opencsd_options
                snapshot_parser
                opencsd_static
            )
            target_link_options(trc_pkt_lister_s PRIVATE -static)
            opencsd_set_output_dirs(trc_pkt_lister_s "bin")
        endif()

        add_custom_target(tests DEPENDS
            echo_test_dcd
            snapshot_parser
            trc_pkt_lister
            c_api_pkt_print_test
            mem-buffer-eg
            frame-demux-test
            ocsd-perr
            mem-acc-test
            itm-decode-test
        )
    endif()
else()
    if(NOT OPENCSD_BUILD_MIN_LIB_STATIC)
        add_custom_target(tests)
    endif()
endif()

if(NOT OPENCSD_BUILD_MIN_LIB_STATIC)
    find_package(Doxygen QUIET)
    if(DOXYGEN_FOUND)
        add_custom_target(docs
            COMMAND ${DOXYGEN_EXECUTABLE} doxygen_config.dox
            WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/decoder/docs
        )
    endif()
endif()

set(OPENCSD_INSTALL_TARGETS
    opencsd
    opencsd_c_api
    opencsd_options
)

if(NOT OPENCSD_BUILD_MIN_LIB_STATIC)
    list(APPEND OPENCSD_INSTALL_TARGETS
        opencsd_static
        opencsd_c_api_static
    )
endif()

install(TARGETS ${OPENCSD_INSTALL_TARGETS}
    EXPORT OpenCSDTargets
    COMPONENT development
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

if(BUILD_TESTING AND NOT OPENCSD_BUILD_MIN_LIB_STATIC)
    install(TARGETS trc_pkt_lister
        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    )

    install(FILES decoder/docs/man/trc_pkt_lister.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
endif()

set(OPENCSD_INSTALL_HEADERS
    decoder/include/opencsd/trc_gen_elem_types.h
    decoder/include/opencsd/ocsd_if_types.h
    decoder/include/opencsd/ocsd_if_version.h
    decoder/include/opencsd/trc_pkt_types.h
)

set(OPENCSD_INSTALL_HEADERS_PTM
    decoder/include/opencsd/ptm/trc_pkt_types_ptm.h
)

set(OPENCSD_INSTALL_HEADERS_STM
    decoder/include/opencsd/stm/trc_pkt_types_stm.h
)

set(OPENCSD_INSTALL_HEADERS_ETMV3
    decoder/include/opencsd/etmv3/trc_pkt_types_etmv3.h
)

set(OPENCSD_INSTALL_HEADERS_ETMV4
    decoder/include/opencsd/etmv4/trc_pkt_types_etmv4.h
)

set(OPENCSD_INSTALL_HEADERS_ETE
    decoder/include/opencsd/ete/trc_pkt_types_ete.h
)

set(OPENCSD_INSTALL_HEADERS_ITM
    decoder/include/opencsd/itm/trc_pkt_types_itm.h
)

set(OPENCSD_INSTALL_HEADERS_C_API
    decoder/include/opencsd/c_api/ocsd_c_api_types.h
    decoder/include/opencsd/c_api/opencsd_c_api.h
    decoder/include/opencsd/c_api/ocsd_c_api_custom.h
)

install(FILES ${OPENCSD_INSTALL_HEADERS}
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/opencsd
)

install(FILES ${OPENCSD_INSTALL_HEADERS_PTM}
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/opencsd/ptm
)

install(FILES ${OPENCSD_INSTALL_HEADERS_STM}
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/opencsd/stm
)

install(FILES ${OPENCSD_INSTALL_HEADERS_ETMV3}
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/opencsd/etmv3
)

install(FILES ${OPENCSD_INSTALL_HEADERS_ETMV4}
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/opencsd/etmv4
)

install(FILES ${OPENCSD_INSTALL_HEADERS_ETE}
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/opencsd/ete
)

install(FILES ${OPENCSD_INSTALL_HEADERS_ITM}
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/opencsd/itm
)

install(FILES ${OPENCSD_INSTALL_HEADERS_C_API}
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/opencsd/c_api
)

install(EXPORT OpenCSDTargets
    FILE OpenCSDTargets.cmake
    NAMESPACE OpenCSD::
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/OpenCSD
)

include(CMakePackageConfigHelpers)

configure_package_config_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/decoder/build/cmake/OpenCSDConfig.cmake.in
    ${CMAKE_CURRENT_BINARY_DIR}/OpenCSDConfig.cmake
    INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/OpenCSD
)

write_basic_package_version_file(
    ${CMAKE_CURRENT_BINARY_DIR}/OpenCSDConfigVersion.cmake
    VERSION ${PROJECT_VERSION}
    COMPATIBILITY SameMajorVersion
)

install(FILES
    ${CMAKE_CURRENT_BINARY_DIR}/OpenCSDConfig.cmake
    ${CMAKE_CURRENT_BINARY_DIR}/OpenCSDConfigVersion.cmake
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/OpenCSD
)

configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/decoder/build/cmake/uninstall.cmake.in
    ${CMAKE_CURRENT_BINARY_DIR}/uninstall.cmake
    @ONLY
)

add_custom_target(uninstall
    COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/uninstall.cmake
)
