Android配置C++开发环境

联系作者:私信 或者 个人主页

1. 解决的问题

在Android开发过程中,如果需要进行C++方面的开发,选择好用的IDE非常重要,很多人会使用Source Insight进行开发,首先它是收费软件,其次Source Insight对于资源消耗挺大,会造成电脑的卡顿。

我们知道,Android的模块使用的是Android.bp或者Android.mk进行构建的,目前并没有IDE可以解析这些配置文件。

本质上来说,需要解决的问题还是C++工程的索引问题,抛砖引玉可以参考我的另一篇文章:vscode配置c++代码跳转demo

2. 实践步骤

下面以Android12为例说明

解决C++工程索引问题,需要得到compile_commands.json,然后配置vscode + clangd进行索引。

2.1. 生成compile_commands.json

官方文档:build/soong/docs/compdb.md

需要导出以下环境变量:

bash 复制代码
$ export SOONG_GEN_COMPDB=1
$ export SOONG_GEN_COMPDB_DEBUG=1

建议加入到~/.bashrc文件中,默认导入到环境变量中。

使用如下命令触发编译生成compile_commands.json

bash 复制代码
$ make nothing

生成的compile_commands.json目录:

bash 复制代码
out/soong/development/ide/compdb/compile_commands.json

注意:这种方式生成的compile_commands.json包含了所有使用Android.bp定义的模块的编译数据库,使用Android.mk编译的模块并不包含在内。

2.2. 配置vscode

参考文章:vscode配置c++代码跳转demo

首先需要使用vscode远程登录到服务器,然后增加clangd配置,索引一遍即可。

2.3. 热更新

添加新模块、删除模块、变更源代码的头文件后,只需要编译该模块即可,使用mm或者mmm或者make <target>都可以,不需要编译整个系统。

2.4. 精简compile_commands.json

你可能会发现,make nothing后生成的compile_commands.json大概有300M,如果打开整个Android目录会非常慢,一般来说,我们只会打开一个仓库,如frameworks仓库,所以我们需要精简compile_commands.json,然后打开特定仓库进行索引,以加快vscode的响应速度。

以下给出的是精简compile_commands.json的python脚本:

python 复制代码
import json
import sys
from typing import *


def simply_compile_commands_json(completed_json_file: 'str', simplified_json_file: 'str', repositories: 'List[str]'):
    with open(completed_json_file) as input_file:
        command_content = input_file.read()
        command_json = json.loads(command_content)
        target_command_list = []
        for command in command_json:
            file: 'str' = command['file']
            if any((file.startswith(repository) for repository in repositories)):
                target_command_list.append(command)

        with open(simplified_json_file, "w") as output_file:
            output_file.write(json.dumps(target_command_list, indent=4))


if __name__ == '__main__':
    if len(sys.argv) != 4:
        print('Usage: python3 {} <complete.json> <simply.json> <repo[,repo[,repo]...]>'.format(sys.argv[0]))
        print('Eg: python3 {} ./compile_commands.json.big ./compile_commands.json system,hardware,frameworks'.format(sys.argv[0]))
        exit(1)
    else:
        input_compile_commands = sys.argv[1]
        output_compile_commands = sys.argv[2]
        repositories = sys.argv[3].split(',')
        simply_compile_commands_json(input_compile_commands, output_compile_commands, repositories)

2.5. for kernel

对于驱动部分的代码,其实也可以使用compile_commands.json配合vscode进行索引,从Kernel 5.10开始就预置了脚本用于生成compile_commands.json。

bash 复制代码
kernel-5.10$ python3 ./scripts/clang-tools/gen_compile_commands.py

生成的compile_commands.json在kernel-5.10目录下。

2.6. for Android.mk module

前面提到,对于Android.mk定义的模块,以上的方式是不会生成到compile_commands.json中的。

对于这类模块,我提出的想法是自己解析编译的命令,自己生成compile_commands.json!

第一个问题是如何得到编译的完整命令:

其实只需要在Android.mk中加入如下参数,就可以打印出完整的编译命令:

androidmk 复制代码
LOCAL_CFLAGS += -v
LOCAL_CXXFLAGS += -v

如何批量往Android.mk中添加以上flag?可以通过如下脚本:

python 复制代码
import os
import re
import sys
import time

def toggle_verbose_cflag(path: 'str'):
    lines = []
    try:
        with open(path, 'r') as input:
            lines = input.readlines()
    except Exception:
        return

    verbose_cflag_count = 0
    for line in lines:
        verbose_cflag_count += line.count('LOCAL_CFLAGS += -v')
        verbose_cflag_count += line.count('LOCAL_CXXFLAGS += -v')

    if verbose_cflag_count > 0:
        # remove LOCAL_CFLAGS += -v
        # remove LOCAL_CXXFLAGS += -v
        temp = []
        for line in lines:
            if line.count('LOCAL_CFLAGS += -v') > 0 or line.count('LOCAL_CXXFLAGS += -v') > 0:
                pass
            else:
                temp.append(line)
        lines = temp
    else:
        # add LOCAL_CFLAGS += -v
        # add LOCAL_CXXFLAGS += -v
        temp = []
        for line in lines:
            if line.strip().startswith('include') and not line.strip().startswith(r'include $(CLEAR_VARS)'):
                m = re.match(r'^\s*', line)
                spaces = m.group(0) if m else ''
                temp.append(spaces + 'LOCAL_CFLAGS += -v' + os.linesep)
                temp.append(spaces + 'LOCAL_CXXFLAGS += -v' + os.linesep)
            temp.append(line)
        lines = temp

    with open(path, 'w') as output:
        output.writelines(lines)


