本次编译核心实现基于 CMake FetchContent 自动拉取第三方依赖(Abseil/Protobuf)+ 编译生成 Android 动态库 libnativesdk.so,全程适配 NDK25/CMake3.22,最终实现无报错编译成功。

注:CMakeLists.txt完整文件在文章末尾

一、编译核心目标

基于现有业务代码(core/jni 目录)+ Protobuf 生成代码,通过 CMake 实现:

  1. 自动化拉取 Abseil、Protobuf 3.25.3 第三方依赖,无需本地存放依赖源码;
  2. 复用 Protobuf 内置的 utf8_range 库,解决其独立仓库 404、无源文件问题;
  3. 编译生成 Android 可执行动态库 libnativesdk.so,仅实现编译 + 链接依赖,无需安装 / 导出依赖库;
  4. 配置兼容跨平台(后续可直接迁移至 Visual Studio),无冗余配置。

二、完整编译配置流程(按 CMake 执行顺序)

步骤 1:基础编译环境与配置初始化

  1. 指定 CMake 最低版本(3.10.2)和项目名(nativesdk),匹配 Android Studio 内置 CMake 版本;
  2. 配置跨平台编译基础参数:C++17 标准、位置无关代码(PIC)、Android 静态 STL(c++_static),保证编译产物兼容性;
  3. 引入 CMake 原生模块 FetchContent,为自动化拉取 Git 仓库依赖做准备。

步骤 2:第三方依赖拉取配置(FetchContent)

(1)Abseil 配置

  • 拉取官方仓库:,锁定稳定 Tag 20240116.0;
  • 关闭浅克隆(GIT_SHALLOW OFF)、开启拉取进度(GIT_PROGRESS ON);
  • 禁用 Abseil 测试 / 示例 / 基准编译(ABSL_BUILD_TESTING/OFF 等),减少编译冗余。

(2)Protobuf 3.25.3 核心配置

  • 拉取官方仓库:,升级版本至 v3.25.3(稳定版);
  • 核心修复:设置 GIT_SUBMODULES "",禁止拉取 gtest/benchmark 子模块,彻底解决网络 SSL 拉取错误;
  • 启用 Lite 运行时(protobuf_USE_LITE_RUNTIME ON)、编译静态库(protobuf_BUILD_SHARED_LIBS OFF),匹配轻量开发需求;
  • 关键新增:set(protobuf_INSTALL OFF CACHE BOOL "" FORCE),禁用 Protobuf 内置的安装 / 导出逻辑,从根源规避导出依赖链错误;
  • 禁用 Protobuf 测试 / 示例编译,提升编译速度。

(3)依赖初始化

执行 FetchContent_MakeAvailable(abseil-cpp protobuf),CMake 自动完成:

  • 首次构建:检测本地无缓存时,调用系统 Git 拉取依赖源码至本地缓存目录;
  • 后续构建:直接读取本地缓存,不发起网络请求;
  • Protobuf 3.25.3 自动编译其内置的 utf8_range 库,生成可用的 CMake 目标。

步骤 3:utf8_range 库适配(核心难点解决)

  1. 定位 Protobuf 内置 utf8_range 头文件目录:${protobuf_SOURCE_DIR}/third_party/utf8_range
  2. 通过 include_directories 暴露头文件路径,保证业务代码可正常 #include "utf8_range.h"
  3. 直接复用 Protobuf 自动构建的 utf8_range 目标,无需手动编译,避免命名冲突。

