cmake_minimum_required(VERSION 3.16) project(CmvrControlStation VERSION 1.0.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/ui") set(CMAKE_INCLUDE_CURRENT_DIR ON) if(WIN32) set(CMVR_MSQUIC_DEFAULT ON) else() set(CMVR_MSQUIC_DEFAULT OFF) endif() option(CMVR_WITH_MSQUIC "Build the embedded MsQuic test server" ${CMVR_MSQUIC_DEFAULT}) set(CMVR_MSQUIC_ROOT "" CACHE PATH "Optional MsQuic installation prefix on Linux") set(CMVR_CXX_RUNTIME "" CACHE FILEPATH "Optional libstdc++.so.6 matching a non-system Linux dependency prefix") set(CMVR_ASSIMP_SCENE_PARSER "" CACHE FILEPATH "Qt3D Assimp scene parser plugin used for URDF mesh files") set(CMVR_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") set(CMVR_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/lib") set(CMVR_TOOLS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tools") set(CMVR_CERT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/certs") set(PROTO_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/protos") set(GENERATED_ROOT "${CMAKE_CURRENT_BINARY_DIR}/generated_cpp") find_package(Qt5 5.12 REQUIRED COMPONENTS Core Gui Widgets Multimedia 3DCore 3DRender 3DExtras 3DInput 3DAnimation) get_target_property(CMVR_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION) if(CMVR_QMAKE_EXECUTABLE) execute_process( COMMAND "${CMVR_QMAKE_EXECUTABLE}" -query QT_INSTALL_PLUGINS OUTPUT_VARIABLE CMVR_QT_PLUGIN_ROOT OUTPUT_STRIP_TRAILING_WHITESPACE) endif() if(NOT CMVR_ASSIMP_SCENE_PARSER) find_file(CMVR_ASSIMP_SCENE_PARSER NAMES assimpsceneimport.dll assimpsceneimportd.dll libassimpsceneimport.so HINTS "${CMVR_QT_PLUGIN_ROOT}/sceneparsers" "/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}/qt5/plugins/sceneparsers") endif() if(NOT CMVR_ASSIMP_SCENE_PARSER OR NOT EXISTS "${CMVR_ASSIMP_SCENE_PARSER}") message(FATAL_ERROR "Qt3D's Assimp scene parser was not found. It is required to display " "STL, DAE, and 3DS meshes from bundled URDF models. On Ubuntu install " "qt3d-assimpsceneimport-plugin, or set CMVR_ASSIMP_SCENE_PARSER to the " "plugin file supplied by the selected Qt installation.") endif() if(WIN32) # The checked-in native libraries use the Qt 5.12.4 MinGW 7.3 64-bit ABI. set(PROTOC "${CMVR_TOOLS_DIR}/run_protoc.cmd") set(GRPC_CPP_PLUGIN "${CMVR_TOOLS_DIR}/grpc_cpp_plugin.exe") foreach(REQUIRED_FILE IN ITEMS "${PROTOC}" "${GRPC_CPP_PLUGIN}" "${CMVR_LIB_DIR}/libgrpc++.a" "${CMVR_LIB_DIR}/libprotobuf.a" "${CMVR_LIB_DIR}/libavcodec.dll.a" "${CMVR_LIB_DIR}/liburdfdom_model.dll.a") if(NOT EXISTS "${REQUIRED_FILE}") message(FATAL_ERROR "Required Windows dependency not found: ${REQUIRED_FILE}") endif() endforeach() else() find_package(Protobuf CONFIG QUIET) if(NOT Protobuf_FOUND) find_package(Protobuf REQUIRED) endif() find_package(gRPC CONFIG REQUIRED) find_package(Threads REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(FFMPEG REQUIRED IMPORTED_TARGET libavcodec libavutil libswscale) find_path(CMVR_URDFDOM_INCLUDE_DIR urdf_parser/urdf_parser.h PATH_SUFFIXES include include/urdfdom) find_path(CMVR_TINYXML_INCLUDE_DIR tinyxml.h PATH_SUFFIXES include) find_library(CMVR_URDFDOM_MODEL_LIBRARY NAMES urdfdom_model) if(NOT CMVR_URDFDOM_INCLUDE_DIR OR NOT CMVR_TINYXML_INCLUDE_DIR OR NOT CMVR_URDFDOM_MODEL_LIBRARY) message(FATAL_ERROR "urdfdom/tinyxml was not found. Install liburdfdom-dev or add its prefix " "to CMAKE_PREFIX_PATH.") endif() if(NOT TARGET protobuf::protoc) message(FATAL_ERROR "The Protobuf package does not provide protobuf::protoc") endif() if(NOT TARGET gRPC::grpc_cpp_plugin) message(FATAL_ERROR "The gRPC package does not provide gRPC::grpc_cpp_plugin") endif() set(PROTOC "$") set(GRPC_CPP_PLUGIN "$") # A Conda-provided Qt/gRPC stack may carry an older libstdc++ than the # host compiler. Link against the runtime next to the imported dependency # so symbol versions selected at link time match the library loaded from # the generated RUNPATH. System packages resolve to the system runtime. if(NOT CMVR_CXX_RUNTIME) foreach(DEPENDENCY_TARGET IN ITEMS gRPC::grpc++ Qt5::Core) get_target_property(DEPENDENCY_LOCATION ${DEPENDENCY_TARGET} IMPORTED_LOCATION_RELEASE) if(NOT DEPENDENCY_LOCATION) get_target_property(DEPENDENCY_LOCATION ${DEPENDENCY_TARGET} IMPORTED_LOCATION) endif() if(DEPENDENCY_LOCATION) get_filename_component(DEPENDENCY_LIB_DIR "${DEPENDENCY_LOCATION}" DIRECTORY) if(EXISTS "${DEPENDENCY_LIB_DIR}/libstdc++.so.6") set(CMVR_CXX_RUNTIME "${DEPENDENCY_LIB_DIR}/libstdc++.so.6") message(STATUS "Using dependency-prefix C++ runtime: ${CMVR_CXX_RUNTIME}") break() endif() endif() endforeach() endif() endif() if(CMVR_WITH_MSQUIC) if(WIN32) if(NOT EXISTS "${CMVR_INCLUDE_DIR}/msquic.h" OR NOT EXISTS "${CMVR_LIB_DIR}/msquic.dll") message(FATAL_ERROR "CMVR_WITH_MSQUIC is enabled, but include/msquic.h or lib/msquic.dll is missing") endif() set(CMVR_MSQUIC_INCLUDE_DIR "${CMVR_INCLUDE_DIR}") else() find_path(CMVR_MSQUIC_INCLUDE_DIR msquic.h HINTS "${CMVR_MSQUIC_ROOT}" PATH_SUFFIXES include) find_library(CMVR_MSQUIC_LIBRARY NAMES msquic HINTS "${CMVR_MSQUIC_ROOT}" PATH_SUFFIXES lib lib64) if(NOT CMVR_MSQUIC_INCLUDE_DIR OR NOT CMVR_MSQUIC_LIBRARY) message(FATAL_ERROR "CMVR_WITH_MSQUIC=ON requires a Linux MsQuic installation. " "Set CMVR_MSQUIC_ROOT or configure with -DCMVR_WITH_MSQUIC=OFF.") endif() get_filename_component(CMVR_MSQUIC_LIBRARY_DIR "${CMVR_MSQUIC_LIBRARY}" DIRECTORY) endif() endif() set(CMVR_SOURCES src/audiostreammanager.cpp src/audiostreammanager.h src/configeditorwidget.cpp src/configeditorwidget.h src/main.cpp src/mainwindow.cpp src/mainwindow_ui.cpp src/mainwindow.h src/quictestserver.cpp src/quictestserver.h src/rpcservicewidget.cpp src/rpcservicewidget.h src/robotclient.cpp src/robotclient.h src/trendwidget.cpp src/trendwidget.h src/uiicons.h src/urdfviewerwidget.cpp src/urdfviewerwidget.h src/videodecoder.cpp src/videodecoder.h src/videoframe.h src/videorenderwidget.cpp src/videorenderwidget.h ui/handeyecalibrationwidget.cpp ui/handeyecalibrationwidget.h ui/handeyecalibrationwidget.ui ui/mainwindow.ui ui/systemservicepage.ui ui/armservicepage.ui ui/cameraservicepage.ui ui/agvservicepage.ui ui/agvstatusdialog.ui ui/audioservicepage.ui ui/quicserverpage.ui ui/diagnosticspage.ui ui/rpcservicewidget.ui resources.qrc) add_executable(CmvrControlStation ${CMVR_SOURCES}) if(WIN32) set_target_properties(CmvrControlStation PROPERTIES WIN32_EXECUTABLE TRUE) endif() set_target_properties(CmvrControlStation PROPERTIES DEBUG_POSTFIX "_d" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin/$>") target_compile_definitions(CmvrControlStation PRIVATE CMVR_WITH_GRPC=1 CMVR_WITH_MSQUIC=$) if(WIN32) target_compile_definitions(CmvrControlStation PRIVATE _WIN32_WINNT=0x0600) endif() target_include_directories(CmvrControlStation PRIVATE "${GENERATED_ROOT}") if(WIN32) target_include_directories(CmvrControlStation PRIVATE "${CMVR_INCLUDE_DIR}" "${CMVR_INCLUDE_DIR}/urdfdom_headers") else() target_include_directories(CmvrControlStation PRIVATE "${CMVR_URDFDOM_INCLUDE_DIR}" "${CMVR_TINYXML_INCLUDE_DIR}" "${CMVR_INCLUDE_DIR}/urdfdom_headers") endif() if(CMVR_WITH_MSQUIC) target_include_directories(CmvrControlStation PRIVATE "${CMVR_MSQUIC_INCLUDE_DIR}") if(UNIX AND CMVR_MSQUIC_LIBRARY_DIR) set_property(TARGET CmvrControlStation APPEND PROPERTY BUILD_RPATH "${CMVR_MSQUIC_LIBRARY_DIR}") endif() endif() target_link_libraries(CmvrControlStation PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Multimedia Qt5::3DCore Qt5::3DRender Qt5::3DExtras Qt5::3DInput Qt5::3DAnimation) # Generate every project-local CMVR message. This avoids source-tree generated # files and automatically picks up newly added configuration messages. file(GLOB_RECURSE PROTO_FILES CONFIGURE_DEPENDS "${PROTO_ROOT}/cmvr/api/*.proto" "${PROTO_ROOT}/cmvr/common/*.proto" "${PROTO_ROOT}/cmvr/config/*.proto" "${PROTO_ROOT}/cmvr/msgs/agv.proto" "${PROTO_ROOT}/cmvr/quic_edge/v1/*.proto") set(SERVICE_NAMES cmvr/api/agv_service cmvr/api/arm_service cmvr/api/biohead_service cmvr/api/camera_service cmvr/api/configuration_service cmvr/api/dexhand_service cmvr/api/hlc_service cmvr/api/microphone_service cmvr/api/speaker_service cmvr/api/system_service cmvr/api/test_service) set(GENERATED_FILES) foreach(PROTO_FILE IN LISTS PROTO_FILES) file(RELATIVE_PATH PROTO_RELATIVE "${PROTO_ROOT}" "${PROTO_FILE}") string(REGEX REPLACE "\\.proto$" "" PROTO_NAME "${PROTO_RELATIVE}") set(PB_CC "${GENERATED_ROOT}/${PROTO_NAME}.pb.cc") set(PB_H "${GENERATED_ROOT}/${PROTO_NAME}.pb.h") get_filename_component(PROTO_OUTPUT_DIR "${PB_CC}" DIRECTORY) add_custom_command( OUTPUT "${PB_CC}" "${PB_H}" COMMAND ${CMAKE_COMMAND} -E make_directory "${PROTO_OUTPUT_DIR}" COMMAND "${PROTOC}" "--proto_path=${PROTO_ROOT}" "--cpp_out=${GENERATED_ROOT}" "${PROTO_FILE}" DEPENDS "${PROTO_FILE}" ${PROTO_FILES} VERBATIM) list(APPEND GENERATED_FILES "${PB_CC}" "${PB_H}") endforeach() foreach(SERVICE_NAME IN LISTS SERVICE_NAMES) set(PROTO_FILE "${PROTO_ROOT}/${SERVICE_NAME}.proto") set(GRPC_CC "${GENERATED_ROOT}/${SERVICE_NAME}.grpc.pb.cc") set(GRPC_H "${GENERATED_ROOT}/${SERVICE_NAME}.grpc.pb.h") get_filename_component(PROTO_OUTPUT_DIR "${GRPC_CC}" DIRECTORY) add_custom_command( OUTPUT "${GRPC_CC}" "${GRPC_H}" COMMAND ${CMAKE_COMMAND} -E make_directory "${PROTO_OUTPUT_DIR}" COMMAND "${PROTOC}" "--proto_path=${PROTO_ROOT}" "--grpc_out=${GENERATED_ROOT}" "--plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN}" "${PROTO_FILE}" DEPENDS "${PROTO_FILE}" ${PROTO_FILES} VERBATIM) list(APPEND GENERATED_FILES "${GRPC_CC}" "${GRPC_H}") endforeach() target_sources(CmvrControlStation PRIVATE ${GENERATED_FILES}) if(WIN32) file(GLOB ABSL_STATIC_LIBS "${CMVR_LIB_DIR}/libabsl_*.a") target_link_libraries(CmvrControlStation PRIVATE "-Wl,--start-group" "${CMVR_LIB_DIR}/libgrpc++.a" "${CMVR_LIB_DIR}/libgrpc.a" "${CMVR_LIB_DIR}/libgpr.a" "${CMVR_LIB_DIR}/libaddress_sorting.a" "${CMVR_LIB_DIR}/libre2.a" "${CMVR_LIB_DIR}/libupb.a" "${CMVR_LIB_DIR}/libcares.a" "${CMVR_LIB_DIR}/libssl.a" "${CMVR_LIB_DIR}/libcrypto.a" "${CMVR_LIB_DIR}/libzlibstatic.a" "${CMVR_LIB_DIR}/libprotobuf.a" ${ABSL_STATIC_LIBS} "-Wl,--end-group" "${CMVR_LIB_DIR}/libavcodec.dll.a" "${CMVR_LIB_DIR}/libavutil.dll.a" "${CMVR_LIB_DIR}/libswscale.dll.a" "${CMVR_LIB_DIR}/liburdfdom_model.dll.a" ws2_32 crypt32 advapi32 bcrypt dbghelp iphlpapi secur32 ntdll) else() if(CMVR_CXX_RUNTIME) target_link_libraries(CmvrControlStation PRIVATE "${CMVR_CXX_RUNTIME}") endif() target_link_libraries(CmvrControlStation PRIVATE protobuf::libprotobuf gRPC::grpc++ PkgConfig::FFMPEG Threads::Threads "${CMVR_URDFDOM_MODEL_LIBRARY}") endif() if(WIN32) file(GLOB CMVR_RUNTIME_DLLS "${CMVR_LIB_DIR}/*.dll") foreach(RUNTIME_DLL IN LISTS CMVR_RUNTIME_DLLS) add_custom_command(TARGET CmvrControlStation POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${RUNTIME_DLL}" "$") endforeach() endif() foreach(CERTIFICATE IN ITEMS quic-test-server.pfx quic-test-ca.crt) if(EXISTS "${CMVR_CERT_DIR}/${CERTIFICATE}") add_custom_command(TARGET CmvrControlStation POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMVR_CERT_DIR}/${CERTIFICATE}" "$/${CERTIFICATE}") install(FILES "${CMVR_CERT_DIR}/${CERTIFICATE}" DESTINATION bin) endif() endforeach() add_custom_command(TARGET CmvrControlStation POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory "$/sceneparsers" COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMVR_ASSIMP_SCENE_PARSER}" "$/sceneparsers/" COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/models" "$/models") install(TARGETS CmvrControlStation RUNTIME DESTINATION bin) install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/models/" DESTINATION bin/models) install(PROGRAMS "${CMVR_ASSIMP_SCENE_PARSER}" DESTINATION bin/sceneparsers)