def update_androidmk(directory):
    # 获取当前时间戳(以秒为单位)
    current_time = int(time.time())

    # 遍历指定目录下的所有文件和子目录
    for filename in os.listdir(directory):
        # 构建完整的文件路径或子目录路径
        path = os.path.join(directory, filename)

        # 如果这是一个文件(而不是一个目录),则更新其修改时间
        if os.path.isfile(path):
            os.utime(path, (current_time, current_time))
            if filename == 'Android.mk':
                toggle_verbose_cflag(path)
        # 否则,如果这是一个目录,则递归地处理其内容
        elif os.path.isdir(path):
            update_androidmk(path)

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print("Usage: python3 {} directory".format(sys.argv[0]))
        exit(1)
    else:
        directory = sys.argv[1]
        update_androidmk(directory)

通过以上脚本,给定目录,可以一键往其中的Android.mk添加/删除verbose flag。

第二个问题是如何解析编译命令,生成compile_commands.json

可以通过如下脚本实现:

python 复制代码
import json
import sys

def build_compile_commands(input_file: 'str', output_file: 'str', android_top: 'str', compiler='clang'):
    with open(input_file) as input:
        target_lines = []
        compile_command_list = []
        for line in input:
            if line.count(compiler) > 0 and ( line.count('.c') > 0 or line.count('.cpp') > 0 ):
                target_lines.append(line.strip())
        # print(len(target_lines))
        for rule in target_lines:
            if rule.count('"') >= 2:
                compile_command = {}
                compile_command['directory'] = android_top
                compile_command['arguments'] = []
                compile_command_file = ''
                items = rule.split(' ')
                for item in items:
                    item = item.replace('"', '')
                    if item.count(compiler) > 0:
                        item = android_top + '/' + item
                    compile_command['arguments'].append(item)
                    if item.count('.c') > 0 or item.count('.cpp') > 0:
                        if item.count('/') > 0:
                            compile_command_file = item
                if compile_command_file != '':
                    compile_command['file'] = compile_command_file
                    compile_command_list.append(compile_command)

        with open(output_file, 'w') as output:
            print(json.dumps(compile_command_list, indent=4), file=output)

if __name__ == '__main__':
    if len(sys.argv) < 4:
        print("Usage: python3 {} <input_file> <output_file> <android_top> [compiler]".format(sys.argv[0]))
        exit(1)
    else:
        input_file = sys.argv[1]
        output_file = sys.argv[2]
        android_top = sys.argv[3]
        compiler = 'clang'
        if len(sys.argv) > 4:
            compiler = sys.argv[4]
        build_compile_commands(input_file, output_file, android_top, compiler)

简单来说,就是先从编译日志中找到编译命令的行,这种行最基本的特征的会包含clang或者clang++关键字,compile_commands.json中有3项需要填,directory指的是安卓根目录,这个可以直接传入该脚本,注意需要是绝对路径;file指的是这条命令编译的是哪个源文件,这个可以从编译命令中拿到,特征是以.c或者.cpp结尾的那一项,最后是arguments,这一项是从编译命令中拷贝过去的,注意其中需要处理的是clang或者clang++,需要加上安卓根目录路径。

以上就是使用vscode配置android 开发c++的全部内容!

相关推荐
BD_Marathon11 小时前
【MySQL】函数
android·数据库·mysql
西西学代码11 小时前
安卓开发---耳机的按键设置的UI实例
android·ui
maki07716 小时前
虚幻版Pico大空间VR入门教程 05 —— 原点坐标和项目优化技巧整理
android·游戏引擎·vr·虚幻·pico·htc vive·大空间
千里马学框架16 小时前
音频焦点学习之AudioFocusRequest.Builder类剖析
android·面试·智能手机·车载系统·音视频·安卓framework开发·audio
fundroid19 小时前
掌握 Compose 性能优化三步法
android·android jetpack
TeleostNaCl20 小时前
如何在 IDEA 中使用 Proguard 自动混淆 Gradle 编译的Java 项目
android·java·经验分享·kotlin·gradle·intellij-idea
旷野说1 天前
Android Studio Narwhal 3 特性
android·ide·android studio
maki0771 天前
VR大空间资料 01 —— 常用VR框架对比
android·ue5·游戏引擎·vr·虚幻·pico
xhBruce1 天前
InputReader与InputDispatcher关系 - android-15.0.0_r23
android·ims
领创工作室1 天前
安卓设备分区作用详解-测试机红米K40
android·java·linux