summaryrefslogtreecommitdiff
path: root/cmake/Modules/FindGRPC.cmake
blob: a559da499963eb51b3aace694765cd8d234647eb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
option(ENABLE_GRPC_REFLECTION "Link to gRPC Reflection library" OFF)

# FIXME(kirillbobyrev): Check if gRPC and Protobuf headers can be included at
# configure time.
find_package(Threads REQUIRED)
if (GRPC_INSTALL_PATH)
  # This setup requires gRPC to be built from sources using CMake and installed
  # to ${GRPC_INSTALL_PATH} via -DCMAKE_INSTALL_PREFIX=${GRPC_INSTALL_PATH}.
  # Libraries will be linked according to gRPC build policy which generates
  # static libraries when BUILD_SHARED_LIBS is Off and dynamic libraries when
  # it's On (NOTE: This is a variable passed to gRPC CMake build invocation,
  # LLVM's BUILD_SHARED_LIBS has no effect).
  set(protobuf_MODULE_COMPATIBLE TRUE)
  find_package(Protobuf CONFIG REQUIRED HINTS ${GRPC_INSTALL_PATH})
  message(STATUS "Using protobuf ${Protobuf_VERSION}")
  find_package(gRPC CONFIG REQUIRED HINTS ${GRPC_INSTALL_PATH})
  message(STATUS "Using gRPC ${gRPC_VERSION}")

  include_directories(${Protobuf_INCLUDE_DIRS})

  # gRPC CMake CONFIG gives the libraries slightly odd names, make them match
  # the conventional system-installed names.
  set_target_properties(protobuf::libprotobuf PROPERTIES IMPORTED_GLOBAL TRUE)
  add_library(protobuf ALIAS protobuf::libprotobuf)
  set_target_properties(gRPC::grpc++ PROPERTIES IMPORTED_GLOBAL TRUE)
  add_library(grpc++ ALIAS gRPC::grpc++)
  if (ENABLE_GRPC_REFLECTION)
    set_target_properties(gRPC::grpc++_reflection PROPERTIES IMPORTED_GLOBAL TRUE)
    add_library(grpc++_reflection ALIAS gRPC::grpc++_reflection)
  endif()

  set(GRPC_CPP_PLUGIN $<TARGET_FILE:gRPC::grpc_cpp_plugin>)
  set(PROTOC ${Protobuf_PROTOC_EXECUTABLE})
else()
  # This setup requires system-installed gRPC and Protobuf.
  # We always link dynamically in this mode. While the static libraries are
  # usually installed, the CMake files telling us *which* static libraries to
  # link are not.
  if (NOT BUILD_SHARED_LIBS)
    message(NOTICE "gRPC and Protobuf will be linked dynamically. If you want static linking, build gRPC from sources with -DBUILD_SHARED_LIBS=Off.")
  endif()
  find_program(GRPC_CPP_PLUGIN grpc_cpp_plugin)
  find_program(PROTOC protoc)
  if (NOT GRPC_CPP_PLUGIN OR NOT PROTOC)
    message(FATAL_ERROR "gRPC C++ Plugin and Protoc must be on $PATH for gRPC-enabled build.")
  endif()
  # On macOS the libraries are typically installed via Homebrew and are not on
  # the system path.
  set(GRPC_OPTS "")
  set(PROTOBUF_OPTS "")
  set(GRPC_INCLUDE_PATHS "")
  if (${APPLE})
    find_program(HOMEBREW brew)
    # If Homebrew is not found, the user might have installed libraries
    # manually. Fall back to the system path.
    if (HOMEBREW)
      execute_process(COMMAND ${HOMEBREW} --prefix grpc
        OUTPUT_VARIABLE GRPC_HOMEBREW_PATH
        RESULT_VARIABLE GRPC_HOMEBREW_RETURN_CODE
        OUTPUT_STRIP_TRAILING_WHITESPACE)
      execute_process(COMMAND ${HOMEBREW} --prefix protobuf
        OUTPUT_VARIABLE PROTOBUF_HOMEBREW_PATH
        RESULT_VARIABLE PROTOBUF_HOMEBREW_RETURN_CODE
        OUTPUT_STRIP_TRAILING_WHITESPACE)
      execute_process(COMMAND ${HOMEBREW} --prefix abseil
        OUTPUT_VARIABLE ABSL_HOMEBREW_PATH
        RESULT_VARIABLE ABSL_HOMEBREW_RETURN_CODE
        OUTPUT_STRIP_TRAILING_WHITESPACE)
      # If either library is not installed via Homebrew, fall back to the
      # system path.
      if (GRPC_HOMEBREW_RETURN_CODE EQUAL "0")
        list(APPEND GRPC_INCLUDE_PATHS ${GRPC_HOMEBREW_PATH}/include)
        list(APPEND GRPC_OPTS PATHS ${GRPC_HOMEBREW_PATH}/lib NO_DEFAULT_PATH)
      endif()
      if (PROTOBUF_HOMEBREW_RETURN_CODE EQUAL "0")
        list(APPEND GRPC_INCLUDE_PATHS ${PROTOBUF_HOMEBREW_PATH}/include)
        list(APPEND PROTOBUF_OPTS PATHS ${PROTOBUF_HOMEBREW_PATH}/lib NO_DEFAULT_PATH)
      endif()
      if (ABSL_HOMEBREW_RETURN_CODE EQUAL "0")
        list(APPEND GRPC_INCLUDE_PATHS ${ABSL_HOMEBREW_PATH}/include)
      endif()
    endif()
  endif()
  if(NOT TARGET grpc++)
    find_library(GRPC_LIBRARY grpc++ ${GRPC_OPTS} REQUIRED)
    add_library(grpc++ UNKNOWN IMPORTED GLOBAL)
    message(STATUS "Using grpc++: " ${GRPC_LIBRARY})
    set_target_properties(grpc++ PROPERTIES IMPORTED_LOCATION ${GRPC_LIBRARY})
    target_include_directories(grpc++ INTERFACE ${GRPC_INCLUDE_PATHS})
    if (ENABLE_GRPC_REFLECTION)
      find_library(GRPC_REFLECTION_LIBRARY grpc++_reflection ${GRPC_OPTS} REQUIRED)
      add_library(grpc++_reflection UNKNOWN IMPORTED GLOBAL)
      set_target_properties(grpc++_reflection PROPERTIES IMPORTED_LOCATION ${GRPC_REFLECTION_LIBRARY})
    endif()
    find_library(PROTOBUF_LIBRARY protobuf ${PROTOBUF_OPTS} REQUIRED)
    message(STATUS "Using protobuf: " ${PROTOBUF_LIBRARY})
    add_library(protobuf UNKNOWN IMPORTED GLOBAL)
    set_target_properties(protobuf PROPERTIES IMPORTED_LOCATION ${PROTOBUF_LIBRARY})
  endif()
