about summary refs log tree commit diff
path: root/cmake/FindProtobufTargets.cmake
blob: ab196118d73152a0998a181aacfab46fccc5d3dc (plain) (blame)
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# ~~~
# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ~~~

#[=======================================================================[.rst:
FindProtobufTargets
-------------------

A module to use `Protobuf` with less complications.

Using ``find_package(Protobuf)`` should be simple, but it is not.

CMake provides a ``FindProtobuf`` module. Unfortunately it does not generate
`protobuf::*` targets until CMake-3.9, and `protobuf::protoc` does not
appear until CMake-3.10.

The CMake-config files generated by `protobuf` always create these targets,
but on some Linux distributions (e.g. Fedora>=29, and openSUSE-Tumbleweed) there
are system packages for protobuf, but these packages are installed without the
CMake-config files. One must either use the ``FindProtobuf`` module, find the
libraries via `pkg-config`, or find the libraries manually.

When the CMake-config files are installed they produce the same targets as
recent versions of ``FindProtobuf``. However, they do not produce the
`Protobuf_LIBRARY`, `Protobuf_INCLUDE_DIR`, etc. that are generated by the
module. Furthermore, the `protobuf::protoc` library is not usable when loaded
from the CMake-config files: its ``IMPORTED_LOCATION`` variable is not defined.

This module is designed to provide a single, uniform, ``find_package()``
module that always produces the same outputs:

- It always generates the ``protobuf::*`` targets.
- It always defines ``ProtobufTargets_FOUND`` and ``ProtobufTargets_VERSION``.
- It *prefers* using the CMake config files if they are available.
- It fallsback on the ``FindProtobuf`` module if the config files are not found.
- It populates any missing targets and their properties.

The following :prop_tgt:`IMPORTED` targets are defined:

``protobuf::libprotobuf``
  The protobuf library.
``protobuf::libprotobuf-lite``
  The protobuf lite library.
``protobuf::libprotoc``
  The protoc library.
``protobuf::protoc``
  The protoc compiler.

Example:

.. code-block:: cmake

  find_package(ProtobufTargets REQUIRED)
  add_executable(bar bar.cc)
  target_link_libraries(bar PRIVATE protobuf::libprotobuf)

#]=======================================================================]

if (protobuf_DEBUG)
    message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
                   "protobuf_USE_STATIC_LIBS = ${protobuf_USE_STATIC_LIBS}"
                   " ProtobufTargets = ${ProtobufTargets_FOUND}")
endif ()

# Always load thread support, even on Windows.
find_package(Threads REQUIRED)

# First try to use the ``protobufConfig.cmake`` or ``protobuf-config.cmake``
# file if it was installed. This is common on systems (or package managers)
# where protobuf was compiled and installed with `CMake`. Note that on Linux
# this *must* be all lowercase ``protobuf``, while on Windows it does not
# matter.
find_package(protobuf CONFIG)

if (protobuf_DEBUG)
    message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
                   "protobuf_FOUND = ${protobuf_FOUND}"
                   " protobuf_VERSION = ${protobuf_VERSION}")
endif ()

if (protobuf_FOUND)
    set(ProtobufTargets_FOUND 1)
    set(ProtobufTargets_VERSION ${protobuf_VERSION})
    if (protobuf_DEBUG)
        message(
            STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
                   "ProtobufTargets_FOUND = ${ProtobufTargets_FOUND}"
                   " ProtobufTargets_VERSION = ${ProtobufTargets_VERSION}")
    endif ()
