news 2026/6/3 6:27:08

告别Makefile!手把手教你用Android.bp和Soong构建你的第一个C++模块

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
告别Makefile!手把手教你用Android.bp和Soong构建你的第一个C++模块

告别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_CFLAGS)
  • static_libs:依赖的静态库(这里链接Android日志库)

注意:模块名称必须全局唯一,避免与AOSP现有模块冲突

3. 高级模块配置技巧

3.1 条件编译与架构适配

Blueprint支持针对不同CPU架构进行差异化配置:

cc_library { name: "my_jni", srcs: ["jni_interface.cpp"], arch: { arm: { cflags: ["-mfloat-abi=softfp"], }, 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 常用构建命令

  1. 全量构建特定模块:
m my_tool
  1. 强制重新生成Ninja文件:
m soong
  1. 增量构建检查:
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_tool

5. 从Android.mk到Android.bp的迁移指南

对于已有Makefile的项目,可以参照以下转换对照表:

Android.mk语法Android.bp等效实现
LOCAL_MODULE := fooname: "foo"
LOCAL_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. 性能优化与最佳实践

  1. 模块粒度控制

    • 每个功能独立的组件应拆分为单独模块
    • 避免创建包含数百个源文件的巨型模块
  2. 依赖管理原则

    • 优先使用shared_libs减少二进制体积
    • 对性能关键路径考虑static_libs
  3. 构建缓存利用

cc_binary { name: "perf_tool", srcs: ["*.cpp"], use_versioned_soong: true, // 启用构建缓存 }
  1. 调试信息处理
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
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/3 6:18:10

VLC for Android 跨平台多媒体引擎技术深度解析与实战部署指南

VLC for Android 跨平台多媒体引擎技术深度解析与实战部署指南 【免费下载链接】vlc-android VLC for Android, Android TV and ChromeOS 项目地址: https://gitcode.com/gh_mirrors/vl/vlc-android 项目探秘:移动端多媒体技术栈的架构演进 VLC for Androi…

作者头像 李华
网站建设 2026/6/3 6:17:08

Phi-2小模型解析:27亿参数如何实现高效AI部署与微调实战

1. 项目概述:小模型,大能量最近在跟几个做边缘计算和端侧部署的朋友聊天,大家不约而同地提到了一个名字:Phi-2。这让我想起了几年前,整个行业还在疯狂追逐千亿、万亿参数大模型的时候,谁能想到,…

作者头像 李华