CMakeLists.txt 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. cmake_minimum_required(VERSION 3.10)
  2. project(devGrpcClient VERSION 1.0.0 LANGUAGES CXX)
  3. # 设置编译选项
  4. set(CMAKE_CXX_STANDARD 17)
  5. set(CMAKE_CXX_STANDARD_REQUIRED ON)
  6. add_compile_options(-fPIC -g -Wall)
  7. # 目标目录
  8. set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
  9. get_filename_component(DELIVER_DIR "${CMAKE_BINARY_DIR}/../../Deliver" ABSOLUTE)
  10. file(MAKE_DIRECTORY "${DELIVER_DIR}/lib" "${DELIVER_DIR}/include")
  11. # ========== 强制使用动态链接和共享库 ==========
  12. # ARM Ubuntu 的系统静态库没有 -fPIC,无法链接到共享库
  13. # 使用 CACHE FORCE 确保覆盖任何缓存中的旧值
  14. set(GRPC_STATIC_LINK OFF CACHE BOOL "静态链接gRPC和Protobuf" FORCE)
  15. set(BUILD_SHARED_LIBS ON CACHE BOOL "编译为共享库(.so)" FORCE)
  16. # 强制输出消息确认配置
  17. message(STATUS "强制配置: GRPC_STATIC_LINK=${GRPC_STATIC_LINK}, BUILD_SHARED_LIBS=${BUILD_SHARED_LIBS}")
  18. # ========== 依赖查找:使用系统安装的 gRPC 和 Protobuf ==========
  19. # 强制使用系统路径,避免 /usr/local 下的多版本冲突
  20. set(CMAKE_FIND_USE_CMAKE_SYSTEM_PATH TRUE)
  21. set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH FALSE)
  22. # 1. 查找 Protobuf(优先使用系统路径)
  23. # 设置提示路径为系统目录
  24. set(Protobuf_ROOT "/usr")
  25. set(CMAKE_PREFIX_PATH "/usr" ${CMAKE_PREFIX_PATH})
  26. find_package(Protobuf REQUIRED HINTS /usr NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH)
  27. message(STATUS "找到系统Protobuf版本: ${Protobuf_VERSION}")
  28. message(STATUS "Protobuf头文件路径: ${Protobuf_INCLUDE_DIRS}")
  29. # 2. 查找 gRPC(通过CONFIG模式,适用于现代gRPC安装)
  30. find_package(gRPC CONFIG QUIET)
  31. if (gRPC_FOUND)
  32. message(STATUS "通过CONFIG模式找到系统gRPC版本: ${gRPC_VERSION}")
  33. else()
  34. # 回退到MODULE模式(旧版或自定义安装)
  35. find_package(gRPC MODULE QUIET)
  36. if (gRPC_FOUND)
  37. message(STATUS "通过MODULE模式找到gRPC")
  38. else()
  39. message(STATUS "未通过find_package找到gRPC,尝试使用pkg-config")
  40. find_package(PkgConfig REQUIRED)
  41. pkg_check_modules(GRPC REQUIRED grpc++ grpc)
  42. set(GRPC_LIBRARIES ${GRPC_LIBRARIES})
  43. set(GRPC_INCLUDE_DIRS ${GRPC_INCLUDE_DIRS})
  44. endif()
  45. endif()
  46. # 3. 查找gRPC代码生成器插件(增强查找逻辑)
  47. find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin
  48. HINTS
  49. ${GRPC_CPP_PLUGIN_DIR}
  50. /usr/bin
  51. /usr/local/bin
  52. /usr/lib/grpc
  53. /usr/local/lib/grpc
  54. PATH_SUFFIXES bin
  55. )
  56. # 如果还是找不到,尝试从gRPC目标获取
  57. if(NOT _GRPC_CPP_PLUGIN_EXECUTABLE AND TARGET gRPC::grpc_cpp_plugin)
  58. get_target_property(_GRPC_CPP_PLUGIN_EXECUTABLE gRPC::grpc_cpp_plugin LOCATION)
  59. endif()
  60. # 最后尝试:直接使用which命令查找
  61. if(NOT _GRPC_CPP_PLUGIN_EXECUTABLE)
  62. execute_process(
  63. COMMAND which grpc_cpp_plugin
  64. OUTPUT_VARIABLE _GRPC_CPP_PLUGIN_EXECUTABLE
  65. OUTPUT_STRIP_TRAILING_WHITESPACE
  66. ERROR_QUIET
  67. )
  68. endif()
  69. if(NOT _GRPC_CPP_PLUGIN_EXECUTABLE)
  70. message(FATAL_ERROR
  71. "未找到 'grpc_cpp_plugin' 程序。\n"
  72. "请安装gRPC开发包:\n"
  73. " Ubuntu/Debian: sudo apt install protobuf-compiler-grpc libgrpc++-dev\n"
  74. " 或者设置 GRPC_CPP_PLUGIN_DIR 变量指向插件所在目录"
  75. )
  76. else()
  77. message(STATUS "找到gRPC编译器插件: ${_GRPC_CPP_PLUGIN_EXECUTABLE}")
  78. endif()
  79. # 4. 查找protoc编译器(优先使用与libprotobuf匹配的版本)
  80. # 首先尝试从 Protobuf 包获取
  81. if(TARGET protobuf::protoc)
  82. get_target_property(_PROTOBUF_PROTOC protobuf::protoc IMPORTED_LOCATION)
  83. if(NOT _PROTOBUF_PROTOC)
  84. get_target_property(_PROTOBUF_PROTOC protobuf::protoc LOCATION)
  85. endif()
  86. endif()
  87. # 如果没找到,使用 Protobuf_PROTOC_EXECUTABLE(由 find_package 设置)
  88. if(NOT _PROTOBUF_PROTOC AND Protobuf_PROTOC_EXECUTABLE)
  89. set(_PROTOBUF_PROTOC "${Protobuf_PROTOC_EXECUTABLE}")
  90. endif()
  91. # 最后回退到系统路径
  92. if(NOT _PROTOBUF_PROTOC)
  93. find_program(_PROTOBUF_PROTOC protoc
  94. HINTS
  95. /usr/bin
  96. /usr/local/bin
  97. )
  98. endif()
  99. if(NOT _PROTOBUF_PROTOC)
  100. message(FATAL_ERROR "未找到protoc编译器")
  101. endif()
  102. # 获取 protoc 版本并验证
  103. execute_process(
  104. COMMAND ${_PROTOBUF_PROTOC} --version
  105. OUTPUT_VARIABLE PROTOC_VERSION_OUTPUT
  106. OUTPUT_STRIP_TRAILING_WHITESPACE
  107. ERROR_QUIET
  108. )
  109. message(STATUS "找到Protobuf编译器: ${_PROTOBUF_PROTOC}")
  110. message(STATUS "Protoc版本: ${PROTOC_VERSION_OUTPUT}")
  111. message(STATUS "Protobuf库版本: ${Protobuf_VERSION}")
  112. # 警告版本不匹配
  113. string(REGEX MATCH "[0-9]+\\.[0-9]+" PROTOC_MAJOR_MINOR "${PROTOC_VERSION_OUTPUT}")
  114. string(REGEX MATCH "^[0-9]+\\.[0-9]+" PROTOBUF_MAJOR_MINOR "${Protobuf_VERSION}")
  115. if(NOT "${PROTOC_MAJOR_MINOR}" STREQUAL "${PROTOBUF_MAJOR_MINOR}")
  116. message(WARNING
  117. "protoc版本(${PROTOC_MAJOR_MINOR})与protobuf库版本(${PROTOBUF_MAJOR_MINOR})不匹配!\n"
  118. "这可能导致编译错误。请确保 protoc 和 libprotobuf-dev 版本一致。"
  119. )
  120. endif()
  121. # ========== 包含头文件目录 ==========
  122. # 强制优先使用系统 protobuf 头文件(/usr/include),避免 /usr/local 下的冲突版本
  123. include_directories(BEFORE SYSTEM
  124. /usr/include
  125. )
  126. include_directories(
  127. ${CMAKE_CURRENT_SOURCE_DIR}/src
  128. ${CMAKE_CURRENT_BINARY_DIR}
  129. )
  130. # 只有当 Protobuf_INCLUDE_DIRS 是系统路径时才添加
  131. if(Protobuf_INCLUDE_DIRS AND "${Protobuf_INCLUDE_DIRS}" MATCHES "^/usr/include")
  132. include_directories(${Protobuf_INCLUDE_DIRS})
  133. endif()
  134. if(DEFINED GRPC_INCLUDE_DIRS)
  135. # 过滤掉 /usr/local 路径,只保留系统路径
  136. foreach(_dir ${GRPC_INCLUDE_DIRS})
  137. if(NOT "${_dir}" MATCHES "^/usr/local")
  138. include_directories(${_dir})
  139. endif()
  140. endforeach()
  141. endif()
  142. # ========== 编译Proto文件 ==========
  143. get_filename_component(DEV_PROTO "${CMAKE_CURRENT_SOURCE_DIR}/protos/device-sm.proto" ABSOLUTE)
  144. get_filename_component(DEV_PROTO_DIR "${DEV_PROTO}" PATH)
  145. set(DEV_PROTO_SRC "${CMAKE_CURRENT_BINARY_DIR}/device-sm.pb.cc")
  146. set(DEV_PROTO_HDR "${CMAKE_CURRENT_BINARY_DIR}/device-sm.pb.h")
  147. set(DEV_GRPC_SRC "${CMAKE_CURRENT_BINARY_DIR}/device-sm.grpc.pb.cc")
  148. set(DEV_GRPC_HDR "${CMAKE_CURRENT_BINARY_DIR}/device-sm.grpc.pb.h")
  149. # 清理旧的proto生成文件(防止跨版本不兼容)
  150. # 检查是否存在旧文件,如果protobuf版本变化则删除重新生成
  151. set(PROTO_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/.protobuf_version")
  152. if(EXISTS "${PROTO_VERSION_FILE}")
  153. file(READ "${PROTO_VERSION_FILE}" CACHED_PROTOBUF_VERSION)
  154. string(STRIP "${CACHED_PROTOBUF_VERSION}" CACHED_PROTOBUF_VERSION)
  155. if(NOT "${CACHED_PROTOBUF_VERSION}" STREQUAL "${Protobuf_VERSION}")
  156. message(STATUS "Protobuf版本变化 (${CACHED_PROTOBUF_VERSION} -> ${Protobuf_VERSION}),清理旧的proto文件")
  157. file(REMOVE "${DEV_PROTO_SRC}" "${DEV_PROTO_HDR}" "${DEV_GRPC_SRC}" "${DEV_GRPC_HDR}")
  158. endif()
  159. endif()
  160. file(WRITE "${PROTO_VERSION_FILE}" "${Protobuf_VERSION}")
  161. # 使用找到的protoc路径(兼容不同版本)
  162. add_custom_command(
  163. OUTPUT "${DEV_PROTO_SRC}" "${DEV_PROTO_HDR}" "${DEV_GRPC_SRC}" "${DEV_GRPC_HDR}"
  164. COMMAND ${_PROTOBUF_PROTOC}
  165. ARGS
  166. --grpc_out="${CMAKE_CURRENT_BINARY_DIR}"
  167. --cpp_out="${CMAKE_CURRENT_BINARY_DIR}"
  168. -I "${DEV_PROTO_DIR}"
  169. --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}"
  170. "${DEV_PROTO}"
  171. DEPENDS "${DEV_PROTO}"
  172. COMMENT "生成gRPC proto代码 (protobuf ${Protobuf_VERSION})"
  173. )
  174. add_custom_target(GenerateProto ALL
  175. DEPENDS "${DEV_PROTO_SRC}" "${DEV_PROTO_HDR}" "${DEV_GRPC_SRC}" "${DEV_GRPC_HDR}"
  176. )
  177. # ========== 编译动态库 ==========
  178. set(SRC_FILES
  179. ${CMAKE_CURRENT_SOURCE_DIR}/src/devGrpcClient.cpp
  180. ${DEV_PROTO_SRC}
  181. ${DEV_GRPC_SRC}
  182. )
  183. # 根据选项编译为共享库或静态库
  184. if(BUILD_SHARED_LIBS)
  185. add_library(devGrpcClient SHARED ${SRC_FILES})
  186. else()
  187. add_library(devGrpcClient STATIC ${SRC_FILES})
  188. endif()
  189. add_dependencies(devGrpcClient GenerateProto)
  190. set_target_properties(devGrpcClient PROPERTIES
  191. VERSION ${PROJECT_VERSION}
  192. SOVERSION 1
  193. OUTPUT_NAME "devGrpcClient"
  194. )
  195. # ========== 链接依赖库 ==========
  196. if(GRPC_STATIC_LINK)
  197. message(STATUS "使用静态链接模式(提高跨系统兼容性)")
  198. # 查找静态库
  199. 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)
  200. 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)
  201. 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)
  202. 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)
  203. 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)
  204. find_library(CARES_LIB cares PATHS /usr/lib /usr/local/lib /usr/lib/aarch64-linux-gnu /usr/lib/x86_64-linux-gnu)
  205. find_library(RE2_LIB re2 PATHS /usr/lib /usr/local/lib /usr/lib/aarch64-linux-gnu /usr/lib/x86_64-linux-gnu)
  206. 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)
  207. # 检查是否找到静态库
  208. if(GRPCPP_STATIC_LIB AND GRPC_STATIC_LIB AND PROTOBUF_STATIC_LIB)
  209. message(STATUS "找到gRPC静态库: ${GRPCPP_STATIC_LIB}")
  210. message(STATUS "找到Protobuf静态库: ${PROTOBUF_STATIC_LIB}")
  211. # 构建静态链接列表
  212. set(GRPC_LINK_LIBS
  213. ${GRPCPP_STATIC_LIB}
  214. ${GRPC_STATIC_LIB}
  215. ${GPR_STATIC_LIB}
  216. ${PROTOBUF_STATIC_LIB}
  217. )
  218. # 添加可选依赖
  219. if(ADDRESS_SORTING_LIB)
  220. list(APPEND GRPC_LINK_LIBS ${ADDRESS_SORTING_LIB})
  221. endif()
  222. if(CARES_LIB)
  223. list(APPEND GRPC_LINK_LIBS ${CARES_LIB})
  224. endif()
  225. if(RE2_LIB)
  226. list(APPEND GRPC_LINK_LIBS ${RE2_LIB})
  227. endif()
  228. if(UPBDEFS_LIB)
  229. list(APPEND GRPC_LINK_LIBS ${UPBDEFS_LIB})
  230. endif()
  231. target_link_libraries(devGrpcClient PRIVATE
  232. -Wl,--start-group
  233. ${GRPC_LINK_LIBS}
  234. -Wl,--end-group
  235. pthread dl z ssl crypto
  236. )
  237. else()
  238. message(WARNING "未找到gRPC静态库,回退到动态链接模式")
  239. set(GRPC_STATIC_LINK OFF)
  240. endif()
  241. endif()
  242. if(NOT GRPC_STATIC_LINK)
  243. message(STATUS "使用动态链接模式")
  244. # 优先使用CMake目标
  245. if(TARGET gRPC::grpc++)
  246. target_link_libraries(devGrpcClient PRIVATE
  247. gRPC::grpc++
  248. protobuf::libprotobuf
  249. pthread dl z ssl crypto
  250. )
  251. elseif(DEFINED GRPC_LIBRARIES)
  252. # 使用pkg-config找到的库
  253. target_link_libraries(devGrpcClient PRIVATE
  254. ${GRPC_LIBRARIES}
  255. ${Protobuf_LIBRARIES}
  256. pthread dl z ssl crypto
  257. )
  258. else()
  259. # 最后回退:直接链接库名
  260. target_link_libraries(devGrpcClient PRIVATE
  261. grpc++ grpc gpr
  262. protobuf
  263. pthread dl z ssl crypto
  264. )
  265. endif()
  266. endif()
  267. # ========== 输出到交付目录 ==========
  268. if(BUILD_SHARED_LIBS)
  269. add_custom_command(TARGET devGrpcClient POST_BUILD
  270. COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:devGrpcClient>" "${DELIVER_DIR}/lib/"
  271. COMMAND ${CMAKE_COMMAND} -E create_symlink "$<TARGET_FILE_NAME:devGrpcClient>" "${DELIVER_DIR}/lib/$<TARGET_SONAME_FILE_NAME:devGrpcClient>"
  272. COMMAND ${CMAKE_COMMAND} -E create_symlink "$<TARGET_SONAME_FILE_NAME:devGrpcClient>" "${DELIVER_DIR}/lib/$<TARGET_LINKER_FILE_NAME:devGrpcClient>"
  273. COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/src/devGrpcClient.h" "${DELIVER_DIR}/include/"
  274. )
  275. else()
  276. # 静态库只需要复制 .a 文件
  277. add_custom_command(TARGET devGrpcClient POST_BUILD
  278. COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:devGrpcClient>" "${DELIVER_DIR}/lib/"
  279. COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/src/devGrpcClient.h" "${DELIVER_DIR}/include/"
  280. )
  281. endif()
  282. # ========== 打印配置摘要 ==========
  283. message(STATUS "========== devGrpcClient 配置摘要 ==========")
  284. message(STATUS "Protobuf版本: ${Protobuf_VERSION}")
  285. message(STATUS "gRPC版本: ${gRPC_VERSION}")
  286. message(STATUS "静态链接gRPC: ${GRPC_STATIC_LINK}")
  287. message(STATUS "编译为共享库: ${BUILD_SHARED_LIBS}")
  288. message(STATUS "交付目录: ${DELIVER_DIR}")
  289. message(STATUS "============================================")