使用Gradle构建CMake ExternalProject时外部项目未编译问题排查
你遇到的问题核心是CMake的ExternalProject_Add配置不完整,再加上老版本Gradle与CMake的兼容性限制,导致外部项目的构建没有被正确触发或错误检测。下面一步步拆解问题并给出修复方案:
1. 依赖关系未正确绑定
一开始外部项目foo没被编译,是因为你没有让目标可执行文件bar明确依赖foo的构建流程。后来添加依赖后configure执行但build步骤跳过,本质是CMake不知道foo构建后会生成哪些关键产物,也没把这些产物和bar的链接步骤关联起来,所以CMake误判foo已完成构建,直接跳过了编译阶段。
解决办法:
- 用
add_dependencies(bar foo)明确让bar等待foo构建完成; - 必须指定
BUILD_BYPRODUCTS参数,告诉CMakefoo构建后会生成的核心库文件(比如libfoo.a或libfoo.so),这样CMake会检查这些文件的存在性,不存在就触发build,文件变化时也会重新构建。
2. 交叉编译环境变量未正确传递
你只指定了--host=arm-linux-androideabi,但autotools的configure脚本还需要知道交叉编译工具链的具体路径(比如CC、CXX、AR等),否则可能会用主机编译器构建出不符合Android要求的产物,而CMake不会主动检测这个问题。
解决办法:
从CMake的交叉编译变量中获取NDK工具链路径,在CONFIGURE_COMMAND前传递这些环境变量:
# 获取NDK交叉编译工具链相关变量 set(TOOLCHAIN_BIN_DIR ${CMAKE_C_COMPILER}/../) set(CC ${CMAKE_C_COMPILER}) set(CXX ${CMAKE_CXX_COMPILER}) set(AR ${TOOLCHAIN_BIN_DIR}/arm-linux-androideabi-ar) set(RANLIB ${TOOLCHAIN_BIN_DIR}/arm-linux-androideabi-ranlib) ExternalProject_Add(foo SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../libfoo BUILD_IN_SOURCE ON # 多数autotools库需要在源码目录内构建 CONFIGURE_COMMAND env CC=${CC} CXX=${CXX} AR=${AR} RANLIB=${RANLIB} ${CMAKE_CURRENT_SOURCE_DIR}/../libfoo/configure --host=arm-linux-androideabi --prefix=${CMAKE_CURRENT_BINARY_DIR}/foo_install BUILD_COMMAND ${MAKE} INSTALL_COMMAND ${MAKE} install # 指定安装路径,方便后续链接 BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/foo_install/lib/libfoo.a # 标记构建产物 )
3. 关联构建产物到目标可执行文件
构建完foo后,需要把它的头文件和库文件路径传递给bar,确保链接阶段能找到依赖:
# 引入foo的头文件与库路径 include_directories(${CMAKE_CURRENT_BINARY_DIR}/foo_install/include) link_directories(${CMAKE_CURRENT_BINARY_DIR}/foo_install/lib) add_executable(bar bar.c) add_dependencies(bar foo) # 明确依赖关系 target_link_libraries(bar foo) # 链接foo库
4. 版本兼容性问题
你使用的Gradle 4.1和CMake 3.6都是较老旧的版本,ExternalProject_Add在旧版CMake中对Android交叉编译的支持不完善,Gradle与CMake的集成也存在一些bug。建议升级到:
- CMake 3.10及以上(Android Studio官方推荐的最低兼容版本)
- Gradle 5.x及以上,并搭配对应版本的Android Gradle Plugin
验证方法
修改libfoo中的代码后,执行./gradlew clean assembleDebug,此时应该会重新触发foo的configure、build和install步骤;如果libfoo编译失败,Gradle也会返回错误状态。
内容的提问来源于stack exchange,提问作者martinkunev

