cmake_minimum_required(VERSION 3.10) project(devGrpcClient VERSION 1.0.0 LANGUAGES CXX) # 设置编译选项 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_compile_options(-fPIC -g -Wall) # 目标目录 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) get_filename_component(DELIVER_DIR "${CMAKE_BINARY_DIR}/../../Deliver" ABSOLUTE) file(MAKE_DIRECTORY "${DELIVER_DIR}/lib" "${DELIVER_DIR}/include") # ========== 强制使用动态链接和共享库 ========== # ARM Ubuntu 的系统静态库没有 -fPIC,无法链接到共享库 # 使用 CACHE FORCE 确保覆盖任何缓存中的旧值 set(GRPC_STATIC_LINK OFF CACHE BOOL "静态链接gRPC和Protobuf" FORCE) set(BUILD_SHARED_LIBS ON CACHE BOOL "编译为共享库(.so)" FORCE) # 强制输出消息确认配置 message(STATUS "强制配置: GRPC_STATIC_LINK=${GRPC_STATIC_LINK}, BUILD_SHARED_LIBS=${BUILD_SHARED_LIBS}") # ========== 依赖查找:使用系统安装的 gRPC 和 Protobuf ========== # 强制使用系统路径,避免 /usr/local 下的多版本冲突 set(CMAKE_FIND_USE_CMAKE_SYSTEM_PATH TRUE) set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH FALSE) # 1. 查找 Protobuf(优先使用系统路径) # 设置提示路径为系统目录 set(Protobuf_ROOT "/usr") set(CMAKE_PREFIX_PATH "/usr" ${CMAKE_PREFIX_PATH}) find_package(Protobuf REQUIRED HINTS /usr NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH) message(STATUS "找到系统Protobuf版本: ${Protobuf_VERSION}") message(STATUS "Protobuf头文件路径: ${Protobuf_INCLUDE_DIRS}") # 2. 查找 gRPC(通过CONFIG模式,适用于现代gRPC安装) find_package(gRPC CONFIG QUIET) if (gRPC_FOUND) message(STATUS "通过CONFIG模式找到系统gRPC版本: ${gRPC_VERSION}") else() # 回退到MODULE模式(旧版或自定义安装) find_package(gRPC MODULE QUIET) if (gRPC_FOUND) message(STATUS "通过MODULE模式找到gRPC") else() message(STATUS "未通过find_package找到gRPC,尝试使用pkg-config") find_package(PkgConfig REQUIRED) pkg_check_modules(GRPC REQUIRED grpc++ grpc) set(GRPC_LIBRARIES ${GRPC_LIBRARIES}) set(GRPC_INCLUDE_DIRS ${GRPC_INCLUDE_DIRS}) endif() endif() # 3. 查找gRPC代码生成器插件(增强查找逻辑) find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin HINTS ${GRPC_CPP_PLUGIN_DIR} /usr/bin /usr/local/bin /usr/lib/grpc /usr/local/lib/grpc PATH_SUFFIXES bin ) # 如果还是找不到,尝试从gRPC目标获取 if(NOT _GRPC_CPP_PLUGIN_EXECUTABLE AND TARGET gRPC::grpc_cpp_plugin) get_target_property(_GRPC_CPP_PLUGIN_EXECUTABLE gRPC::grpc_cpp_plugin LOCATION) endif() # 最后尝试:直接使用which命令查找 if(NOT _GRPC_CPP_PLUGIN_EXECUTABLE) execute_process( COMMAND which grpc_cpp_plugin OUTPUT_VARIABLE _GRPC_CPP_PLUGIN_EXECUTABLE OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) endif() if(NOT _GRPC_CPP_PLUGIN_EXECUTABLE) message(FATAL_ERROR "未找到 'grpc_cpp_plugin' 程序。\n" "请安装gRPC开发包:\n" " Ubuntu/Debian: sudo apt install protobuf-compiler-grpc libgrpc++-dev\n" " 或者设置 GRPC_CPP_PLUGIN_DIR 变量指向插件所在目录" ) else() message(STATUS "找到gRPC编译器插件: ${_GRPC_CPP_PLUGIN_EXECUTABLE}") endif() # 4. 查找protoc编译器(优先使用与libprotobuf匹配的版本) # 首先尝试从 Protobuf 包获取 if(TARGET protobuf::protoc) get_target_property(_PROTOBUF_PROTOC protobuf::protoc IMPORTED_LOCATION) if(NOT _PROTOBUF_PROTOC) get_target_property(_PROTOBUF_PROTOC protobuf::protoc LOCATION) endif() endif() # 如果没找到,使用 Protobuf_PROTOC_EXECUTABLE(由 find_package 设置) if(NOT _PROTOBUF_PROTOC AND Protobuf_PROTOC_EXECUTABLE) set(_PROTOBUF_PROTOC "${Protobuf_PROTOC_EXECUTABLE}") endif() # 最后回退到系统路径 if(NOT _PROTOBUF_PROTOC) find_program(_PROTOBUF_PROTOC protoc HINTS /usr/bin /usr/local/bin ) endif() if(NOT _PROTOBUF_PROTOC) message(FATAL_ERROR "未找到protoc编译器") endif() # 获取 protoc 版本并验证 execute_process( COMMAND ${_PROTOBUF_PROTOC} --version OUTPUT_VARIABLE PROTOC_VERSION_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) message(STATUS "找到Protobuf编译器: ${_PROTOBUF_PROTOC}") message(STATUS "Protoc版本: ${PROTOC_VERSION_OUTPUT}") message(STATUS "Protobuf库版本: ${Protobuf_VERSION}") # 警告版本不匹配 string(REGEX MATCH "[0-9]+\\.[0-9]+" PROTOC_MAJOR_MINOR "${PROTOC_VERSION_OUTPUT}") string(REGEX MATCH "^[0-9]+\\.[0-9]+" PROTOBUF_MAJOR_MINOR "${Protobuf_VERSION}") if(NOT "${PROTOC_MAJOR_MINOR}" STREQUAL "${PROTOBUF_MAJOR_MINOR}") message(WARNING "protoc版本(${PROTOC_MAJOR_MINOR})与protobuf库版本(${PROTOBUF_MAJOR_MINOR})不匹配!\n" "这可能导致编译错误。请确保 protoc 和 libprotobuf-dev 版本一致。" ) endif() # ========== 包含头文件目录 ========== # 强制优先使用系统 protobuf 头文件(/usr/include),避免 /usr/local 下的冲突版本 include_directories(BEFORE SYSTEM /usr/include ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR} ) # 只有当 Protobuf_INCLUDE_DIRS 是系统路径时才添加 if(Protobuf_INCLUDE_DIRS AND "${Protobuf_INCLUDE_DIRS}" MATCHES "^/usr/include") include_directories(${Protobuf_INCLUDE_DIRS}) endif() if(DEFINED GRPC_INCLUDE_DIRS) # 过滤掉 /usr/local 路径,只保留系统路径 foreach(_dir ${GRPC_INCLUDE_DIRS}) if(NOT "${_dir}" MATCHES "^/usr/local") include_directories(${_dir}) endif() endforeach() endif() # ========== 编译Proto文件 ========== get_filename_component(DEV_PROTO "${CMAKE_CURRENT_SOURCE_DIR}/protos/device-sm.proto" ABSOLUTE) get_filename_component(DEV_PROTO_DIR "${DEV_PROTO}" PATH) set(DEV_PROTO_SRC "${CMAKE_CURRENT_BINARY_DIR}/device-sm.pb.cc") set(DEV_PROTO_HDR "${CMAKE_CURRENT_BINARY_DIR}/device-sm.pb.h") set(DEV_GRPC_SRC "${CMAKE_CURRENT_BINARY_DIR}/device-sm.grpc.pb.cc") set(DEV_GRPC_HDR "${CMAKE_CURRENT_BINARY_DIR}/device-sm.grpc.pb.h") # 清理旧的proto生成文件(防止跨版本不兼容) # 检查是否存在旧文件,如果protobuf版本变化则删除重新生成 set(PROTO_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/.protobuf_version") if(EXISTS "${PROTO_VERSION_FILE}") file(READ "${PROTO_VERSION_FILE}" CACHED_PROTOBUF_VERSION) string(STRIP "${CACHED_PROTOBUF_VERSION}" CACHED_PROTOBUF_VERSION) if(NOT "${CACHED_PROTOBUF_VERSION}" STREQUAL "${Protobuf_VERSION}") message(STATUS "Protobuf版本变化 (${CACHED_PROTOBUF_VERSION} -> ${Protobuf_VERSION}),清理旧的proto文件") file(REMOVE "${DEV_PROTO_SRC}" "${DEV_PROTO_HDR}" "${DEV_GRPC_SRC}" "${DEV_GRPC_HDR}") endif() endif() file(WRITE "${PROTO_VERSION_FILE}" "${Protobuf_VERSION}") # 使用找到的protoc路径(兼容不同版本) add_custom_command( OUTPUT "${DEV_PROTO_SRC}" "${DEV_PROTO_HDR}" "${DEV_GRPC_SRC}" "${DEV_GRPC_HDR}" COMMAND ${_PROTOBUF_PROTOC} ARGS --grpc_out="${CMAKE_CURRENT_BINARY_DIR}" --cpp_out="${CMAKE_CURRENT_BINARY_DIR}" -I "${DEV_PROTO_DIR}" --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}" "${DEV_PROTO}" DEPENDS "${DEV_PROTO}" COMMENT "生成gRPC proto代码 (protobuf ${Protobuf_VERSION})" ) add_custom_target(GenerateProto ALL DEPENDS "${DEV_PROTO_SRC}" "${DEV_PROTO_HDR}" "${DEV_GRPC_SRC}" "${DEV_GRPC_HDR}" ) # ========== 编译动态库 ========== set(SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/devGrpcClient.cpp ${DEV_PROTO_SRC} ${DEV_GRPC_SRC} ) # 根据选项编译为共享库或静态库 if(BUILD_SHARED_LIBS) add_library(devGrpcClient SHARED ${SRC_FILES}) else() add_library(devGrpcClient STATIC ${SRC_FILES}) endif() add_dependencies(devGrpcClient GenerateProto) set_target_properties(devGrpcClient PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION 1 OUTPUT_NAME "devGrpcClient" ) # ========== 链接依赖库 ========== if(GRPC_STATIC_LINK) message(STATUS "使用静态链接模式(提高跨系统兼容性)") # 查找静态库 find_library(GRPC_STATIC_LIB libgrpc.a grpc PATHS /usr/lib /usr/local/lib /usr/lib/aarch64-linux-gnu /usr/lib/x86_64-linux-gnu) find_library(GRPCPP_STATIC_LIB libgrpc++.a grpc++ PATHS /usr/lib /usr/local/lib /usr/lib/aarch64-linux-gnu /usr/lib/x86_64-linux-gnu) find_library(GPR_STATIC_LIB libgpr.a gpr PATHS /usr/lib /usr/local/lib /usr/lib/aarch64-linux-gnu /usr/lib/x86_64-linux-gnu) find_library(ADDRESS_SORTING_LIB libaddress_sorting.a address_sorting PATHS /usr/lib /usr/local/lib /usr/lib/aarch64-linux-gnu /usr/lib/x86_64-linux-gnu) find_library(PROTOBUF_STATIC_LIB libprotobuf.a protobuf PATHS /usr/lib /usr/local/lib /usr/lib/aarch64-linux-gnu /usr/lib/x86_64-linux-gnu) find_library(CARES_LIB cares PATHS /usr/lib /usr/local/lib /usr/lib/aarch64-linux-gnu /usr/lib/x86_64-linux-gnu) find_library(RE2_LIB re2 PATHS /usr/lib /usr/local/lib /usr/lib/aarch64-linux-gnu /usr/lib/x86_64-linux-gnu) find_library(UPBDEFS_LIB upb_json_lib upb PATHS /usr/lib /usr/local/lib /usr/lib/aarch64-linux-gnu /usr/lib/x86_64-linux-gnu) # 检查是否找到静态库 if(GRPCPP_STATIC_LIB AND GRPC_STATIC_LIB AND PROTOBUF_STATIC_LIB) message(STATUS "找到gRPC静态库: ${GRPCPP_STATIC_LIB}") message(STATUS "找到Protobuf静态库: ${PROTOBUF_STATIC_LIB}") # 构建静态链接列表 set(GRPC_LINK_LIBS ${GRPCPP_STATIC_LIB} ${GRPC_STATIC_LIB} ${GPR_STATIC_LIB} ${PROTOBUF_STATIC_LIB} ) # 添加可选依赖 if(ADDRESS_SORTING_LIB) list(APPEND GRPC_LINK_LIBS ${ADDRESS_SORTING_LIB}) endif() if(CARES_LIB) list(APPEND GRPC_LINK_LIBS ${CARES_LIB}) endif() if(RE2_LIB) list(APPEND GRPC_LINK_LIBS ${RE2_LIB}) endif() if(UPBDEFS_LIB) list(APPEND GRPC_LINK_LIBS ${UPBDEFS_LIB}) endif() target_link_libraries(devGrpcClient PRIVATE -Wl,--start-group ${GRPC_LINK_LIBS} -Wl,--end-group pthread dl z ssl crypto ) else() message(WARNING "未找到gRPC静态库,回退到动态链接模式") set(GRPC_STATIC_LINK OFF) endif() endif() if(NOT GRPC_STATIC_LINK) message(STATUS "使用动态链接模式") # 优先使用CMake目标 if(TARGET gRPC::grpc++) target_link_libraries(devGrpcClient PRIVATE gRPC::grpc++ protobuf::libprotobuf pthread dl z ssl crypto ) elseif(DEFINED GRPC_LIBRARIES) # 使用pkg-config找到的库 target_link_libraries(devGrpcClient PRIVATE ${GRPC_LIBRARIES} ${Protobuf_LIBRARIES} pthread dl z ssl crypto ) else() # 最后回退:直接链接库名 target_link_libraries(devGrpcClient PRIVATE grpc++ grpc gpr protobuf pthread dl z ssl crypto ) endif() endif() # ========== 输出到交付目录 ========== if(BUILD_SHARED_LIBS) add_custom_command(TARGET devGrpcClient POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "$" "${DELIVER_DIR}/lib/" COMMAND ${CMAKE_COMMAND} -E create_symlink "$" "${DELIVER_DIR}/lib/$" COMMAND ${CMAKE_COMMAND} -E create_symlink "$" "${DELIVER_DIR}/lib/$" COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/src/devGrpcClient.h" "${DELIVER_DIR}/include/" ) else() # 静态库只需要复制 .a 文件 add_custom_command(TARGET devGrpcClient POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "$" "${DELIVER_DIR}/lib/" COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/src/devGrpcClient.h" "${DELIVER_DIR}/include/" ) endif() # ========== 打印配置摘要 ========== message(STATUS "========== devGrpcClient 配置摘要 ==========") message(STATUS "Protobuf版本: ${Protobuf_VERSION}") message(STATUS "gRPC版本: ${gRPC_VERSION}") message(STATUS "静态链接gRPC: ${GRPC_STATIC_LINK}") message(STATUS "编译为共享库: ${BUILD_SHARED_LIBS}") message(STATUS "交付目录: ${DELIVER_DIR}") message(STATUS "============================================")