protobuf新版本(21+)安装记录
安装过protobuf不下10次,每次安装都会踩坑。而这次,踩的坑更深,原因是protobuf21+版本引用了第三方库。
一开始,我用的macos系统搭建环境,使用brew包管理器安装 protobuf:
evenoyan@MacBook-Pro ~ % brew install protobuf
安装完成后,开始编写cmakelist文件:
1 cmake_minimum_required(VERSION 3.10.0) 2 3 set(TARGET_NAME demo_server) 4 project(${TARGET_NAME} VERSION 0.1.0 LANGUAGES C CXX) 5 6 #set(CMAKE_VERBOSE_MAKEFILE ON) 7 8 # C++17 9 set(CMAKE_CXX_STANDARD 17) 10 11 # 定义C++宏,当前工作目录 12 add_definitions(-DWORKSPACE_PATH="${CMAKE_SOURCE_DIR}") 13 14 # 公共代码位置 15 set(PUBLIC_DIR ${CMAKE_SOURCE_DIR}/../public) 16 17 # protobuf 文件位置 18 set(PROTO_DIR ${PUBLIC_DIR}/proto) 19 file(GLOB_RECURSE PROTO_FILES ${PROTO_DIR}/*.proto) 20 21 # protubuf 生成文件输出位置 22 set(PROTO_PB_DIR ${PUBLIC_DIR}/proto_gen) 23 24 file(GLOB_RECURSE PROTO_GEN_INCLUDE ${PROTO_PB_DIR}/*.h) 25 file(GLOB_RECURSE PROTO_GEN_SOURCES ${PROTO_PB_DIR}/*.cc) 26 27 # include 头文件目录 28 include_directories( 29 ${PROTO_GEN_INCLUDE} 30 ${PUBLIC_DIR} 31 ${PUBLIC_DIR}/include 32 ${CMAKE_SOURCE_DIR}/include 33 ) 34 35 # source .cpp文件目录,要编译它们 36 file(GLOB CPP_SOURCE_FILES 37 ${PROTO_GEN_SOURCES} 38 ${PUBLIC_DIR}/*.cpp 39 ${PUBLIC_DIR}/source/*.cpp 40 ${CMAKE_SOURCE_DIR}/source/*.cpp 41 ${CMAKE_SOURCE_DIR}/*.cpp 42 ) 43 44 add_executable(${TARGET_NAME} ${CPP_SOURCE_FILES}) 45 46 # 支持c++11 thread 47 target_link_libraries(${TARGET_NAME} pthread) 48 49 # 寻找 protobuf 50 find_package(Protobuf REQUIRED) 51 include_directories(${Protobuf_INCLUDE_DIRS}) 52 53 # 链接 protobuf 库 54 target_link_libraries(${TARGET_NAME} ${Protobuf_LIBRARIES})
然后,新建 .proto 文件,并使用 protoc 命令来生成 .h .cc文件
(这里不叙述了,重点不是这里)
但工程编译时,在最后的链接阶段发生了错误:未定义符号,就是找不到类型或函数的实现:
Undefined symbols for architecture x86_64: "void absl::lts_20240722::log_internal::LogMessage::CopyToEncodedBuffer<(absl::lts_20240722::log_internal::LogMessage::StringType)0>(std::__1::basic_string_view<char, std::__1::char_traits<char>>)", referenced from: absl::lts_20240722::log_internal::LogMessage& absl::lts_20240722::log_internal::LogMessage::operator<<<19>(char const (&) [19]) in login.pb.cc.o "absl::lts_20240722::log_internal::LogMessage& absl::lts_20240722::log_internal::LogMessage::operator<<<unsigned long, 0>(unsigned long const&)", referenced from: absl::lts_20240722::log_internal::LogMessage::operator<<(unsigned long) in login.pb.cc.o "absl::lts_20240722::log_internal::LogMessageFatal::LogMessageFatal(char const*, int, std::__1::basic_string_view<char, std::__1::char_traits<char>>)", referenced from: DATA_Account::SharedDtor(google::protobuf::MessageLite&) in account_info.pb.cc.o DATA_Account::MergeImpl(google::protobuf::MessageLite&, google::protobuf::MessageLite const&) in account_info.pb.cc.o DATA_Account::InternalSwap(DATA_Account*) in account_info.pb.cc.o google::protobuf::internal::ClassData::full() const in account_info.pb.cc.o MSG_W2D_DATASAVE::SharedDtor(google::protobuf::MessageLite&) in data_save.pb.cc.o MSG_W2D_DATASAVE::MergeImpl(google::protobuf::MessageLite&, google::protobuf::MessageLite const&) in data_save.pb.cc.o MSG_W2D_DATASAVE::InternalSwap(MSG_W2D_DATASAVE*) in data_save.pb.cc.o ... "absl::lts_20240722::log_internal::LogMessageFatal::~LogMessageFatal()", referenced from: DATA_Account::SharedDtor(google::protobuf::MessageLite&) in account_info.pb.cc.o ......(还有好多)
查了一下,需要链接absl库,于是在cmakelist文件中增加absl库的链接:
1 # 寻找abseil库,protobuf需要 2 find_package(absl REQUIRED) 3 4 # 链接 abseil 库 5 # protobuf@25+ 依赖 abseil,如果不这样写,protobuf就会找不到absl的实现代码, 6 # 这似乎是 protobuf 的 bug 7 # 建议使用 protobuf 21及之前的版本 8 # 这里的坑巨大,可能导致protobuf运行时的崩溃,具体请查看: 9 # https://blog.gitcode.com/763172bc3b68275eea57297078deeb90.html 10 # 在安装时,推荐使用源码编译安装 protobuf 与 abseil,虽然这极其复杂 11 target_link_libraries(${TARGET_NAME} 12 absl::absl_check 13 absl::absl_log 14 absl::algorithm 15 absl::base 16 absl::bind_front 17 absl::bits 18 absl::btree 19 absl::cleanup 20 absl::cord 21 absl::core_headers 22 absl::debugging 23 absl::die_if_null 24 absl::dynamic_annotations 25 absl::flags 26 absl::flat_hash_map 27 absl::flat_hash_set 28 absl::function_ref 29 absl::hash 30 absl::layout 31 absl::log_initialize 32 absl::log_severity 33 absl::memory 34 absl::node_hash_map 35 absl::node_hash_set 36 absl::optional 37 absl::span 38 absl::status 39 absl::statusor 40 absl::strings 41 absl::synchronization 42 absl::time 43 absl::type_traits 44 absl::utility 45 absl::variant 46 )
不要怀疑为什么要链接这么多的库,因为坑爹的absl就是这样干的:
列出名字包含absl的库:
evenoyan@MacBook-Pro ~ % pkg-config --list-package-names | grep absl absl_strerror absl_cord absl_log absl_bad_any_cast absl_hashtable_debug absl_random_internal_mock_helpers absl_malloc_internal absl_bind_front absl_log_internal_structured absl_absl_check absl_fixed_array absl_status_matchers absl_string_view absl_log_structured absl_has_ostream_operator absl_flat_hash_map absl_scoped_mock_log absl_random_internal_randen_hwaes_impl absl_statusor absl_strings absl_random_internal_distribution_caller absl_low_level_hash absl_variant absl_compare absl_node_hash_map absl_log_internal_conditions absl_container_common absl_core_headers absl_random_random absl_flags_program_name absl_memory absl_bad_any_cast_impl absl_cordz_update_scope absl_cordz_info absl_flags_path_util absl_log_internal_check_impl absl_flags_commandlineflag_internal absl_cleanup_internal absl_flags_reflection absl_log_flags absl_crc_internal absl_cordz_statistics absl_fast_type_id absl_config absl_random_internal_pool_urbg absl_flags_private_handle_accessor absl_common_policy_traits absl_vlog_config_internal absl_utf8_for_code_point absl_raw_hash_set absl_bounded_utf8_length_sequence absl_scoped_set_env absl_log_internal_message absl_absl_log absl_die_if_null absl_log_internal_flags absl_graphcycles_internal absl_random_seed_sequences absl_flat_hash_set absl_exponential_biased absl_bits absl_decode_rust_punycode absl_cord_test_helpers absl_time absl_log_internal_log_impl absl_base_internal absl_throw_delegate absl_endian absl_charset absl_random_internal_randen absl_crc32c absl_random_internal_randen_engine absl_layout absl_flags_usage_internal absl_random_internal_generate_real absl_flags_parse absl_numeric absl_hashtablez_sampler absl_numeric_representation absl_no_destructor absl_city absl_errno_saver absl_crc_cord_state absl_raw_hash_map absl_log_internal_fnmatch absl_if_constexpr absl_node_slot_policy absl_poison absl_symbolize absl_flags_commandlineflag absl_atomic_hook absl_compressed_tuple absl_bad_variant_access absl_log_sink absl_periodic_sampler absl_vlog_is_on absl_spy_hash_state absl_any_invocable absl_crc_cpu_detect absl_random_internal_salted_seed_seq absl_log_internal_globals absl_log_globals absl_demangle_internal absl_optional absl_hash_container_defaults absl_hash_policy_traits absl_node_hash_set absl_flags_usage absl_random_internal_nonsecure_base absl_hashtable_debug_hooks absl_container_memory absl_flags_internal absl_log_initialize absl_cord_internal absl_str_format_internal absl_log_internal_log_sink_set absl_algorithm_container absl_inlined_vector_internal absl_raw_logging_internal absl_cleanup absl_random_internal_randen_slow absl_random_distributions absl_dynamic_annotations absl_log_internal_check_op absl_bad_optional_access absl_random_bit_gen_ref absl_overload absl_flags absl_log_internal_append_truncated absl_time_zone absl_random_seed_gen_exception absl_type_traits absl_hash_function_defaults absl_random_internal_distribution_test_util absl_meta absl_utility absl_non_temporal_memcpy absl_btree absl_civil_time absl_examine_stack absl_spinlock_wait absl_random_internal_traits absl_strings_internal absl_cordz_functions absl_algorithm absl_cordz_update_tracker absl_random_internal_fast_uniform_bits absl_synchronization absl_pretty_function absl_log_internal_config absl_log_internal_voidify absl_base absl_span absl_log_internal_nullguard absl_random_internal_fastmath absl_hash_testing absl_random_internal_platform absl_log_internal_strip absl_log_streamer absl_kernel_timeout_internal absl_inlined_vector absl_demangle_rust absl_int128 absl_failure_signal_handler absl_random_internal_iostream_state_saver absl_cordz_sample_token absl_cordz_handle absl_random_mocking_bit_gen absl_log_entry absl_debugging absl_random_internal_wide_multiply absl_random_internal_pcg_engine absl_str_format absl_hash absl_log_sink_registry absl_log_internal_nullstream absl_prefetch absl_flags_marshalling absl_stacktrace absl_log_internal_proto absl_random_internal_seed_material absl_leak_check absl_status absl_nullability absl_any absl_function_ref absl_debugging_internal absl_log_internal_format absl_random_internal_uniform_helper absl_check absl_sample_recorder absl_random_internal_randen_hwaes absl_flags_config absl_log_severity absl_non_temporal_arm_intrinsics absl_absl_vlog_is_on
ok,这次链接不失败了,但是遇到了新的问题。
现在,在一个proto文件中定义一个message,而这个message中,存在另一个类型的message成员:
1 syntax = "proto3"; 2 3 message DATA_Account 4 { 5 optional string nick = 1; //昵称 6 optional int64 last_login = 2; //最后登陆时间 7 optional int64 total_online = 3; //总计在线时间 8 } 9 10 message DATA_PlayerInfo 11 { 12 optional int64 player_id = 1; //玩家id 13 optional DATA_Account account_info = 2; //玩家账号相关数据 14 15 }
1 message MSG_D2W_Login 2 { 3 optional EMessageErrorCode err = 1; //错误码 4 optional int32 fd = 2; //world server 需要的玩家 fd 5 optional DATA_PlayerInfo player_info = 3; //玩家数据 6 }
于是,它就在 ParseFromString 函数中水灵灵地炸了。
1 MSG_D2W_Login from; 2 from.ParseFromString(data);
(崩溃的图没截,就是 DATA_PlayerInfo 中的 account_info 成员是一个空指针)

这非常坑,坑得我不得不采用另一种方式来安装protobuf.
现在,我不想在macos中搭建环境了,我一直用的clang,因为它安装的gcc压根儿用不了!找不到某些头文件!除非使用xcode。(这个坑我实在是不想爬了)
以下是ubuntu中安装过程:
下载 protobuf 源码:
https://codeload.github.com/protocolbuffers/protobuf/zip/refs/heads/main
下载 abseil 源码:
https://codeload.github.com/abseil/abseil-cpp/zip/refs/heads/master
even@evenubuntu:~$ ls abseil-cpp-master protobuf-main
首先安装abseil:
在 abseil-cpp-master 文件夹的 CMakeList.txt 中增加c++版本约定为c++17
set(CMAKE_CXX_STANDARD 17)
然后开始编译安装
even@evenubuntu:~$ cd abseil-cpp-master/ even@evenubuntu:~/abseil-cpp-master$ mkdir build even@evenubuntu:~/abseil-cpp-master$ cd build/ even@evenubuntu:~/abseil-cpp-master/build$ cmake .. CMake Warning at CMakeLists.txt:183 (message): The default and system-level install directories are unsupported except in LTS releases of Abseil. Please set CMAKE_INSTALL_PREFIX to install Abseil in your source or build tree directly. -- Configuring done (0.5s) -- Generating done (0.7s) -- Build files have been written to: /home/even/abseil-cpp-master/build even@evenubuntu:~/abseil-cpp-master/build$ cmake -DCMAKE_INSTALL_PREFIX=/usr/local .. CMake Warning at CMakeLists.txt:183 (message): The default and system-level install directories are unsupported except in LTS releases of Abseil. Please set CMAKE_INSTALL_PREFIX to install Abseil in your source or build tree directly. -- Configuring done (0.2s) -- Generating done (0.3s) -- Build files have been written to: /home/even/abseil-cpp-master/build even@evenubuntu:~/abseil-cpp-master/build$ sudo make & make install
然后安装 protobuf:
在 protobuf-main 文件夹的 CMakeList.txt 中增加c++版本约定为c++17
set(CMAKE_CXX_STANDARD 17)
然后编译安装
even@evenubuntu:~/protobuf-main/build$ cmake -DCMAKE_INSTALL_PREFIX=/usr/local .. -- The C compiler identification is GNU 13.3.0 -- The CXX compiler identification is GNU 13.3.0 -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working C compiler: /usr/bin/cc - skipped -- Detecting C compile features -- Detecting C compile features - done -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: /usr/bin/c++ - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- protobuf version: 32.0.0 -- Performing Test protobuf_HAVE_LD_VERSION_SCRIPT -- Performing Test protobuf_HAVE_LD_VERSION_SCRIPT - Success -- Performing Test CMAKE_HAVE_LIBC_PTHREAD -- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success -- Found Threads: TRUE -- Found ZLIB: /usr/lib/x86_64-linux-gnu/libz.so (found version "1.3") -- Performing Test protobuf_HAVE_BUILTIN_ATOMICS -- Performing Test protobuf_HAVE_BUILTIN_ATOMICS - Success -- Configuring done (1.0s) -- Generating done (0.1s) -- Build files have been written to: /home/even/protobuf-main/build
even@evenubuntu:~/protobuf-main/build$ cmake --build . --config Release
even@evenubuntu:~/protobuf-main/build$ cmake --install . --config Release
安装完成后,编译测试代码,又出现链接错误:
/usr/bin/ld: /usr/local/lib/libprotobuf.a(generated_message_tctable_lite.cc.o): in function `utf8_range::IsStructurallyValid(std::basic_string_view<char, std::char_traits<char> >)': generated_message_tctable_lite.cc:(.text._ZN10utf8_range19IsStructurallyValidESt17basic_string_viewIcSt11char_traitsIcEE[_ZN10utf8_range19IsStructurallyValidESt17basic_string_viewIcSt11char_traitsIcEE]+0x40): undefined reference to `utf8_range_IsValid' /usr/bin/ld: /usr/local/lib/libprotobuf.a(parse_context.cc.o): in function `utf8_range::SpanStructurallyValid(std::basic_string_view<char, std::char_traits<char> >)': parse_context.cc:(.text._ZN10utf8_range21SpanStructurallyValidESt17basic_string_viewIcSt11char_traitsIcEE[_ZN10utf8_range21SpanStructurallyValidESt17basic_string_viewIcSt11char_traitsIcEE]+0x40): undefined reference to `utf8_range_ValidPrefix' collect2: error: ld returned 1 exit status make[2]: *** [CMakeFiles/test_repo.dir/build.make:202: test_repo] Error 1 make[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/test_repo.dir/all] Error 2 make: *** [Makefile:91: all] Error 2
这次又发现 utf8_range 找不到...
这又是怎么回事?直到我安装protobuf时( cmake --install . --config Release 命令),有一段输出引起了我的注意:
1 even@evenubuntu:~/protobuf-main$ sudo cmake --install ./build --config Release 2 [sudo] password for even: 3 -- Installing: /usr/local/lib/cmake/utf8_range/utf8_range-targets.cmake 4 -- Installing: /usr/local/lib/libutf8_validity.a 5 -- Installing: /usr/local/lib/libutf8_range.a 6 -- Installing: /usr/local/lib/cmake/utf8_range/utf8_range-config.cmake 7 -- Installing: /usr/local/lib/pkgconfig/utf8_range.pc 8 -- Up-to-date: /usr/local/include/utf8_range.h 9 -- Up-to-date: /usr/local/include/utf8_validity.h 10 -- Installing: /usr/local/lib/libprotobuf-lite.a
......
看见 utf8_range.pc 了没?
也就是说,utf8_range不是什么函数,它是一个库,protobuf32版本不仅依赖absl,还依赖 utf8_range
去pkg-config里碰碰运气看看有没有:
even@evenubuntu:/usr/bin$ pkg-config --list-all | grep utf8 absl_bounded_utf8_length_sequence absl_bounded_utf8_length_sequence - Abseil bounded_utf8_length_sequence library absl_utf8_for_code_point absl_utf8_for_code_point - Abseil utf8_for_code_point library utf8_range UTF8 Range - Google's UTF8 Library
有!那就在cmakelist文件中链接上:
1 find_package(utf8_range REQUIRED) 2 target_link_libraries(${TARGET_NAME} utf8_range)
这次终于编译成功了......
另外,如何得知protobuf需要absl中的哪些库?毕竟有那么多,少一个可能都编译不过。
在protobuf的源码文件夹中,有一个cmake文件夹:
even@evenubuntu:~$ cd protobuf-main/ even@evenubuntu:~/protobuf-main$ ls appveyor.bat CMakeLists.txt examples maven_dev_install.json Protobuf.podspec toolchain appveyor.yml CODE_OF_CONDUCT.md fix_permissions.sh maven_install.json protobuf_release.bzl upb bazel compatibility generate_descriptor_proto.sh MODULE.bazel protobuf_version.bzl upb_generator benchmarks conformance global.json Neverlink_jruby_jars.patch python version.json build CONTRIBUTING.md go objectivec README.md WORKSPACE BUILD.bazel CONTRIBUTORS.txt google3_export_generated_files.sh patches regenerate_stale_files.sh WORKSPACE.bzlmod build_defs csharp hpb php ruby Cargo.bazel.lock Disable_bundle_install.patch hpb_generator pkg rust Cargo.lock docs java PrivacyInfo.xcprivacy SECURITY.md ci editions LICENSE protobuf.bzl src cmake editors lua protobuf_deps.bzl third_party
打开其中的 abseil-cpp.cmake 文件,里面有:
even@evenubuntu:~/protobuf-main$ cd cmake even@evenubuntu:~/protobuf-main/cmake$ ls abseil-cpp.cmake gtest.cmake libprotobuf.cmake protobuf-config-version.cmake.in protoc.cmake version.rc.in BUILD.bazel install.cmake libprotobuf-lite.cmake protobuf-generate.cmake README.md conformance.cmake installed_bin_golden.txt libprotoc.cmake protobuf-lite.pc.cmake tests.cmake dependencies.cmake installed_include_golden.txt libupb.cmake protobuf-module.cmake.in upb_generators.cmake dependencies_generator.py installed_lib_shared_golden.txt protobuf-config.cmake.in protobuf-options.cmake upb.pc.cmake examples.cmake installed_lib_static_golden.txt protobuf-configure-target.cmake protobuf.pc.cmake utf8_range.cmake even@evenubuntu:~/protobuf-main/cmake$ cat abseil-cpp.cmake # Setup our dependency on Abseil. if(protobuf_BUILD_TESTS) # Tell Abseil to build test-only helpers. set(ABSL_BUILD_TEST_HELPERS ON) # We depend on googletest too, so just tell Abseil to use the same one we've # already setup. set(ABSL_USE_EXTERNAL_GOOGLETEST ON) set(ABSL_FIND_GOOGLETEST OFF) endif() if (NOT TARGET absl::strings) if (NOT protobuf_FORCE_FETCH_DEPENDENCIES) # Use "CONFIG" as there is no built-in cmake module for absl. find_package(absl CONFIG) endif() # Fallback to fetching Abseil from github if it's not found locally. if (NOT absl_FOUND AND NOT protobuf_LOCAL_DEPENDENCIES_ONLY) include(${protobuf_SOURCE_DIR}/cmake/dependencies.cmake) message(STATUS "Fallback to downloading Abseil ${abseil-cpp-version} from GitHub") include(FetchContent) FetchContent_Declare( absl GIT_REPOSITORY "https://github.com/abseil/abseil-cpp.git" GIT_TAG "${abseil-cpp-version}" ) if (protobuf_INSTALL) # When protobuf_INSTALL is enabled and Abseil will be built as a module, # Abseil will be installed along with protobuf for convenience. set(ABSL_ENABLE_INSTALL ON) endif() FetchContent_MakeAvailable(absl) endif() endif() if (NOT TARGET absl::strings) message(FATAL_ERROR "Cannot find abseil-cpp dependency that's needed to build protobuf.\n") endif() set(_protobuf_FIND_ABSL "if(NOT TARGET absl::strings)\n find_package(absl CONFIG)\nendif()") if (BUILD_SHARED_LIBS AND MSVC) # On MSVC Abseil is bundled into a single DLL. # This condition is necessary as of abseil 20230125.3 when abseil is consumed # via add_subdirectory, the abseil_dll target is named abseil_dll, while if # abseil is consumed via find_package, the target is called absl::abseil_dll # Once https://github.com/abseil/abseil-cpp/pull/1466 is merged and released # in the minimum version of abseil required by protobuf, it is possible to # always link absl::abseil_dll and absl::abseil_test_dll and remove the if set(protobuf_ABSL_USED_TARGETS absl::abseil_dll) set(protobuf_ABSL_USED_TEST_TARGETS absl::abseil_test_dll) else() set(protobuf_ABSL_USED_TARGETS absl::absl_check absl::absl_log absl::algorithm absl::base absl::bind_front absl::bits absl::btree absl::cleanup absl::cord absl::core_headers absl::debugging absl::die_if_null absl::dynamic_annotations absl::flags absl::flat_hash_map absl::flat_hash_set absl::function_ref absl::hash absl::layout absl::log_initialize absl::log_globals absl::log_severity absl::memory absl::node_hash_map absl::node_hash_set absl::optional absl::random_distributions absl::random_random absl::span absl::status absl::statusor absl::strings absl::synchronization absl::time absl::type_traits absl::utility ) set(protobuf_ABSL_USED_TEST_TARGETS absl::scoped_mock_log ) endif ()
什么破玩意,搞得这么恶心,我都想自己造轮子了。
浙公网安备 33010602011771号