avatar

Nihil

Nichts Hsu

  • 首页
  • 子域
  • 分类
  • 标签
  • 归档
  • 关于
首页 Android C++ 生成 compile_commands.json
文章

Android C++ 生成 compile_commands.json

发表于 2022/11/28 更新于 2023/01/09
作者 Nichts Hsu
5 分钟阅读

Android C++ 程序开发现状

在 Android 下开发 C++ 程序,我见过绝大多数人都是不使用任何语法插件,就靠硬写,写完之后再根据编译报错来修改语法错误。这也怪不得程序员,一方面,Android 使用 Arm 平台的 clang 编译器,跟 x86 平台的开发环境并不是很兼容;另一方面,Android 要求我们将 C++ 程序放在 vendor 目录下,但是我们包含的头文件却是去 kernel/include 下面找的。如果想要自己配置插件的开发环境,通常都是一顿操作猛如虎,结果还是各种报错。

Bear

Bear 是一个用来生成包含编译时选项的数据库的工具。通常它将输出文件 compile_commands.json,可以供给例如 vscode 中的 Microsoft C/C++ 插件或者 vim 中的 YouCompleteMe 插件使用,让插件可以正确的解析当前 C++ 源文件的各种依赖信息,例如头文件包含路径。

但是如果想要在 Android 开发环境中使用 Bear 有个很大的问题,那就是 mm/mma 这类命令它不能被 Bear 识别,即使你将其封装为脚本,最后得到的 compile_commands.json 也是空空如也的。

compdb

但是深入了解 Android 之后,我发现其实 Android 内置有 compdb 可以用来生成 compile_commands.json,流程上只需要设置几个环境变量即可:

1
2
3
4
5
6
7
8
cd /path/to/android/root    # Android 源码根路径
source build/envsetup.sh
lunch xxxx-userdebug
cd /path/to/app/dir         # 项目 Android.mk/Android.bp 所在目录
export SOONG_GEN_COMPDB=1
export SOONG_GEN_COMPDB_DEBUG=1
export SOONG_LINK_COMPDB_TO=$(pwd)
mm

等待一段时间后,就会在 /path/to/app/dir 目录下看到生成好的 compile_commands.json 了。需要注意的是,有些平台似乎不接受 SOONG_LINK_COMPDB_TO,不管怎么设置都固定生成在 Android 源码根目录,所以如果你在项目目录找不到该文件或者该文件无效,就去 Android 根目录看看。

如果还是没有 compile_commands.json,我们也可以借助 ninja 来生成,建议使用 Github 上最新的 ninja,否则可能会生成空文件:

1
2
cd /path/to/android/root    # Android 源码根路径
ninja -f out/combined-kona.ninja -t compdb | tee ./compile_commands.json

注意此处 combined-xxx.ninja 文件叫什么名字取决于你所使用的平台。

通常这个文件大的离谱,我这边生成的有 300 MB,这样如果直接给插件用的话,会导致加载时间过长。因此,我们需要对该文件进行一些裁剪。

compile_commands.json 的基本格式是:

1
2
3
4
5
6
7
8
9
10
[
    {
        "directory": "/path/to/android/root",
        "arguments": [
            "xxx"
        ],
        "file": "xxx"
    },
    ...
]

如果是 ninja 生成的 compile_commands.json,格式会有些许不同,它会用 command 字段替换掉 arguments 字段,二者都是有效的,都可以被 vscode/YouCompleteMe 等识别。

我们需要关注 file 字段,这里指示了每个参与编译的 cpp 文件。我们可以将只包含我们模块内的 cpp 文件的那些 json 块单独提取出来,组成一个 KB 级别的小 compile_commands.json 文件,这样,插件读取该文件会更轻松。

这里用 python 简单写个处理程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/python3
import sys
import json
import os
from pathlib import Path

if len(sys.argv) != 4:
    print("Usage: python3 main.py <project directory> <input compiledb path> <output compiledb path>")
    sys.exit()

files = Path(sys.argv[1])
result = list(map(os.path.realpath, map(str, list(files.rglob("*.cpp")) + list(files.rglob("*.c")))))

with open(sys.argv[2]) as compdb_input:
    compdb_origin = json.load(compdb_input)
    compdb_gen = []
    for obj in compdb_origin:
        full_path = obj["directory"] + '/' + obj["file"]
        if full_path in result:
            print("Find file: %s" % full_path)
            compdb_gen.append(obj)

with open(sys.argv[3], "w") as compdb_out:
    compdb_out.write(json.dumps(compdb_gen, indent=1))

用法是 python3 main.py 模块路径 输入的json文件路径 输出的json文件路径。

最后,配置好对应插件即可,例如对于 vscode,我们需要在 .vscode/c_cpp_properties.json 中添加下面高亮行的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**"
            ],
            "defines": [],
            "intelliSenseMode": "linux-gcc-x64",
            "compileCommands": "compile_commands.json"
        }
    ],
    "version": 4
}

重新打开一下工作区,然后用鼠标追踪一下 #include 的头文件,会发现这些头文件都指向了 Android 目录的,而不再是本机的 /usr/include/。

教程, Android
c++ android 教程
本文由作者按照 CC BY 4.0 进行授权
分享

最近更新

  • 从另一个视角看 Rust HRTBs
  • C++ Coroutine VS Rust Async
  • Rust 不透明类型上的生命周期
  • USB 2.0 与 USB 3.2
  • Rust 中的闭包递归与 Y 组合子
外部链接
  • 996.icu
  •  此博客的 Github 仓库
  •  Olimi 的个人博客

文章内容

相关文章

2023/10/16

Android.bp 中启用 openmp

在找到正确答案之前,我尝试了许多方法,例如: 1 2 3 4 5 cc_binary { shared_libs: [ "libopenmp", ], } 又或者: 1 2 3 4 5 cc_binary { cppflags: [ "-fopenmp" ], } 但是毫无例外地都失败了,以至于在很长一段时间内我都以...

2022/11/23

C++20 Concept

模板 在为静态类型语言开发代码时,我们很经常遇到这样的情况:我们需要为多个数据类型实现相同的功能。放在 C 语言中,我们不得不为他们各自定义一个函数或结构体,例如: int add(int x, int y); unsigned addu(unsigned x, unsigned y); float addf(float x, float y); // ... struct Vecto...

2023/04/24

初探 C++20 Coroutine

前言 近段时间研究了一下 C++20 的协程(Coroutine),大概了解了其中的工作原理,做一下记录。 初次接触 Coroutine 时,给我的感觉是一脸懵逼的。和其他语言简单的 async、await 不同,想要使用 C++20 的 Coroutine,它要求你定义一个包含 promise_type 的类型,其中 promise_type 又需要至少包含 get_return_ob...

C++20 Concept

Type-C 接口 CC 针脚的工作模式

© 2025 Nichts Hsu. 保留部分权利。

本站采用 Jekyll 主题 Chirpy

热门标签

编程语言 教程 rust c++ android c++20 usb 翻译 linux qt

发现新版本的内容。