cpm-cmake / CPM.cmake

📦 CMake's missing package manager. A small CMake script for setup-free, cross-platform, reproducible dependency management.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

I cannot add protobuf using CPM.

M46f988b814 opened this issue · comments

I have a small example.
Here is my protobuf file

syntax = "proto3";

message Example{
    double version=13;
    double d = 1;
}

Here is my cpp file

#include "example.pb.h"
#include <iostream>

#include "example.pb.h"

int main()
{
    Example e;
    std::cout << "Testing" << std::endl;
}

Here is my CMakeLists.txt

cmake_minimum_required(VERSION 3.17)
project(protobuf_example)

# Include CPM.cmake
include(cmake/CPM.cmake)

# Use CPM to fetch and configure Protobuf
CPMAddPackage(
    NAME protobuf
    GITHUB_REPOSITORY protocolbuffers/protobuf
    VERSION 3.21.12
    OPTIONS("-Dprotobuf_BUILD_TESTS=OFF")
)

# Protobuf-generated files
set(PROTO_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/example.pb.cc)
set(PROTO_HDRS ${CMAKE_CURRENT_SOURCE_DIR}/example.pb.h)
add_custom_command(
    OUTPUT ${PROTO_SRCS} ${PROTO_HDRS}
    COMMAND ${CMAKE_BINARY_DIR}/_deps/protobuf-build/protoc
    --cpp_out=${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/example.proto
    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/example.proto
)

# Compile executable using generated protobuf files
add_executable(${PROJECT_NAME} 
    ${PROTO_SRCS}
    main.cpp
)

# Include directories for the generated protobuf files
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})

# Link against protobuf
target_link_libraries(${PROJECT_NAME} PRIVATE protobuf::libprotobuf)

If I run this with -G "Ninja" (ninja 1.10.1) then it fails at building protobuf at ~51%

[build] FAILED: ../example.pb.cc ../example.pb.h <path>/software/scratch/example.pb.cc <path>/software/scratch/example.pb.h 
[build] cd <path>/build/_deps/protobuf-build/protoc --cpp_out= <path>/example.proto
...
[build] [209/407  51% :: 12.310] Building CXX object _deps/protobuf-build/CMakeFiles/gmock.dir/third_party/googletest/googletest/src/gtest-all.cc.o
[build] ninja: build stopped: subcommand failed.

If I run this with -G "Unix Makefiles" (make version 4.3) then it will not try building, but if I go into build/_deps/protobuf-build and run make -j. Everything completes as expected. The problem here is that without building I can't link against protobuf. Any ideas with why CPM isn't working here?

More system info.
g++ 11.4.0
cmake 3.22.1
Ubuntu 22.04 LTS

Hmm It seems like the issue was some async behavior because protoc is needed before target link libraries perhaps. However I couldn't fix it with a sleep like follows. I think the build is run before the target link libraries perhaps?

# Use CPM to fetch and configure Protobuf
CPMAddPackage(
    NAME protobuf
    GITHUB_REPOSITORY protocolbuffers/protobuf
    VERSION 3.21.12
    OPTIONS("-Dprotobuf_BUILD_TESTS=OFF")
)

execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 180)

#Protobuf-generated files
set(PROTO_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/example.pb.cc)
set(PROTO_HDRS ${CMAKE_CURRENT_SOURCE_DIR}/example.pb.h)
add_custom_command(
    OUTPUT ${PROTO_SRCS} ${PROTO_HDRS}
    COMMAND ${CMAKE_BINARY_DIR}/_deps/protobuf-build/protoc
    --cpp_out=${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/example.proto
    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/example.proto
)

Solved. I was able to wait by adding protoc to the DEPENDS

cmake_minimum_required(VERSION 3.17)
project(protobuf_example)

# Include CPM.cmake
include(cmake/CPM.cmake)
set(CPM_SOURCE_CACHE ${CMAKE_CURRENT_SOURCE_DIR}/cache)

# Use CPM to fetch and configure Protobuf
CPMAddPackage(
    NAME protobuf
    GITHUB_REPOSITORY protocolbuffers/protobuf
    VERSION 3.21.12
    OPTIONS("-Dprotobuf_BUILD_TESTS=OFF")
)
#Protobuf-generated files
set(PROTO_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/example.pb.cc)
set(PROTO_HDRS ${CMAKE_CURRENT_SOURCE_DIR}/example.pb.h)
add_custom_command(
    OUTPUT ${PROTO_SRCS} ${PROTO_HDRS}
    COMMAND ${CMAKE_BINARY_DIR}/_deps/protobuf-build/protoc
    --cpp_out=${CMAKE_CURRENT_SOURCE_DIR}
    --proto_path=${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/example.proto
    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/example.proto ${CMAKE_BINARY_DIR}/_deps/protobuf-build/protoc
)

# Compile library using generated protobuf files
add_executable(${PROJECT_NAME} 
    ${PROTO_SRCS}
    main.cpp
)

# Include directories for the generated protobuf files
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})

# Link against protobuf
target_link_libraries(${PROJECT_NAME} PRIVATE protobuf::libprotobuf)

Won't using generator expression be a cleaner way to solve your issue?
ie $<TARGET_FILE:protoc> so you don't have specify full path to protoc that will most likely break?

add_custom_command(
    OUTPUT ${PROTO_SRCS} ${PROTO_HDRS}
    COMMAND $<TARGET_FILE:protoc>
    --cpp_out=${CMAKE_CURRENT_SOURCE_DIR}
    --proto_path=${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/example.proto
    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/example.proto protoc
)

I haven't tested but depending on CMAKE_BINARY_DIR is quite a bad practice for cases like that, since the binary could easily be relocated, or this will break when using multi config generator (msvc, ...)
Note that in DEPENDS I used the target protoc.