endif()

if (ENABLE_GRPC_REFLECTION)
  set(REFLECTION_LIBRARY grpc++_reflection)
endif()

# Proto headers are generated in ${CMAKE_CURRENT_BINARY_DIR}.
# Libraries that use these headers should adjust the include path.
# If the "GRPC" argument is given, services are also generated.
# The DEPENDS list should name *.proto source files that are imported.
# They may be relative to the source dir or absolute (for generated protos).
function(generate_proto_sources GeneratedSource ProtoFile)
  cmake_parse_arguments(PARSE_ARGV 2 PROTO "GRPC" "" "DEPENDS")
  get_filename_component(ProtoSourceAbsolutePath "${CMAKE_CURRENT_SOURCE_DIR}/${ProtoFile}" ABSOLUTE)
  get_filename_component(ProtoSourcePath ${ProtoSourceAbsolutePath} PATH)
  get_filename_component(Basename ${ProtoSourceAbsolutePath} NAME_WLE)

  set(GeneratedProtoSource "${CMAKE_CURRENT_BINARY_DIR}/${Basename}.pb.cc")
  set(GeneratedProtoHeader "${CMAKE_CURRENT_BINARY_DIR}/${Basename}.pb.h")
  set(Flags
    --cpp_out="${CMAKE_CURRENT_BINARY_DIR}"
    --proto_path="${ProtoSourcePath}")
  if (PROTO_GRPC)
    list(APPEND Flags
      --grpc_out="${CMAKE_CURRENT_BINARY_DIR}"
      --plugin=protoc-gen-grpc="${GRPC_CPP_PLUGIN}")
    list(APPEND GeneratedProtoSource "${CMAKE_CURRENT_BINARY_DIR}/${Basename}.grpc.pb.cc")
    list(APPEND GeneratedProtoHeader "${CMAKE_CURRENT_BINARY_DIR}/${Basename}.grpc.pb.h")
  endif()
  add_custom_command(
        OUTPUT ${GeneratedProtoSource} ${GeneratedProtoHeader}
        COMMAND ${PROTOC}
        ARGS ${Flags} "${ProtoSourceAbsolutePath}"
        DEPENDS "${ProtoSourceAbsolutePath}")

  set(${GeneratedSource} ${GeneratedProtoSource} PARENT_SCOPE)

  # Ensure dependency headers are generated before dependent protos are built.
  # DEPENDS arg is a list of "Foo.proto". While they're logically relative to
  # the source dir, the generated headers we need are in the binary dir.
  foreach(ImportedProto IN LISTS PROTO_DEPENDS)
    # Foo.proto -> Foo.pb.h
    STRING(REGEX REPLACE "\\.proto$" ".pb.h" ImportedHeader "${ImportedProto}")
    # Foo.pb.h -> ${CMAKE_CURRENT_BINARY_DIR}/Foo.pb.h
    get_filename_component(ImportedHeader "${ImportedHeader}"
      ABSOLUTE
      BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
    # Compilation of each generated source depends on ${BINARY}/Foo.pb.h.
    foreach(Generated IN LISTS GeneratedProtoSource)
      # FIXME: CMake docs suggest OBJECT_DEPENDS isn't needed, but I can't get
      #        the recommended add_dependencies() approach to work.
      set_source_files_properties("${Generated}"
        PROPERTIES OBJECT_DEPENDS "${ImportedHeader}")
    endforeach(Generated)
  endforeach(ImportedProto)
endfunction()