perspective-server 4.4.0

A data visualization and analytics component, especially well-suited for large and/or streaming datasets.
Documentation
set(DEFAULT_PROTOBUF_VERSION "33.2")

option(PSP_PROTOC_PATH "Path to the protoc binary" "")

# Function to download and extract protoc
function(download_protoc VERSION DESTINATION)
    if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
        set(PROTOC_ZIP "protoc-${VERSION}-win64.zip")
    elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
        set(PROTOC_ZIP "protoc-${VERSION}-osx-x86_64.zip")
    elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
        if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "aarch64")
            set(PROTOC_ZIP "protoc-${VERSION}-linux-aarch_64.zip")
        else()
            set(PROTOC_ZIP "protoc-${VERSION}-linux-x86_64.zip")
        endif()
    else()
        message(FATAL_ERROR "Unsupported host system: ${CMAKE_HOST_SYSTEM_NAME}")
    endif()

    set(PROTOC_URL "https://github.com/protocolbuffers/protobuf/releases/download/v${VERSION}/${PROTOC_ZIP}")
    set(DOWNLOAD_PATH "${CMAKE_BINARY_DIR}/downloaded/${PROTOC_ZIP}")

    file(DOWNLOAD ${PROTOC_URL} ${DOWNLOAD_PATH}
         STATUS DOWNLOAD_STATUS
         SHOW_PROGRESS)

    list(GET DOWNLOAD_STATUS 0 STATUS_CODE)
    if(STATUS_CODE EQUAL 0)
        message(STATUS "Downloaded protoc: ${DOWNLOAD_PATH}")
        file(ARCHIVE_EXTRACT INPUT ${DOWNLOAD_PATH} DESTINATION ${DESTINATION})
        file(REMOVE ${DOWNLOAD_PATH})
    else()
        message(FATAL_ERROR "Failed to download protoc: Status code ${STATUS_CODE}")
    endif()
endfunction()

find_program(Protobuf_EXECUTABLE NAMES protoc PATHS ${PSP_PROTOC_PATH} ENV PATH ${CMAKE_BINARY_DIR}/protoc-release/bin/)

if(NOT Protobuf_EXECUTABLE)
    message(STATUS "Protobuf_EXECUTABLE not found, searching for protoc")
    download_protoc(${DEFAULT_PROTOBUF_VERSION} ${CMAKE_BINARY_DIR}/protoc-release)
    set(PROTOC_EXECUTABLE ${CMAKE_BINARY_DIR}/protoc-release/bin/protoc)
    # find_program(PROTOC_EXECUTABLE NAMES protoc)
else()
    message(STATUS "Using Protobuf_EXECUTABLE from parent scope")
    set(PROTOC_EXECUTABLE ${Protobuf_EXECUTABLE})
endif()

if(NOT PROTOC_EXECUTABLE)
    message(FATAL_ERROR "protoc not found")
    set(Protoc_FOUND FALSE)
else()
    set(Protoc_FOUND TRUE)
    message(STATUS "Found protoc: ${PROTOC_EXECUTABLE}")

    function(CHECK_PROTOC_VERSION)
        execute_process(
            COMMAND ${PROTOC_EXECUTABLE} --version
            OUTPUT_VARIABLE PROTOC_VERSION_OUTPUT
            OUTPUT_STRIP_TRAILING_WHITESPACE
        )

        if(NOT PROTOC_VERSION_OUTPUT MATCHES "^libprotoc ([0-9]+)\\.([0-9]+)(:?\\.([0-9]+))?")
            message(WARNING "Unable to determine protoc version")
            return()
        endif()

        set(PROTOC_VERSION_MAJOR ${CMAKE_MATCH_1})
        set(PROTOC_VERSION_MINOR ${CMAKE_MATCH_2})
        set(PROTOC_VERSION_PATCH ${CMAKE_MATCH_4})

        if (NOT PROTOC_VERSION_PATCH)
            set(FOUND_VERSION "${PROTOC_VERSION_MAJOR}.${PROTOC_VERSION_MINOR}")
        else()
            set(FOUND_VERSION "${PROTOC_VERSION_MAJOR}.${PROTOC_VERSION_MINOR}.${PROTOC_VERSION_PATCH}")
        endif()

        # Force the external project to use the same version as our installed protoc CLI
        set(LIBPROTOBUF_VERSION "v${FOUND_VERSION}" PARENT_SCOPE)

        set(MIN_PROTOC_VERSION_MAJOR 3)

        if(PROTOC_VERSION_MAJOR LESS MIN_PROTOC_VERSION_MAJOR)
            message(FATAL_ERROR "protoc version ${FOUND_VERSION} is too old, at least ${MIN_PROTOC_VERSION_MAJOR}.x.x is required")
        else()
            message(STATUS "Found protoc version ${FOUND_VERSION}")
        endif()
    endfunction()

    # Check the version of protoc
    CHECK_PROTOC_VERSION()
endif()

function(protobuf_generate_cpp SRCS HDRS)
    if(NOT ARGN)
        message(SEND_ERROR "Error: protobuf_generate_cpp() called without any proto files")
        return()
    endif()

    set(_PROTOBUF_GENERATE_CPP_SRCS)
    set(_PROTOBUF_GENERATE_CPP_HDRS)

    foreach(PROTO_FILE ${ARGN})
        get_filename_component(PROTO_NAME ${PROTO_FILE} NAME_WE)
        get_filename_component(PROTO_PATH ${PROTO_FILE} PATH)

        set(GENERATED_SRC "${CMAKE_CURRENT_BINARY_DIR}/${PROTO_NAME}.pb.cc")
        set(GENERATED_HDR "${CMAKE_CURRENT_BINARY_DIR}/${PROTO_NAME}.pb.h")

        add_custom_command(
            OUTPUT ${GENERATED_SRC} ${GENERATED_HDR}
            COMMAND ${PROTOC_EXECUTABLE} --cpp_out ${CMAKE_CURRENT_BINARY_DIR} --proto_path ${PROTO_PATH} ${PROTO_FILE}
            DEPENDS ${PROTO_FILE} ${PROTOC_EXECUTABLE}
            WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
            COMMENT "Generating ${GENERATED_SRC} and ${GENERATED_HDR} from ${PROTO_FILE}"
        )

        set_source_files_properties(${GENERATED_SRC} ${GENERATED_HDR} PROPERTIES GENERATED TRUE)

        list(APPEND _PROTOBUF_GENERATE_CPP_SRCS ${GENERATED_SRC})
        list(APPEND _PROTOBUF_GENERATE_CPP_HDRS ${GENERATED_HDR})
    endforeach()
    set(${SRCS} ${_PROTOBUF_GENERATE_CPP_SRCS} PARENT_SCOPE)
    set(${HDRS} ${_PROTOBUF_GENERATE_CPP_HDRS} PARENT_SCOPE)
endfunction()