热搜: fiddler git ip 代理
历史搜索

Android怎么配置C++开发环境

游客2024-08-04 10:30:01
目录文章目录
  1. 1. 解决的问题
  2. 2. 实践步骤

Android 配置 C++开发环境是进行 Android 平台上 C++开发所必需的一项任务。

Android怎么配置C++开发环境 1

1. 解决的问题

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

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

2. 实践步骤

下面以 Android12 为例说明

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

2.1. 生成 compile_commands.json

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

需要导出以下环境变量:

$ export SOONG_GEN_COMPDB=1
$ export SOONG_GEN_COMPDB_DEBUG=1

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

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

$ make nothing

生成的 compile_commands.json 目录:

out/soong/development/ide/compdb/compile_commands.json

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

2.2. 配置 vscode

首先需要使用 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 脚本:

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。

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 中加入如下参数,就可以打印出完整的编译命令:

LOCAL_CFLAGS += -v
LOCAL_CXXFLAGS += -v

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

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

可以通过如下脚本实现:

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++的全部内容!

标签:Android