
告别Makefile手把手教你用Android.bp和Soong构建你的第一个C模块在Android开源项目AOSP的演进历程中构建系统经历了从Makefile到Soong的根本性变革。对于习惯传统Makefile语法的开发者而言转向基于Blueprint语言的Android.bp文件可能面临陡峭的学习曲线。本文将以一个典型场景为例假设你需要在AOSP中集成一个简单的C模块比如JNI库或原生工具我们将从零开始演示如何用现代构建工具链完成这一任务。1. 为什么需要迁移到Soong构建系统传统Android.mk文件采用过程式语法开发者需要手动指定编译顺序、依赖关系和构建规则。这种模式在小型项目中尚可应付但当面对AOSP这样包含数百万行代码的超大型项目时其劣势愈发明显构建速度瓶颈递归式Makefile解析导致大量冗余计算依赖管理脆弱手动维护的依赖链极易出错语法复杂度高条件判断和变量展开难以维护Soong构建系统通过引入声明式语法和自动化依赖解析显著改善了这些问题。其核心优势体现在并行构建优化基于Ninja的并行任务调度精确依赖追踪自动分析头文件包含关系配置即代码简洁的Blueprint语法定义模块属性以下是一个典型构建速度对比构建方式全量构建时间增量构建时间Makefile系统58分钟12分钟Soong系统32分钟3分钟2. 创建你的第一个Android.bp文件让我们从一个最简单的C可执行文件开始。假设项目结构如下my_module/ ├── Android.bp └── src/ ├── main.cpp └── utils.cpp对应的Android.bp内容如下cc_binary { name: my_tool, srcs: [src/main.cpp, src/utils.cpp], cflags: [-Wall, -Werror], static_libs: [liblog], stl: libc_static, }关键属性解析name必填项定义模块输出名称srcs指定源文件路径支持glob模式匹配cflags编译器选项等效于Makefile中的LOCAL_CFLAGSstatic_libs依赖的静态库这里链接Android日志库注意模块名称必须全局唯一避免与AOSP现有模块冲突3. 高级模块配置技巧3.1 条件编译与架构适配Blueprint支持针对不同CPU架构进行差异化配置cc_library { name: my_jni, srcs: [jni_interface.cpp], arch: { arm: { cflags: [-mfloat-abisoftfp], }, arm64: { srcs: [arm64/optimized.cpp], }, x86: { enabled: false, // 禁用x86构建 }, }, }3.2 多模块项目组织对于包含多个组件的复杂项目推荐采用以下结构// 基础库模块 cc_library { name: libbase, srcs: [base/*.cpp], export_include_dirs: [include], } // 主程序模块 cc_binary { name: main_app, srcs: [app/main.cpp], shared_libs: [libbase], }关键特性export_include_dirs声明公开头文件目录shared_libs指定动态库依赖4. 构建与调试实战4.1 常用构建命令全量构建特定模块m my_tool强制重新生成Ninja文件m soong增量构建检查m nothing # 仅验证构建系统正确性4.2 常见问题排查问题1出现undefined reference错误解决方案检查static_libs/shared_libs是否包含所有依赖库确认库的export_include_dirs设置正确问题2头文件找不到调试步骤# 查看模块的完整编译命令 prebuilts/build-tools/linux-x86/bin/ninja -v -t commands out/soong/.intermediates/my_module/my_tool5. 从Android.mk到Android.bp的迁移指南对于已有Makefile的项目可以参照以下转换对照表Android.mk语法Android.bp等效实现LOCAL_MODULE : fooname: fooLOCAL_SRC_FILESsrcs: [file1.cpp]LOCAL_STATIC_LIBRARIESstatic_libs: [lib1]LOCAL_CFLAGScflags: [-DDEBUG]include $(BUILD_*)自动由模块类型决定如cc_binary实际迁移案例# 原Android.mk LOCAL_MODULE : legacy_lib LOCAL_SRC_FILES : old.cpp LOCAL_CFLAGS : -DCOMPAT_MODE include $(BUILD_STATIC_LIBRARY)转换为// 新Android.bp cc_library_static { name: legacy_lib, srcs: [old.cpp], cflags: [-DCOMPAT_MODE], }6. 性能优化与最佳实践模块粒度控制每个功能独立的组件应拆分为单独模块避免创建包含数百个源文件的巨型模块依赖管理原则优先使用shared_libs减少二进制体积对性能关键路径考虑static_libs构建缓存利用cc_binary { name: perf_tool, srcs: [*.cpp], use_versioned_soong: true, // 启用构建缓存 }调试信息处理cc_defaults { name: debug_config, strip: { none: true, // 保留所有符号 }, debuggable: true, }在完成第一个模块的构建后建议使用nm工具验证输出prebuilts/clang/host/linux-x86/bin/llvm-nm -gC out/target/product/generic/system/bin/my_tool