在Flutter的C++代码中使用Protobuf 3——如何编译并Link?
Use Protobuf 3 In Flutter's C++ Code - How To Compile And Link?
Protobuf是一个使用非常广泛的库,所以我想在Flutter的C++代码中使用它来序列化和反序列化我的数据。但是,我发现编译和链接protobuf库并不是一件小事......我失败了很多次才找到正确的解决方案,所以我想在这里以问答的方式分享。
准备
首先,当然,您需要生成您的 protobuf 文件。这超出了本教程的范围,因此您可以查看 the official guide。假设您已经生成了 hello.pb.h
和 hello.pb.cc
。
iOS
如果您正在开发使用需要 protobuf 的 C++ 代码的 package/plugin,请将以下内容添加到您的 xxx.podspec
。如果是你的may代码需要protobuf,也是类似的。
Pod::Spec.new do |s|
...normal things...
s.pod_target_xcconfig = {
'HEADER_SEARCH_PATHS' => '$(SRCROOT)/Protobuf-C++/src',
}
s.dependency 'Protobuf-C++', '~> 3.18.0'
end
此外,您需要在主项目的 Podfile
中添加以下内容。比如你开发一个包,被10个项目使用,每个项目都需要添加下面的section。
post_install do |installer|
...normal things...
installer.pods_project.targets.each do |target|
# print "target=", target, "\n"
if target.name == "Protobuf-C++"
target.build_configurations.each do |config|
config.build_settings['HEADER_SEARCH_PATHS'] = '$(SRCROOT)/Protobuf-C++/src'
end
end
end
end
然后清理并构建项目并享受!
备注:为什么我需要破解 HEADER_SEARCH_PATHS
:最初它出错并说找不到 protobuf headers。 This solution suggests that we add the paths manually by hand after each pod install
. Moreover, this improved solution 建议我们可以通过添加几行代码来自动化该过程。这就是我们上面所做的。
另一种方式
This link 基本上工作得很好。虽然是在Cardboard的SDK教程下,但是这个网页几乎没有讲到Cardboard-specific的东西,只讲了protobuf。因此,请随时遵循它。除了最后一步(“复制文件”)之外的所有步骤之后,您将得到:include/google/*
和 lib/libprotobuf-lite.a
.
开始之前,请不要忘记make distclean
清理所有内容(如果您已经完成某些操作)。
备注1:如果您在linking时看到错误,提示“Undefined symbols”和“ld: warning: ignoring file”,您可以按照this link解决。
备注2:该教程只创建了libprotobuf-lite.a
。如果需要libprotobuf.a
,可以类推:lipo $build_dir/arch/arm64/lib/libprotobuf.a $build_dir/arch/armv7/lib/libprotobuf.a -create -output $build_dir/lib/libprotobuf.a
.
然后,您可以将 .a
和 headers 放入您的 ios xcode 项目中。这个我还没试过,因为我用的是上面的方法。
Android
获取库和headers
比iOS复杂一点。
开始之前,请不要忘记make distclean
清理所有内容(如果您已经完成某些操作)。
先跟着the same link as the approach to generating your own .a and headers in iOS做git clone
、git checkout
、git submodule
、./autogen.sh
,但就此打住,不再继续。
接下来,您需要按照this answer稍微修改一下代码——否则编译会失败。请参考link使用ltmain.sh.patch
打补丁。 (至于fuse-ld
flag我已经做了,不用你做任何事。)
下面是配置和编译,如下所示。
export NDKDIR=/Users/tom/Library/Android/sdk/ndk/21.1.6352462/toolchains/llvm/prebuilt/darwin-x86_64
export SYSROOT=$NDKDIR/sysroot
./configure \
--prefix=/Users/tom/RefCode/libprotobuf/android \
--host=arm-linux-androideabi \
--with-sysroot="${SYSROOT}" \
--enable-shared \
--enable-cross-compile \
--with-protoc=protoc \
"CC=$NDKDIR/bin/armv7a-linux-androideabi26-clang" \
CFLAGS="-fPIC -fuse-ld=bfd -march=armv7-a -D__ANDROID_API__=26" \
"CXX=$NDKDIR/bin/armv7a-linux-androideabi26-clang++" \
CXXFLAGS="-fPIC -fuse-ld=bfd -frtti -fexceptions -march=armv7-a -D__ANDROID_API__=26" \
"RANLIB=$NDKDIR/bin/x86_64-linux-android-ranlib" \
"C_COMPILER_RANLIB=$NDKDIR/bin/x86_64-linux-android-ranlib" \
"CXX_COMPILER_RANLIB=$NDKDIR/bin/x86_64-linux-android-ranlib" \
"AR=$NDKDIR/bin/x86_64-linux-android-ar" \
LIBS="-llog -lz -lc++_static";
make -j8 && make install
备注:以上命令与this post相似但不等同,因为post中使用的NDK独立工具链已被弃用。当然,你可能需要改变你的版本,比如你自己的ndk的路径,你的ndk版本等等
备注:您可能还需要知道上面使用的命名规则。比如为什么有时候是llvm
,为什么有时候是darwin
,为什么是armv7a
。它们被详细描述 in the official guide.
备注:当您使用 Gradle.
编译 android 应用程序时遇到 libprotobuf.a: no archive symbol table (run ranlib)
问题时,this link 也很有用
备注:如果没有-fPIC
,会出现requires unsupported dynamic reloc R_ARM_REL32; recompile with -fPIC
这样的错误。所以我跟着this link来解决它。
备注:如果看到error: undefined reference to 'stderr'
这样的错误,可能需要把你的minSdkVersion
调大一些,比如改成23。ref
备注:更换东西后,可能需要多次清洗。例如,gradle sync,gradle clean,make distclean 等
与the guide类似,下一步是make -j8
,然后是make install
。然后你就完成了,你会看到一些库,比如 libprotobuf-lite.a
以及包括 headers.
在你的项目中使用它
将以下内容添加到 CMakeLists.txt
:
set(OPENCV_BASE_DIR "/some/absolute/path/to/your/base/dir/that/has/built/just/now")
message("PROTOBUF_BASE_DIR: ${PROTOBUF_BASE_DIR}")
set(PROTOBUF_INCLUDE_DIR "${PROTOBUF_BASE_DIR}/include/")
set(PROTOBUF_STATIC_LIB_DIR "${PROTOBUF_BASE_DIR}/lib")
if (NOT EXISTS ${PROTOBUF_INCLUDE_DIR})
message(FATAL_ERROR "PROTOBUF_INCLUDE_DIR=${PROTOBUF_INCLUDE_DIR} does not exist - have you provided the correct PROTOBUF_BASE_DIR=${PROTOBUF_BASE_DIR}")
endif ()
include_directories(${PROTOBUF_INCLUDE_DIR})
add_library(protobuf-lite STATIC IMPORTED)
set_target_properties(protobuf-lite PROPERTIES IMPORTED_LOCATION ${PROTOBUF_STATIC_LIB_DIR}/libprotobuf-lite.a)
add_library(protobuf STATIC IMPORTED)
set_target_properties(protobuf PROPERTIES IMPORTED_LOCATION ${PROTOBUF_STATIC_LIB_DIR}/libprotobuf.a)
target_link_libraries(yourapp
...others...
protobuf
)
由于我在上面的代码中只为 armv7 而不是 armv8 构建,我们可以添加一个 abi 过滤器,如下所示。或者,您也可以为 armv8 构建并将它们合并到一个胖库中,就像我们在 ios.
中所做的那样
build.gradle
:
android {
defaultConfig {
externalNativeBuild {
cmake {
abiFilters 'armeabi-v7a'
}
}
}
}
奖励:MacOS
获取库和headers
如果您还想在 mac 计算机上使用 C++ 代码(使用 protobuf)怎么办?
开始之前,请不要忘记make distclean
清理所有内容(如果您已经完成某些操作)。
类似于this link,你只需要将你的./configure
改为:
./configure CC=clang CXX="clang++ -std=c++11 -stdlib=libc++" CXXFLAGS="-O3" --disable-shared
其他的和the guide几乎一样,比如下载、配置、make
最后sudo make install
(注意这里需要sudo)。
您将在系统的 include 目录中看到 headers,在系统的 lib 文件夹中看到 libs。
在你的项目中使用它
如果您使用的是 CMake,它主要是标准的。首先,将 path/to/your/hello.pb.cc
添加到您的 add_executable
中。其次,将 /usr/local/lib/libprotobuf.a
添加到您的 target_link_libraries
。完整示例:
add_executable(app
./main.cpp
path/to/your/hello.pb.cc
... others ...
)
target_link_libraries(app
/usr/local/lib/libprotobuf.a
... others ...
)
备注:libprotobuf-lite.a
有时不够用,所以你需要link使用big lib而不是lite lib。
Protobuf是一个使用非常广泛的库,所以我想在Flutter的C++代码中使用它来序列化和反序列化我的数据。但是,我发现编译和链接protobuf库并不是一件小事......我失败了很多次才找到正确的解决方案,所以我想在这里以问答的方式分享。
准备
首先,当然,您需要生成您的 protobuf 文件。这超出了本教程的范围,因此您可以查看 the official guide。假设您已经生成了 hello.pb.h
和 hello.pb.cc
。
iOS
如果您正在开发使用需要 protobuf 的 C++ 代码的 package/plugin,请将以下内容添加到您的 xxx.podspec
。如果是你的may代码需要protobuf,也是类似的。
Pod::Spec.new do |s|
...normal things...
s.pod_target_xcconfig = {
'HEADER_SEARCH_PATHS' => '$(SRCROOT)/Protobuf-C++/src',
}
s.dependency 'Protobuf-C++', '~> 3.18.0'
end
此外,您需要在主项目的 Podfile
中添加以下内容。比如你开发一个包,被10个项目使用,每个项目都需要添加下面的section。
post_install do |installer|
...normal things...
installer.pods_project.targets.each do |target|
# print "target=", target, "\n"
if target.name == "Protobuf-C++"
target.build_configurations.each do |config|
config.build_settings['HEADER_SEARCH_PATHS'] = '$(SRCROOT)/Protobuf-C++/src'
end
end
end
end
然后清理并构建项目并享受!
备注:为什么我需要破解 HEADER_SEARCH_PATHS
:最初它出错并说找不到 protobuf headers。 This solution suggests that we add the paths manually by hand after each pod install
. Moreover, this improved solution 建议我们可以通过添加几行代码来自动化该过程。这就是我们上面所做的。
另一种方式
This link 基本上工作得很好。虽然是在Cardboard的SDK教程下,但是这个网页几乎没有讲到Cardboard-specific的东西,只讲了protobuf。因此,请随时遵循它。除了最后一步(“复制文件”)之外的所有步骤之后,您将得到:include/google/*
和 lib/libprotobuf-lite.a
.
开始之前,请不要忘记make distclean
清理所有内容(如果您已经完成某些操作)。
备注1:如果您在linking时看到错误,提示“Undefined symbols”和“ld: warning: ignoring file”,您可以按照this link解决。
备注2:该教程只创建了libprotobuf-lite.a
。如果需要libprotobuf.a
,可以类推:lipo $build_dir/arch/arm64/lib/libprotobuf.a $build_dir/arch/armv7/lib/libprotobuf.a -create -output $build_dir/lib/libprotobuf.a
.
然后,您可以将 .a
和 headers 放入您的 ios xcode 项目中。这个我还没试过,因为我用的是上面的方法。
Android
获取库和headers
比iOS复杂一点。
开始之前,请不要忘记make distclean
清理所有内容(如果您已经完成某些操作)。
先跟着the same link as the approach to generating your own .a and headers in iOS做git clone
、git checkout
、git submodule
、./autogen.sh
,但就此打住,不再继续。
接下来,您需要按照this answer稍微修改一下代码——否则编译会失败。请参考link使用ltmain.sh.patch
打补丁。 (至于fuse-ld
flag我已经做了,不用你做任何事。)
下面是配置和编译,如下所示。
export NDKDIR=/Users/tom/Library/Android/sdk/ndk/21.1.6352462/toolchains/llvm/prebuilt/darwin-x86_64
export SYSROOT=$NDKDIR/sysroot
./configure \
--prefix=/Users/tom/RefCode/libprotobuf/android \
--host=arm-linux-androideabi \
--with-sysroot="${SYSROOT}" \
--enable-shared \
--enable-cross-compile \
--with-protoc=protoc \
"CC=$NDKDIR/bin/armv7a-linux-androideabi26-clang" \
CFLAGS="-fPIC -fuse-ld=bfd -march=armv7-a -D__ANDROID_API__=26" \
"CXX=$NDKDIR/bin/armv7a-linux-androideabi26-clang++" \
CXXFLAGS="-fPIC -fuse-ld=bfd -frtti -fexceptions -march=armv7-a -D__ANDROID_API__=26" \
"RANLIB=$NDKDIR/bin/x86_64-linux-android-ranlib" \
"C_COMPILER_RANLIB=$NDKDIR/bin/x86_64-linux-android-ranlib" \
"CXX_COMPILER_RANLIB=$NDKDIR/bin/x86_64-linux-android-ranlib" \
"AR=$NDKDIR/bin/x86_64-linux-android-ar" \
LIBS="-llog -lz -lc++_static";
make -j8 && make install
备注:以上命令与this post相似但不等同,因为post中使用的NDK独立工具链已被弃用。当然,你可能需要改变你的版本,比如你自己的ndk的路径,你的ndk版本等等
备注:您可能还需要知道上面使用的命名规则。比如为什么有时候是llvm
,为什么有时候是darwin
,为什么是armv7a
。它们被详细描述 in the official guide.
备注:当您使用 Gradle.
编译 android 应用程序时遇到libprotobuf.a: no archive symbol table (run ranlib)
问题时,this link 也很有用
备注:如果没有-fPIC
,会出现requires unsupported dynamic reloc R_ARM_REL32; recompile with -fPIC
这样的错误。所以我跟着this link来解决它。
备注:如果看到error: undefined reference to 'stderr'
这样的错误,可能需要把你的minSdkVersion
调大一些,比如改成23。ref
备注:更换东西后,可能需要多次清洗。例如,gradle sync,gradle clean,make distclean 等
与the guide类似,下一步是make -j8
,然后是make install
。然后你就完成了,你会看到一些库,比如 libprotobuf-lite.a
以及包括 headers.
在你的项目中使用它
将以下内容添加到 CMakeLists.txt
:
set(OPENCV_BASE_DIR "/some/absolute/path/to/your/base/dir/that/has/built/just/now")
message("PROTOBUF_BASE_DIR: ${PROTOBUF_BASE_DIR}")
set(PROTOBUF_INCLUDE_DIR "${PROTOBUF_BASE_DIR}/include/")
set(PROTOBUF_STATIC_LIB_DIR "${PROTOBUF_BASE_DIR}/lib")
if (NOT EXISTS ${PROTOBUF_INCLUDE_DIR})
message(FATAL_ERROR "PROTOBUF_INCLUDE_DIR=${PROTOBUF_INCLUDE_DIR} does not exist - have you provided the correct PROTOBUF_BASE_DIR=${PROTOBUF_BASE_DIR}")
endif ()
include_directories(${PROTOBUF_INCLUDE_DIR})
add_library(protobuf-lite STATIC IMPORTED)
set_target_properties(protobuf-lite PROPERTIES IMPORTED_LOCATION ${PROTOBUF_STATIC_LIB_DIR}/libprotobuf-lite.a)
add_library(protobuf STATIC IMPORTED)
set_target_properties(protobuf PROPERTIES IMPORTED_LOCATION ${PROTOBUF_STATIC_LIB_DIR}/libprotobuf.a)
target_link_libraries(yourapp
...others...
protobuf
)
由于我在上面的代码中只为 armv7 而不是 armv8 构建,我们可以添加一个 abi 过滤器,如下所示。或者,您也可以为 armv8 构建并将它们合并到一个胖库中,就像我们在 ios.
中所做的那样build.gradle
:
android {
defaultConfig {
externalNativeBuild {
cmake {
abiFilters 'armeabi-v7a'
}
}
}
}
奖励:MacOS
获取库和headers
如果您还想在 mac 计算机上使用 C++ 代码(使用 protobuf)怎么办?
开始之前,请不要忘记make distclean
清理所有内容(如果您已经完成某些操作)。
类似于this link,你只需要将你的./configure
改为:
./configure CC=clang CXX="clang++ -std=c++11 -stdlib=libc++" CXXFLAGS="-O3" --disable-shared
其他的和the guide几乎一样,比如下载、配置、make
最后sudo make install
(注意这里需要sudo)。
您将在系统的 include 目录中看到 headers,在系统的 lib 文件夹中看到 libs。
在你的项目中使用它
如果您使用的是 CMake,它主要是标准的。首先,将 path/to/your/hello.pb.cc
添加到您的 add_executable
中。其次,将 /usr/local/lib/libprotobuf.a
添加到您的 target_link_libraries
。完整示例:
add_executable(app
./main.cpp
path/to/your/hello.pb.cc
... others ...
)
target_link_libraries(app
/usr/local/lib/libprotobuf.a
... others ...
)
备注:libprotobuf-lite.a
有时不够用,所以你需要link使用big lib而不是lite lib。