KEIL 添加格式化 批量格式化
在写代码时,通常都离不开格式化更具。运用格式化工具能使我们的代码更加的美观。
然而KEIL 没有内置格式化工具。因此我们需要自己为其添加格式化工具。之前我使用的是 AStyle
,效果还不错,网上一般也是建议使用这个工具。
但是本人更加钟爱 vscode
的 C/C++
格式化工具:clang-format
。这个工具可配置性更高,格式化出来的效果比 AStyle
好。
比如变量定义初始化时,可以等号对齐,宏定义时,可以宏定义对齐。注释也支持对齐。显然,这样格式化出来的代码更加美观。
直接将宏定义的代码块转换函数格式,这个功能简直不要太好。
使用教程:
1. 使用 clang-format
直接去官网下载。因为我vscode里面下载了这个插件的。因此直接在vscode插件目录下找到这个软件。
C:\Users\28328\.vscode\extensions\ms-vscode.cpptools-1.3.1\LLVM\bin
执行:
.\clang-format.exe -style=LLVM -i main.cpp
即可对 main.cpp
进行格式化。
通过以下命令,可以导出默认配置,通过修改配置,可以实现我们自定义格式化效果。
.\clang-format.exe -style=llvm -dump-config > .clang-format
将配置输出到 .clang-format
中。上面的命令如果是在 powershell中运行的话,会导致输出编码格式不正确,需要将文件转成UTF-8 编码格式。建议使用CMD。
使用配置,则通过以下方式:
.\clang-format.exe -style=file .\.clang-format -i main.cpp
即可使用我们修改过的配置文件。上面的命令如果是在powershell中运行的话,clang-format.exe读取file后,会导致file文件格式混乱,从而无法再此使用。因此,我们每次都需要建立一个file的副本,来确保powershell运行这个命令不会出错。建议使用CMD。
默认导出的配置还达不到vscode的默认效果,因此需要进行修改。
将缩进设置为4个字符,并且将 {}
设置为前后换行。这是我比较喜欢的配置,代码更加清晰。
然后还有变量初始化对齐和宏定义对齐,不允许小块代码写在一行。
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: true
AlignConsecutiveAssignments: true
AlignConsecutiveBitFields: true
AlignConsecutiveDeclarations: true
AllowShortFunctionsOnASingleLine: false
IndentWidth: 4
BreakBeforeBraces: Allman
简单配置即可达到图片的效果。
2. KEIL 配置格式化工具
在KEIL 中配置格式化工具。
这里将Comand设置为格式化工具路径。然后Arguments设置为 -style=file C:\Users\28328\.vscode\extensions\ms-vscode.cpptools-1.3.1\LLVM\bin\.clang-format -i !E
。这个设置表示,使用配置文件格式来 格式化当前文件,即编辑器的当前文件。
当需要格式化整个文件夹的文件时,按如下配置。按理来说,比如 AStyle
会在Arguments里面配置 $E*.c $E*.h
。由于 clang-format
的bug,这样设置会导致报错。因为这两个模糊输入是在同一个文件夹,因此设置为 -i "$E*.c"
,只格式化目录下的所有 .c
文件。
当然Arguments参数前面还是加上我们自定义的格式配置文件 -style=file C:\Users\28328\.vscode\extensions\ms-vscode.cpptools-1.3.1\LLVM\bin\.clang-format
。即再 -i
前面补上这个参数。
当格式化所有 *.c
文件时,任然存在bug。bug表现为:当工作目录和需要格式化的文件不在同一个目录时,格式化的配置参数会部分失效。比如我软件运行在.\test\diraa\
下,但是文件在 .\test\dirbb\
会导致宏定义格式化不对其。当软件运行目录也在 .\testdirbb\
下时,格式化是正常的。因此这里配置了,任然到不到完美的效果。
所以我对 clang-format
进行了功能扩充修改。当输入为目录时,我们直接切换工作路径,然后格式化这个路径下的所有 .c 和 .h 文件,来解决上面提出的两个bug。源代码附于文章末尾。
因此,只需要将KEIL,进行如下配置,即可完美格式化。Command配置为:
C:\Users\28328\.vscode\extensions\ms-vscode.cpptools-1.3.1\LLVM\bin\clang_format_custom.exe
或者这样:
但是抽风的KEIL参数,还是会导致bug。
如果以上面的参数设置,会得到一个路径,但是KEIL在处理路径时,直接字符串参数传给命令行,这时最后一个"
将变成\"
,好了,这将导致一个错误。因此需要将 "$E"
修改为 "$E\"
。或者直接去掉 ""
即可。
但是这还没完,程序运行任然报错。上面两个不同的设置将导致不同的错误,我也是非常的郁闷。但是格式化结果是完美的,因此忽略这个错误了。这里的 invalid argument
错误应该是KEIL报告的。从第一行的命令来看,格式是没有任何问题的。
C:\Users\28328\.vscode\extensions\ms-vscode.cpptools-1.3.1\LLVM\bin\clang_format_custom.exe -i "E:\Projects\2020\Railway_Finder\Finder_terminal\Railway_Finder\examples\ble_peripheral\Railway_Finder_terminal\pca10040e\s112\user_app_src\\"
invalid argument
-i
E:\Projects\2020\Railway_Finder\Finder_terminal\Railway_Finder\examples\ble_peripheral\Railway_Finder_terminal\pca10040e\s112\user_app_src\
full_path: C:\Users\28328\.vscode\extensions\ms-vscode.cpptools-1.3.1\LLVM\bin\clang-format.exe -i E:\Projects\2020\Railway_Finder\Finder_terminal\Railway_Finder\examples\ble_peripheral\Railway_Finder_terminal\pca10040e\s112\user_app_src\
format_file: C:\Users\28328\.vscode\extensions\ms-vscode.cpptools-1.3.1\LLVM\bin\.clang-format
work path: E:\Projects\2020\Railway_Finder\Finder_terminal\Railway_Finder\examples\ble_peripheral\Railway_Finder_terminal\pca10040e\s112\user_app_src\
C:\Users\28328\.vscode\extensions\ms-vscode.cpptools-1.3.1\LLVM\bin\clang_format_custom.exe -i E:\Projects\2020\Railway_Finder\Finder_terminal\Railway_Finder\examples\ble_peripheral\Railway_Finder_terminal\pca10040e\s112\user_app_src\
invalid argument
-i
E:\Projects\2020\Railway_Finder\Finder_terminal\Railway_Finder\examples\ble_peripheral\Railway_Finder_terminal\pca10040e\s112\user_app_src\
full_path: C:\Users\28328\.vscode\extensions\ms-vscode.cpptools-1.3.1\LLVM\bin\clang-format.exe -i E:\Projects\2020\Railway_Finder\Finder_terminal\Railway_Finder\examples\ble_peripheral\Railway_Finder_terminal\pca10040e\s112\user_app_src\
format_file: C:\Users\28328\.vscode\extensions\ms-vscode.cpptools-1.3.1\LLVM\bin\.clang-format
work path: E:\Projects\2020\Railway_Finder\Finder_terminal\Railway_Finder\examples\ble_peripheral\Railway_Finder_terminal\pca10040e\s112\user_app_src\
可以修改源码来解决上报面的报错。不过既然格式没有问题了,我也不管KEIL参数的bug了。
另外很重要的一点。KEIL支持对配置的tool设置快捷键,这样,我们可以通过设置 shift + alt + F
来实现和vscode一样的快捷键格式化,完美。
源码如下,格式配置参见gitee。(下面的代码就是通过格式化工具格式的):
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <windows.h>
#define TOOL_PATH_LEN 1024
#define FULL_PATH_LEN 16384
#define LOG_INFO(...) printf(__VA_ARGS__)
char tool_path[FULL_PATH_LEN] = "C:\\Users\\28328\\.vscode\\extensions\\ms-vscode.cpptools-1.3.1\\LLVM\\bin\\";
char tool_name[128] = "clang-format.exe";
char format_file_name[] = ".clang-format";
static int path_index = 0;
static int get_format_file(char *file_path);
static int rm_format_file(void);
static int hj_copy_file(char *old_file, char *new_file);
static _Bool is_dir(char *name);
int main(int argv, char *argc[])
{
char full_path[FULL_PATH_LEN] = {0};
char format_file[TOOL_PATH_LEN] = {0};
memcpy(full_path, tool_path, TOOL_PATH_LEN);
memcpy(format_file, tool_path, TOOL_PATH_LEN);
strncat(full_path, tool_name, FULL_PATH_LEN - strlen(tool_path));
strncat(format_file, format_file_name, TOOL_PATH_LEN - strlen(tool_path));
for (int i = 1; i < argv; i++)
{
LOG_INFO("%s\n", argc[i]);
strncat(full_path, " ", FULL_PATH_LEN - strlen(tool_path));
strncat(full_path, argc[i], FULL_PATH_LEN - strlen(tool_path));
if (0 == strncmp(argc[i], "-i", 2))
{
path_index = i + 1;
}
}
LOG_INFO("full_path: %s\n", full_path);
LOG_INFO("format_file: %s\n", format_file);
/* 格式化目录下的所有.c .h 文件 */
if (is_dir(argc[path_index]))
{
char work_path[256] = {0};
strcat(work_path, argc[path_index]);
if (work_path[strlen(work_path) - 1] != '\\')
{
strcat(work_path, "\\");
}
LOG_INFO("work path: %s\n", work_path);
int ret = chdir(work_path);
if (ret)
{
LOG_INFO("%s:%d: chdir error: %d\n", __func__, __LINE__, ret);
return 0;
}
char temp[FULL_PATH_LEN] = {0};
memcpy(temp, full_path, FULL_PATH_LEN);
strcat(temp, "*.c");
get_format_file(format_file);
system(temp);
rm_format_file();
memcpy(temp, full_path, FULL_PATH_LEN);
strcat(temp, "*.h");
get_format_file(format_file);
system(temp);
rm_format_file();
}
else
{/* 格式化单个文件 */
get_format_file(format_file);
system(full_path);
rm_format_file();
}
return 0;
}
static int get_format_file(char *file_path)
{
int ret = hj_copy_file(file_path, format_file_name);
if (ret)
{
LOG_INFO("%s:%d: copy file error: %d\n", __func__, __LINE__, ret);
return ret;
}
return ret;
}
static int rm_format_file(void)
{
uint32_t ret = remove(format_file_name);
if (ret)
{
LOG_INFO("%s:%d: remove format file error: %d\n", __func__, __LINE__, ret);
}
return ret;
}
static int hj_copy_file(char *old_file, char *new_file)
{
if (!old_file || !new_file)
{
return 1;
}
#define BUFFER_LEN 16384
FILE *fp = NULL;
FILE *fp_out = NULL;
char buffer[BUFFER_LEN] = {0};
int ret = 0;
fp = fopen(old_file, "rb");
if (!fp)
{
LOG_INFO("%s:%d: open file error.\n", __func__, __LINE__);
return 2;
}
ret = fread(buffer, sizeof(char), BUFFER_LEN, fp);
if (ret <= 0)
{
LOG_INFO("%s:%d: read file error.\n", __func__, __LINE__);
return 3;
}
fclose(fp);
fp_out = fopen(new_file, "wb");
if (!fp_out)
{
LOG_INFO("%s:%d: open file error.\n", __func__, __LINE__);
return 4;
}
ret = fwrite(buffer, sizeof(char), ret, fp_out);
if (ret <= 0)
{
LOG_INFO("%s:%d: write file error.\n", __func__, __LINE__);
return 5;
}
fclose(fp_out);
return 0;
}
static _Bool is_dir(char *name)
{
struct _stat buf;
int result = 0;
result = _stat(name, &buf);
if (_S_IFDIR & buf.st_mode)
{
return TRUE;
}
else if (_S_IFREG & buf.st_mode)
{
return FALSE;
}
}