步骤 4:业务代码与 Protobuf 生成代码整合

  1. 通过 file(GLOB) 递归获取核心业务代码(core/ .cpp、jni/ .cpp)和 Protobuf 编译生成的代码(proto/generated/*.pb.cc);
  2. 整合所有源码为 ALL_SRC,作为后续编译动态库的输入。

步骤 5:动态库(libnativesdk.so)编译与链接

  1. 执行 add_library(nativesdk SHARED ${ALL_SRC}),指定编译为 Android 动态库;
  2. 配置头文件搜索路径:包含项目根目录、Protobuf 生成目录、所有第三方依赖源码目录,保证头文件可被正确找到;
  3. 设置编译宏:保留 PROTOBUF_USE_LITE_RUNTIME 等核心宏,匹配 Protobuf Lite 版本使用规范;
  4. 库链接:将业务动态库与 Abseil 核心模块、Protobuf-lite、utf8_range 及 Android 系统库(log/z/atomic/m)链接,形成完整依赖链。

步骤 6:执行编译(Android Studio)

  1. 点击「Build -> Make Project」,Android Studio 自动调用 CMake + NDK 执行构建;
  2. 编译成功后,在 build/intermediates/cmake/[Debug/Release]/obj/[架构(如arm64-v8a)]/ 生成最终产物 libnativesdk.so

三、编译过程中遇到的核心问题及解决方案

问题 1:utf8_range 独立仓库拉取失败(404 / 无有效链接)

  • 现象:最初配置拉取 akrylysov/utf8_range.git 报 404,Google 官方无独立 utf8_range 仓库;
  • 原因:utf8_range 是轻量级工具库,无官方独立仓库,仅作为子目录内嵌于 Protobuf/Google Fonts 等项目;
  • 解决方案:放弃独立拉取,复用 Protobuf 内置的 utf8_range 目录,无需额外网络请求。

问题 2:CMake 无法确定 utf8_range 链接语言(CMake Error: CMake can not determine linker language)

  • 现象:手动编译 utf8_range 时,仅添加头文件 utf8_range.h,CMake 报语言识别失败;
  • 原因:CMake 通过源文件后缀(.c/.cpp)识别编译 / 链接语言,仅头文件无有效识别依据;
  • 解决方案:因后续升级 Protobuf 3.25.3 后其会自动编译 utf8_range,直接移除手动编译逻辑,复用官方构建目标,规避该问题。

问题 3:utf8_range 目标命名冲突(add_library cannot create target "utf8_range" because another target with the same name already exists)

  • 现象:升级 Protobuf 3.25.3 后,手动编译 utf8_range 与 Protobuf 自动构建的同名目标冲突;
  • 原因:Protobuf 3.25.3 优化构建脚本,会自动将内置 utf8_range 编译为 CMake 目标,3.21.12 无此逻辑;
  • 解决方案:彻底移除所有手动编译 utf8_range 的代码,直接复用 Protobuf 自动生成的目标。

问题 4:Protobuf 导出依赖链不完整(install (EXPORT "protobuf-targets" ...) includes target "libprotobuf-lite" which requires target "absl_absl_check" that is not in any export set)

  • 现象:编译时触发 Protobuf 内置的 install/export 逻辑,报 Abseil 目标未加入导出集合;
  • 原因:Protobuf 3.25.3 依赖 Abseil,但其内置脚本未配置 Abseil 目标的导出规则,CMake 导出时校验依赖链失败;
  • 解决方案:项目仅需编译链接,无需安装 / 导出依赖,添加 protobuf_INSTALL OFF 禁用 Protobuf 所有 install/export 逻辑,从根源解决问题。

问题 5:Protobuf 子模块拉取 SSL 错误

  • 现象:最初拉取 Protobuf 时,因自动拉取 gtest/benchmark 子模块,报网络 SSL 连接错误;
  • 原因:网络环境无法直连子模块仓库,或子模块拉取触发证书校验问题;
  • 解决方案:设置 GIT_SUBMODULES "",禁止 Protobuf 拉取任何子模块,仅拉取核心源码。

四、关键注意事项(核心避坑点,必须记录)

(一)依赖拉取与缓存相关

  1. FetchContent 缓存规则:首次拉取的依赖源码缓存于 Android Studio 目录 项目根目录/.cxx/[构建类型]/[随机标识]/[架构]/_deps/,后续构建不重复拉取;如需更新依赖版本(修改 GIT_TAG),必须彻底删除项目根目录的 build/ 和 .cxx/ 文件夹,否则会使用旧缓存;
  2. Git 环境要求:FetchContent 依赖系统 Git,需保证本地安装 Git 并配置到系统环境变量(PATH),否则拉取失败;
  3. 网络配置:若拉取依赖缓慢 / 失败,需配置 Git 全局代理(git config --global https.proxy ,替换为自身代理地址);
  4. Protobuf 版本差异:3.21.12 不自动编译 utf8_range,3.25.3 会自动构建,升级版本后必须移除手动编译 utf8_range 的代码,避免命名冲突。

(二)CMake 配置相关

  1. Android 特有配置CMAKE_ANDROID_STL_TYPE c++_static 仅对 Android 生效,迁移至 Visual Studio 时会被自动忽略,无需修改;
  2. 编译标准一致性:项目指定 C++17 标准,第三方依赖(Abseil 20240116.0、Protobuf 3.25.3)均兼容 C++17,无需调整;
  3. 静态库 / 动态库规范:Abseil、Protobuf 均编译为静态库,最终链接到动态库 libnativesdk.so 中,避免运行时依赖缺失;
  4. 禁止随意修改官方开关protobuf_INSTALL OFFGIT_SUBMODULES "" 等均为官方提供的配置开关,非自定义 hack,稳定性高,无需额外调整。

(三)文件与路径相关

  1. 业务代码路径:core/、jni/、proto/generated/ 目录结构不可随意修改,否则需同步调整 CMake 中 file(GLOB) 的路径;
  2. 头文件引用规范:业务代码中引用 utf8_range 直接写 #include "utf8_range.h",引用 Protobuf 头文件按生成路径写,无需添加绝对路径;
  3. 跨平台路径要求:若后续迁移至 Visual Studio,项目根目录和构建目录不能包含中文、空格或特殊字符,否则会触发 Git 拉取失败或 CMake 路径识别错误。

(四)编译与产物相关

  1. 构建目录清理:遇到任何 CMake 配置错误、编译报错,优先删除 build/.cxx/ 文件夹(彻底清除缓存),可解决 90% 以上的配置冲突问题;
  2. 编译产物位置:成功编译后,libnativesdk.so 按「构建类型(Debug/Release)+ 架构(arm64-v8a/x86_64 等)」分类存放,可根据需求拷贝使用;
  3. Debug/Release 区别:Debug 版本包含调试信息,体积大;Release 版本开启优化,体积小、运行快,正式集成需使用 Release 版本。

(五)跨平台兼容相关(后续迁移 Visual Studio)

  1. 无需修改 CMake 配置:当前配置为纯跨平台写法,Visual Studio 2019+ 可直接打开项目根目录(文件夹形式),自动识别构建;
  2. VS 环境要求:保证 Visual Studio 内置 CMake 版本 ≥3.11,且 Git 已配置到系统环境变量;
  3. VS 缓存路径:Visual Studio 中 FetchContent 缓存于 项目根目录/.vs/CMakeFiles/_deps/,与 Android 环境缓存独立,互不干扰。

五、编译成功后核心成果

当前 CMakeLists.txt 已实现零手动依赖、零配置冲突、跨平台兼容,核心能力:

  1. 自动化拉取:一键拉取 Abseil 20240116.0 + Protobuf 3.25.3,无需手动下载、解压、配置依赖路径;
  2. 无冗余编译:所有第三方依赖均禁用测试 / 示例,仅编译核心代码,提升编译速度;
  3. 产物可用:生成的 libnativesdk.so 可直接集成到 Android 项目中,正常链接并调用业务逻辑;
  4. 易于维护:后续升级依赖版本,仅修改 FetchContent_Declare 中的 GIT_TAG,删除缓存后重新构建即可。

六、后续开发维护建议

  1. 禁止随意修改第三方依赖配置:如无特殊需求,不要修改 Abseil/Protobuf 的编译开关(如 BUILD_TESTS、USE_LITE_RUNTIME 等),避免引入新的配置冲突;
  2. 定期清理缓存:若项目长时间开发,可定期删除 build/.cxx/ 文件夹,避免缓存文件过多占用磁盘空间;
  3. 版本锁定:第三方依赖尽量使用稳定 Tag(如 v3.25.3),避免使用 main/master 分支,防止源码更新引入兼容性问题;
  4. 错误排查优先级:后续若出现编译错误,按「清理 build/.cxx 缓存 → 检查 Git 网络 / 环境 → 检查路径是否含特殊字符 → 检查依赖版本兼容性」的顺序排查,高效定位问题。
cmake_minimum_required(VERSION 3.10.2)
project(nativesdk)

# 基础编译配置(适配NDK25/CMake3.22,无冗余)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "" FORCE)
set(CMAKE_ANDROID_STL_TYPE c++_static CACHE STRING "" FORCE)

# ================================= 全依赖自动化拉取 =================================
include(FetchContent)

# 1. Abseil:官方正确Tag 20240116.0,关闭浅克隆
FetchContent_Declare(
        abseil-cpp
        GIT_REPOSITORY 
        GIT_TAG 20240116.0
        GIT_SHALLOW OFF
        GIT_PROGRESS ON
)
set(ABSL_BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(ABSL_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(ABSL_BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE)

# 2. Protobuf:3.25.3 Lite版 + 核心修复:禁止拉取任何子模块(解决SSL错误)
FetchContent_Declare(
        protobuf
        GIT_REPOSITORY 
        GIT_TAG v3.25.3  # 已改为3.25.3稳定版
        GIT_SHALLOW OFF
        GIT_PROGRESS ON
        GIT_SUBMODULES ""  # 关键!禁止拉取gtest/benchmark子模块,彻底避免SSL错误
)
set(protobuf_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(protobuf_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(protobuf_USE_LITE_RUNTIME ON CACHE BOOL "" FORCE)
set(protobuf_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(protobuf_INSTALL OFF CACHE BOOL "" FORCE)  # 核心新增!禁用Protobuf的安装/导出逻辑,从根源避免错误

# 一次性初始化Abseil+Protobuf(Protobuf 3.25.3会自动构建内置的utf8_range库,无需手动处理)
FetchContent_MakeAvailable(abseil-cpp protobuf)

# ================================= 核心优化:复用Protobuf自动构建的utf8_range库(无命名冲突) =================================
# 定位protobuf 3.25.3内置的utf8_range头文件目录(仅暴露头文件,无需重新编译)
set(UTF8_RANGE_SRC_DIR "${protobuf_SOURCE_DIR}/third_party/utf8_range")
# 暴露utf8_range头文件路径,确保业务代码可正常#include "utf8_range.h"
include_directories(${UTF8_RANGE_SRC_DIR})

# ================================= 对接本地业务代码=================================
file(GLOB YOUR_BUSINESS_SRC
        ${CMAKE_CURRENT_SOURCE_DIR}/core/*.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/jni/*.cpp
)
file(GLOB PROTO_GEN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/proto/generated/*.pb.cc)
set(ALL_SRC ${YOUR_BUSINESS_SRC} ${PROTO_GEN_SRC})

# ================================= 编译SO + 头文件 + 链接(一键对接,无任何修改) =================================
add_library(nativesdk SHARED ${ALL_SRC})

target_include_directories(nativesdk PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}
        ${CMAKE_CURRENT_SOURCE_DIR}/proto/generated
        ${abseil-cpp_SOURCE_DIR}
        ${protobuf_SOURCE_DIR}
        ${UTF8_RANGE_SRC_DIR}  # 保留utf8_range头文件路径
)

target_compile_definitions(nativesdk PRIVATE
        PROTOBUF_USE_LITE_RUNTIME
        GOOGLE_PROTOBUF_NO_RTTI
        PROTOBUF_DISABLE_JSON=1
)

# 链接配置(直接使用Protobuf自动构建的utf8_range库,无命名冲突)
target_link_libraries(nativesdk PRIVATE
        absl::base absl::strings absl::hash absl::synchronization absl::time absl::status
        protobuf::libprotobuf-lite
        utf8_range  # 直接链接Protobuf内置的utf8_range库(无需::别名,原生支持)
        log z atomic m
)
本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com