原理:
- 所有代码文件和Keil 项目文件在同一个文件夹;
- python 脚本里调用clang-format.exe 格式化所有.c .h .cpp .hpp 文件;
- Keil 菜单里添加外部工具,调用python 脚本;
1、 文件夹结构
要求所有源代码都和Keil 项目文件在同一个文件夹,或者在子文件夹里,比如:
C/C++ 源代码都在src 或者lib 文件夹下。库代码如果也在同一个文件夹,比如把CMSIS 放这里,那就也会被格式化。
2.1、clang-format
是个专门用来格式化C/C++ 代码文件的程序,安装LLVM 或者CLANG 编译器的时候会带上,Keil 的armclang 编译器也带了这个,要加到PATH 里。
clang-format 默认的美化风格很可能不是想要的效果,需要自己整个配置文件.clang-format
,一样放在项目文件夹底下,上面截图里就有。我用的配置文件是这样:
BasedOnStyle: google
# 缩进格式:4 空格
IndentWidth: 4
TabWidth: 4
UseTab: Never
NamespaceIndentation: All
ColumnLimit: 0
AlignTrailingComments: true
MaxEmptyLinesToKeep: 2
# 花括号风格:else 块另起一行
BreakBeforeBraces: Custom
BraceWrapping:
BeforeElse: True
BreakConstructorInitializers: AfterColon
# BracketAlignmentStyle: Align
AlignConsecutiveMacros:
Enabled: true
AcrossComments: false
AlignOperands: AlignAfterOperator
BreakBeforeBinaryOperators: All
BreakBeforeTernaryOperators: true
IndentPPDirectives: BeforeHash
AlwaysBreakAfterReturnType: None
AlwaysBreakAfterDefinitionReturnType: None
这个配置文件应该是YAML 格式,和python 一样,不能随便缩进,每个空格都是有用的[doge]。用这个配置,格式化出来是这样:
int main(void) {
setup();
using namespace scheduler_basic;
for (const auto& p : TUBE.pin.seg) {
p.set();
}
TimeCycle<TimeSource> c;
int i = 0;
while (1) {
if (c.cycle(200)) {
TUBE.pin.dig[i].toggle();
i++;
if (i == DIG_COUNT) {
i = 0;
}
}
}
}
直接执行clang-format.exe 没办法一次性把全部代码文件格式化,它的命令行参数好像只能一个一个文件来,或者用另外一个文件先把所有要格式化的文件的路径放进去,作为列表文件,再把这个列表文件放在命令行参数里传进去,所以我用了个python 脚本来生成列表文件。
2.2、Python 脚本
脚本内容如下:
from pathlib import Path
import os
import subprocess
from itertools import chain
from typing import List, Iterable
def multi_glob(parent: Path, pattern_list: List[str], *, recursive=False) -> Iterable[Path]:
if not recursive:
func_glob = lambda s: parent.glob(s)
else:
func_glob = lambda s: parent.rglob(s)
return chain(*(map(func_glob, pattern_list)))
# ===========================================================
here = os.path.dirname(__file__)
here = Path(here)
# 找到.c, .h, .cpp, .hpp, .cxx, .hxx
file_list = multi_glob(here, [ '*.[ch]', '*.[ch]pp', '*.[ch]xx' ], recursive=True)
file_path_list = map(lambda f: str(f.relative_to(here)), file_list)
text = '\n'.join(file_path_list)
list_file = here / 'temp-clang-format-source-files.txt'
list_file.write_text(text)
print('== SOURCE FILE LIST ==')
print(text)
print('======================')
input('PROCEED? ')
subprocess.call(f'clang-format -i -files="{list_file.name}"', shell=True)
input('EXIT→')
os.remove(list_file)
第37 行调用clang-format,用命令行参数传入列表文件。
subprocess.call(f'clang-format -i -files="{list_file.name}"', shell=True)
第23 行递归搜索当前目录及所有子目录下的代码文件,过滤出C/C++ 的代码和头文件:
file_list = multi_glob(cwd, [ '*.[ch]', '*.[ch]pp', '*.[ch]xx' ], recursive=True)
然后会在项目目录底下生成个临时的列表文件temp-clang-format-source-files.txt
,脚本执行完就自动删除。
3、KEIL 添加外部工具
菜单栏Tools > Customize Tools Menu
,打开对话框,设置调用python 脚本的命令:
把Run Independent 勾上。需要本地电脑安装了Python 环境,版本应该至少3.6。然后Tools 菜单底下就多了个自定义命令,单击运行后弹出黑窗口:
上面显示了所有被过滤出来的文件,再按一下回车确认,就调用clang-format 执行格式化。
没有报错,就再点一次回车退出窗口。