问题
I wish to use proto and managed by cmake.
Suppose the files are as follow
.
├── app1
│ ├── app1.cpp
│ └── app1.proto
├── CMakeLists.txt
├── common
│ ├── bar
│ │ ├── bar.proto
│ │ └── CMakeLists.txt
│ └── foo
│ ├── CMakeLists.txt
│ └── foo.proto
└── README.md
If I wanna just generate some cpp files, I can just use command
$ protoc --cpp_out=build common/bar/bar.proto
$ protoc --cpp_out=build common/foo/foo.proto
and the desired files can be generated.
However, if I use CMake, it will report an error as follow :
[ 14%] Running C++ protocol buffer compiler on foo.proto
Scanning dependencies of target FooLib
[ 28%] Building CXX object common/foo/CMakeFiles/FooLib.dir/foo.pb.cc.o
Linking CXX static library libFooLib.a
[ 28%] Built target FooLib
[ 42%] Running C++ protocol buffer compiler on bar.proto
Scanning dependencies of target BarLib
[ 57%] Building CXX object common/bar/CMakeFiles/BarLib.dir/bar.pb.cc.o
/Users/yu/Workspace/res/proto/project/build/common/bar/bar.pb.cc:79:5: error: no member named 'protobuf_AddDesc_common_2ffoo_2ffoo_2eproto' in the global namespace
::protobuf_AddDesc_common_2ffoo_2ffoo_2eproto();
~~^
1 error generated.
make[2]: *** [common/bar/CMakeFiles/BarLib.dir/bar.pb.cc.o] Error 1
make[1]: *** [common/bar/CMakeFiles/BarLib.dir/all] Error 2
make: *** [all] Error 2
Any help are appreciated.
回答1:
I'm not too familiar with cmake, so I could be missing something, but it appears to me that cmake's PROTOBUF_GENERATE_CPP
is broken in the case where your proto files span multiple directories.
When PROTOBUF_GENERATE_CPP
is placed in common/foo/CMakeLists.txt
, cmake executes the following command to generate foo.pb.*
:
cd /home/kenton/test/cmake-proto/common/foo &&
/usr/bin/protoc --cpp_out /<project-dir>/common/foo \
-I /<project-dir>/common/foo -I /<project-dir> \
/<project-dir>/common/foo/foo.proto
When PROTOBUF_GENERATE_CPP
is placed in the top-level CMakeLists.txt
, cmake executes the following command to generate foo.pb.*
:
/usr/bin/protoc --cpp_out /<project-dir> \
-I /<project-dir>/common/foo -I /<project-dir> \
/<project-dir>/common/foo/foo.proto
Both of these are wrong. Specifically, the flag -I /<project-dir>/common/foo
should not be there. -I
should only be used to specify the root of the source tree. Only -I /<project-dir>
is correct. Again, this appears to be a bug in cmake, since AFAICT there is no documented way to make it do the right thing.
This is confusing the Protobuf compiler because it uses the file's path relative to the root directory to decide its canonical name. In your case, foo.proto
's canonical name should be common/foo/foo.proto
, because this is the name that all other files use to import it. The symbol protobuf_AddDesc_common_2ffoo_2ffoo_2eproto
is constructed from this canonical name (you can see the canonical name is encoded in it, using _xx
as escapes for /
and .
characters). Unfortunately, because cmake is passing -I /<project-dir>/common/foo
to protoc
, the protobuf compiler concludes that foo.proto
's canonical name is simply foo.proto
, with no prefix (because it thinks that /<project-dir>/common/foo
is the source root directory, due to the incorrect -I
flag). This causes problems when other files import foo.proto
, because in those other files the protocol compiler believes that the canonical name is common/foo/foo.proto
. So, it generates mismatching code.
Here is the documentation for FindProtobuf. It does not contain any discussion of this issue, which makes me suspect that the authors are not aware that it is broken. However, this would seem very surprising since cmake is widely-used and this code is years old. Perhaps the documentation is simply incomplete? I would suggest filing a bug.
回答2:
Thanks for the help via CMake's mailing list, I got some point.
We may prepare a follow code as workaround.
if(DEFINED PROTOBUF_IMPORT_DIRS)
foreach(DIR ${PROTOBUF_IMPORT_DIRS})
get_filename_component(ABS_PATH ${DIR} ABSOLUTE)
list(FIND _protobuf_include_path ${ABS_PATH} _contains_already)
if(${_contains_already} EQUAL -1)
list(APPEND _protobuf_include_path -I ${ABS_PATH})
endif()
endforeach()
endif()
LIST(APPEND PROTOBUF_IMPORT_DIRS "SOME_PATHS")
can add more data after -I flag.
来源:https://stackoverflow.com/questions/29720410/no-member-found-when-use-cmake-construct-proto