最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - How to build a cmake project which uses protobuf? - Stack Overflow

programmeradmin1浏览0评论

I'm trying to create a small C++ test project which uses protobuf and the cmake build system.

I managed to get everything working with a single directory, and a single CMakeLists.txt.

However, this isn't a scalable structure.

The next change I attempted was to create a proto directory, and to move the *.proto files into this directory.

The project no longer builds, and I cannot figure out how to fix it.

I have searched the web for solutions, and also tried asking ChatGPT. ChatGPT went around in circles, and I found what appeared to be very variable solutions by searching the limited resources I could find online. It was not obvious to me which of the many variations might be the right way to go, but this is most likely because I am not an expert with cmake, so couldn't figure out how to put the various pieces together.

This is what I currently have:

protobuf-example/
  proto/
    CMakeLists.txt
    message.proto
  CMakeLists.txt
  main.cpp

proto/CMakeLists.txt

I am not totally sure what from the below is required. I have some understanding of what each of these lines does, but my understanding is not very solid.

set(PROTO_FILES message.proto)
set(GENERATED_PROTO_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated)
file(MAKE_DIRECTORY ${GENERATED_PROTO_DIR})

protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${PROTO_FILES})

add_library(proto_files STATIC ${PROTO_SRCS})

target_include_directories(proto_files PUBLIC ${Protobuf_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR})

target_link_libraries(proto_files PUBLIC ${Protobuf_LIBRARIES})

set(PROTO_GEN_SRCS ${PROTO_SRCS} PARENT_SCOPE)
set(PROTO_GEN_HDRS ${PROTO_HDRS} PARENT_SCOPE)
set(PROTO_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE)

message.proto

syntax = "proto3";

message Person {
  string name = 1;
  int32 age = 2;
  string email = 3;
}

CMakeLists.txt

On the other hand, I am familiar with these statements and I am fairly sure I know what each of them does.

cmake_minimum_required(VERSION 3.10)
project(ProtobufExample LANGUAGES CXX)

find_package(Protobuf REQUIRED)

add_subdirectory(proto)

add_executable(protobuf_example main.cpp ${PROTO_GEN_SRCS})

target_include_directories(protobuf_example PRIVATE ${Protobuf_INCLUDE_DIR})

target_link_libraries(protobuf_example PRIVATE ${proto_files})

main.cpp

As far as I am aware, this is just a standard example.

#include <iostream>
#include <fstream>
#include "message.pb.h"

void serializePerson(const std::string& filename) {
    Person person;
    person.set_name("John Doe");
    person.set_age(30);
    person.set_email("[email protected]");

    std::ofstream output(filename, std::ios::binary);
    if (!person.SerializeToOstream(&output)) {
        std::cerr << "Failed to serialize data." << std::endl;
    }
}

void deserializePerson(const std::string& filename) {
    Person person;
    std::ifstream input(filename, std::ios::binary);
    if (!person.ParseFromIstream(&input)) {
        std::cerr << "Failed to parse data." << std::endl;
    } else {
        std::cout << "Name: " << person.name() << "\n"
                  << "Age: " << person.age() << "\n"
                  << "Email: " << person.email() << std::endl;
    }
}

int main() {
    GOOGLE_PROTOBUF_VERIFY_VERSION;

    const std::string filename = "person.data";

    serializePerson(filename);
    deserializePerson(filename);

    google::protobuf::ShutdownProtobufLibrary();
    return 0;
}

Error

The specific error message is:

Cannot find source file:

    /home/user/cmake-protobuf-test/protobuf-example/build/proto/message.pb

This is most likely because the build/proto/generated directory is empty.

So it seems as if the protobuf_generate_cpp is not doing anything. However there are no errors or warnings produced relating to this.

I'm trying to create a small C++ test project which uses protobuf and the cmake build system.

I managed to get everything working with a single directory, and a single CMakeLists.txt.

However, this isn't a scalable structure.

The next change I attempted was to create a proto directory, and to move the *.proto files into this directory.

The project no longer builds, and I cannot figure out how to fix it.

I have searched the web for solutions, and also tried asking ChatGPT. ChatGPT went around in circles, and I found what appeared to be very variable solutions by searching the limited resources I could find online. It was not obvious to me which of the many variations might be the right way to go, but this is most likely because I am not an expert with cmake, so couldn't figure out how to put the various pieces together.

This is what I currently have:

protobuf-example/
  proto/
    CMakeLists.txt
    message.proto
  CMakeLists.txt
  main.cpp

proto/CMakeLists.txt

I am not totally sure what from the below is required. I have some understanding of what each of these lines does, but my understanding is not very solid.

set(PROTO_FILES message.proto)
set(GENERATED_PROTO_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated)
file(MAKE_DIRECTORY ${GENERATED_PROTO_DIR})

protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${PROTO_FILES})

add_library(proto_files STATIC ${PROTO_SRCS})

target_include_directories(proto_files PUBLIC ${Protobuf_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR})

target_link_libraries(proto_files PUBLIC ${Protobuf_LIBRARIES})