else ()
    find_package(Protobuf QUIET)
    if (Protobuf_FOUND)
        set(ProtobufTargets_FOUND 1)
        set(ProtobufTargets_VERSION ${Protobuf_VERSION})

        if (NOT TARGET protobuf::libprotobuf)
            add_library(protobuf::libprotobuf IMPORTED INTERFACE)
            set_property(
                TARGET protobuf::libprotobuf
                PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Protobuf_INCLUDE_DIR})
            set_property(
                TARGET protobuf::libprotobuf APPEND
                PROPERTY INTERFACE_LINK_LIBRARIES ${Protobuf_LIBRARY}
                         Threads::Threads)
        endif ()

        if (NOT TARGET protobuf::libprotobuf-lite)
            add_library(protobuf::libprotobuf-lite IMPORTED INTERFACE)
            set_property(
                TARGET protobuf::libprotobuf-lite
                PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Protobuf_INCLUDE_DIR})
            set_property(
                TARGET protobuf::libprotobuf-lite APPEND
                PROPERTY INTERFACE_LINK_LIBRARIES ${Protobuf_LITE_LIBRARY}
                         Threads::Threads)
        endif ()

        if (NOT TARGET protobuf::libprotoc)
            add_library(protobuf::libprotoc IMPORTED INTERFACE)
            set_property(
                TARGET protobuf::libprotoc
                PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Protobuf_INCLUDE_DIR})
            set_property(
                TARGET protobuf::libprotoc APPEND
                PROPERTY INTERFACE_LINK_LIBRARIES ${Protobuf_PROTOC_LIBRARY}
                         Threads::Threads)
        endif ()
    endif ()
endif ()

if (protobuf_DEBUG)
    message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
                   "ProtobufTargets_FOUND = ${ProtobufTargets_FOUND}"
                   " ProtobufTargets_VERSION = ${ProtobufTargets_VERSION}")
endif ()

# We also should try to find the protobuf C++ plugin for the protocol buffers
# compiler.
if (ProtobufTargets_FOUND AND NOT TARGET protobuf::protoc)
    add_executable(protobuf::protoc IMPORTED)

    # Discover the protoc compiler location.
    find_program(
        _protobuf_PROTOC_EXECUTABLE
        NAMES protoc
        DOC "The Google Protocol Buffers Compiler")
    if (protobuf_DEBUG)
        message(
            STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
                   "ProtobufTargets_FOUND = ${ProtobufTargets_FOUND}"
                   " ProtobufTargets_VERSION = ${ProtobufTargets_VERSION}"
                   " EXE = ${_protobuf_PROTOC_EXECUTABLE}")
    endif ()
    set_property(TARGET protobuf::protoc
                 PROPERTY IMPORTED_LOCATION ${_protobuf_PROTOC_EXECUTABLE})
    set_property(
        TARGET protobuf::protoc PROPERTY IMPORTED_LOCATION_DEBUG
                                         ${_protobuf_PROTOC_EXECUTABLE})
    set_property(
        TARGET protobuf::protoc PROPERTY IMPORTED_LOCATION_RELEASE
                                         ${_protobuf_PROTOC_EXECUTABLE})
    unset(_protobuf_PROTOC_EXECUTABLE)

    if (protobuf_DEBUG)
        get_target_property(_protobuf_PROTOC_EXECUTABLE protobuf::protoc
                            IMPORTED_LOCATION)
        message(
            STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
                   "LOCATION=${_protobuf_PROTOC_EXECUTABLE}")
    endif ()
endif ()

if (protobuf_DEBUG)
    message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
                   "ProtobufTargets_FOUND = ${ProtobufTargets_FOUND}"
                   " ProtobufTargets_VERSION = ${ProtobufTargets_VERSION}")
    if (ProtobufTargets_FOUND)
        foreach (_target protobuf::libprotobuf protobuf::libprotobuf-lite
                         protobuf::libprotoc)
            if (NOT TARGET ${_target})
                message(
                    STATUS
                        "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
                        "target=${_target} is NOT a target")
            endif ()
        endforeach ()
        unset(_target)
    endif ()
endif ()

find_package_handle_standard_args(ProtobufTargets REQUIRED_VARS
                                  ProtobufTargets_FOUND ProtobufTargets_VERSION)