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
需要导出以下环境变量:
$ 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配置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脚本:
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++的全部内容!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。