set(PROTO_GEN_SRCS ${PROTO_SRCS} PARENT_SCOPE)
set(PROTO_GEN_HDRS ${PROTO_HDRS} PARENT_SCOPE)
set(PROTO_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE)

message.proto

syntax = "proto3";

message Person {
  string name = 1;
  int32 age = 2;
  string email = 3;
}

CMakeLists.txt

On the other hand, I am familiar with these statements and I am fairly sure I know what each of them does.

cmake_minimum_required(VERSION 3.10)
project(ProtobufExample LANGUAGES CXX)

find_package(Protobuf REQUIRED)

add_subdirectory(proto)

add_executable(protobuf_example main.cpp ${PROTO_GEN_SRCS})

target_include_directories(protobuf_example PRIVATE ${Protobuf_INCLUDE_DIR})

target_link_libraries(protobuf_example PRIVATE ${proto_files})

main.cpp

As far as I am aware, this is just a standard example.

#include <iostream>
#include <fstream>
#include "message.pb.h"

void serializePerson(const std::string& filename) {
    Person person;
    person.set_name("John Doe");
    person.set_age(30);
    person.set_email("[email protected]");

    std::ofstream output(filename, std::ios::binary);
    if (!person.SerializeToOstream(&output)) {
        std::cerr << "Failed to serialize data." << std::endl;
    }
}

void deserializePerson(const std::string& filename) {
    Person person;
    std::ifstream input(filename, std::ios::binary);
    if (!person.ParseFromIstream(&input)) {
        std::cerr << "Failed to parse data." << std::endl;
    } else {
        std::cout << "Name: " << person.name() << "\n"
                  << "Age: " << person.age() << "\n"
                  << "Email: " << person.email() << std::endl;
    }
}

int main() {
    GOOGLE_PROTOBUF_VERIFY_VERSION;

    const std::string filename = "person.data";

    serializePerson(filename);
    deserializePerson(filename);

    google::protobuf::ShutdownProtobufLibrary();
    return 0;
}

Error

The specific error message is:

Cannot find source file:

    /home/user/cmake-protobuf-test/protobuf-example/build/proto/message.pb.cc

This is most likely because the build/proto/generated directory is empty.

So it seems as if the protobuf_generate_cpp is not doing anything. However there are no errors or warnings produced relating to this.

Share Improve this question asked 2 days ago user2138149user2138149 16.8k30 gold badges145 silver badges287 bronze badges 16
  • 1 You don't really need PROTO_GEN_SRCS, PROTO_GEN_HDRS, or PROTO_INCLUDE_DIR. The sources (or rather, their compiled object code) are part of the proto_files static library you're building, and the include dir is a public property of that target, so CMake will automatically apply it transitively to any other target that links to proto_files. – Miles Budnek Commented 2 days ago
  • 1 You should really use a package manager (e.g. conan(2)). And use something like find_package(protobuf) and thentarget_link_libraries(protobuf::protobuf) to setup your link and include paths. And depending on ChatGPT (generic LLM) for coding issues... its just not there yet – Pepijn Kramer Commented yesterday
  • 1 apt will "globally" install packages, forcing ALL your projects to use the same version. With conan you can select a version per project you are developing as well as debug/release versions, build for other target machines etc. In other words using a package manager provides a lot more features (essential to professional product development). So yes it might be a little more difficult to get started but on the long run it will make things a lot more manageable. – Pepijn Kramer Commented yesterday
  • 1 And about your remark: "Nobody uses them"... I think you should replace that with "Nobody you know, uses them". In fact package managers even in C++ are used a LOT. Companies wouldn't be able to build a lot of stable products without them. So be careful not to generalize from your own "feelings" and experiences :) – Pepijn Kramer Commented yesterday
  • 1 I am an experienced software architect and I wouldn't want to live without package managment. 1) To ensure we know which packages we are building with (security reasons). 2) To be able to build different versions of are software that build against different versions of libraries (all on the same machines). 3). To quickly test new versions of libraries and roll back when they have issues. 4) because devops is NOT core business (better to use tools that others have written and tested). And many many more. 5) manage dependency chains. – Pepijn Kramer Commented yesterday
 |  Show 11 more comments

1 Answer 1

Reset to default 2

Two issues are there. The first one:

add_executable(protobuf_example main.cpp ${PROTO_GEN_SRCS})

must be

add_executable(protobuf_example main.cpp)

because ${PROTO_GEN_SRCS} are sources of proto_files.

And the typo:

target_link_libraries(protobuf_example PRIVATE ${proto_files})

must be

target_link_libraries(protobuf_example PRIVATE proto_files)

because targets are not variables.

A nitpick. This is unnecessary

target_include_directories(protobuf_example PRIVATE ${Protobuf_INCLUDE_DIR})

because the further target_link_libraries brings #include to protobuf_example.

This results in that this is also unnecessary

set(PROTO_GEN_SRCS ${PROTO_SRCS} PARENT_SCOPE)
set(PROTO_GEN_HDRS ${PROTO_HDRS} PARENT_SCOPE)
set(PROTO_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE)
发布评论

评论列表(0)

  1. 暂无评论