CMake doesn't work with Google Protobuf

2019-04-14 23:16发布

问题:

Unable to link protobuf library using CMake. My CMakeLists is

cmake_minimum_required(VERSION 3.6)
project(addressbook)

set(CMAKE_CXX_STANDARD 11)
set(PROJECT_NAME addressbook)

ADD_SUBDIRECTORY(proto)

INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
ADD_EXECUTABLE(main main.cpp)
TARGET_LINK_LIBRARIES(main proto ${PROTOBUF_LIBRARY})

and in proto subdirectory there is another CMakeLists.txt (that way it is done in github repo https://github.com/shaochuan/cmake-protobuf-example)

INCLUDE(FindProtobuf)
FIND_PACKAGE(Protobuf REQUIRED)
INCLUDE_DIRECTORIES(${PROTOBUF_INCLUDE_DIR})
PROTOBUF_GENERATE_CPP(PROTO_SRC PROTO_HEADER message.proto)
ADD_LIBRARY(proto ${PROTO_HEADER} ${PROTO_SRC})

But my IDE still outputs buch of lines like

CMakeFiles/main.dir/main.cpp.o: In function main': /home/camille/ClionProjects/protobuf/main.cpp:42: undefined reference togoogle::protobuf::internal::VerifyVersion(int, int, char const*)' /home/camille/ClionProjects/protobuf/main.cpp:49: undefined reference to tutorial::AddressBook::AddressBook()' /home/camille/ClionProjects/protobuf/main.cpp:54: undefined reference togoogle::protobuf::Message::ParseFromIstream(std::istream*)'

Where is my mistake? How do I make it work?

回答1:

The variable you need to pass to target_link_libraries is Protobuf_LIBRARIES. See documentation.



回答2:

Your program fails to link because ${PROTOBUF_LIBRARY} is empty in the scope of your top-level CMakeLists.txt. This happens because calling add_subdirectory creates a child scope, and the Protobuf_XXX variables set by find_package(Protobuf REQUIRED) are only in that child scope.

A good way to fix this is to add the following to proto/CMakeLists.txt:

target_link_libraries(proto INTERFACE ${Protobuf_LIBRARIES})

This instructs targets that link to proto to also link to ${Protobuf_LIBRARIES}. Now you can simplify target_link_libraries in your top-level CMakeLists.txt:

target_link_libraries(addressbook proto)

On a side note, you could also use e.g.

target_link_libraries(${PROJECT_NAME} INTERFACE ... )

${PROJECT_NAME} resolves to whatever you have set in the project(...) statement in that CMakeLists.txt file.

Finally, note that this links to Protobuf_LIBRARIES instead of PROTOBUF_LIBRARY. Protobuf_LIBRARIES includes both the Protocol Buffers libraries and the dependent Pthreads library.



回答3:

Watch out for variable name case: With CMake 3.6 and later, the FindProtobuf module input and output variables were all renamed from PROTOBUF_ to Protobuf_ (see release notes), so using Protobuf_ works with CMake 3.6, but fails with undefined reference with an earlier version.

To be on the safe side, either use the old style

target_link_libraries(${PROJECT_NAME} INTERFACE ${PROTOBUF_LIBRARIES}))

or force everyone to use at least CMake 3.6

cmake_minimum_required(VERSION 3.6)

Also, there is a resolved bug report in the Kitware cmake issue tracker with more information on how to diagnose such issues.