尝试把clang-tidy集成到AWTK项目

前言

项目经过一段时间的耕耘终于进入了团队开发阶段,期间出现了很多问题,其中一个就是开会讨论团队的代码风格规范,目前项目代码风格比较混乱,有的模块是驼峰,有的模块是匈牙利,后面经过讨论,决定采用匈牙利,和awtk库的api风格一致。

讨论完之后就是改代码了,有十几个模块几百个函数要改,一个个人工去改显然费时费力,改的时候就在想这种东西有没有自动化的做法了?

于是下班开始探索一番,首先是尝试用AI写一个批量扫描文件,用正则匹配不符合规则的python脚本,结果费时费力,效果明显不好。

不对,c/c++发展了十几年,这类问题难道没有现成的方案?

后面搜了点原理,正则的思路对于这类问题显然不对,没法识别函数和变量,那只能用抽象语法树了。对象是c/c++,应该是一个类似c/c++编译器的静态分析工具。

最后找到了clang-tidy。

尝试集成1.0

首先下载clang-tidy:

pip install clang-tidy

假设示例项目src结构如下:

src
├── application.c
├── common
│   ├── navigator.c
│   ├── navigator.h
│   └── temperature.h
├── main.c
├── modules
│   ├── libframebuffer.c
│   └── libframebuffer.h
├── pages
│   └── home_page.c
└── SConscript

要修改的文件例:libframebuffer,里面api是驼峰格式,需要自动改成匈牙利格式,home_page有引用。

libframebuffer.h

#ifndef LIBFRAMEBUFFER_H
#define LIBFRAMEBUFFER_H
typedef struct {int width;int height;int bpp;void* buffer;
} LibFrameBuffer;LibFrameBuffer* LibFrameBufferInit();void LibFrameBufferFree(LibFrameBuffer* fb);#endif 	

libframebuffer.c

#include "libframebuffer.h"
#include <stdlib.h>LibFrameBuffer* LibFrameBufferInit()
{return (LibFrameBuffer*)malloc(sizeof(LibFrameBuffer));
}void LibFrameBufferFree(LibFrameBuffer* fb)
{free(fb);
}

home_page.c

#include "awtk.h"
#include "libframebuffer.h"/*** 初始化窗口的子控件*/
static ret_t visit_init_child(void* ctx, const void* iter) {widget_t* win = WIDGET(ctx);widget_t* widget = WIDGET(iter);const char* name = widget->name;// 初始化指定名称的控件(设置属性或注册事件),请保证控件名称在窗口上唯一if (name != NULL && *name != '\0') {}return RET_OK;
}/*** 初始化窗口*/
ret_t home_page_init(widget_t* win, void* ctx) {(void)ctx;return_value_if_fail(win != NULL, RET_BAD_PARAMS);widget_foreach(win, visit_init_child, win);LibFrameBufferInit();return RET_OK;
}

在项目根目录下设置好.clang-tidy,这个是clang-tidy的配置文件, clang-tidy有很多的check项,和代码命名风格相关的是readability-identifier-naming,这个checker下面有非常多类型的拼写设置,我这里设置了类, 变量,函数,宏四个类型的拼写项,其中前三个的lower_case对应的就是匈牙利小写写法,最后一个UPPER_CASE是全大写写法。

readability-identifier-naming的具体设置可见:https://clang.llvm.org/extra/clang-tidy/checks/readability/identifier-naming.html

Checks: >readability-identifier-naming
CheckOptions:- key:             readability-identifier-naming.ClassCasevalue:           lower_case- key:             readability-identifier-naming.VariableCasevalue:           lower_case- key:             readability-identifier-naming.FunctionCasevalue:           lower_case- key:             readability-identifier-naming.MacroDefinitionCasevalue:           UPPER_CASE

clang-tidy本身相当于半个编译器,会对翻译单元的头文件进行处理,而不是像脚本那样单纯进行文本解析,所以在给clang-tidy一个找不到头文件的源文件时会导致报错:

zhangdalin@huwyi-ubuntu:~/AWStudioProjects/awtk_clang_tidy_test$ clang-tidy src/pages/home_page.
Error while trying to load a compilation database:
Could not auto-detect compilation database for file "src/pages/home_page."
No compilation database found in /home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/pages or any parent directory
fixed-compilation-database: Error while opening fixed database: No such file or directory
json-compilation-database: Error while opening JSON database: No such file or directory
Running without flags.
Error while processing /home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/pages/home_page..
error: no input files [clang-diagnostic-error]
error: no such file or directory: '/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/pages/home_page.' [clang-diagnostic-error]
error: unable to handle compilation, expected exactly one compiler job in '' [clang-diagnostic-error]
Found compiler error(s).

因此必须找到一个方法让clang-tidy能够读取到项目所有的源文件和头文件。

使用compiler_command.json

compiler_command.json上面记录了项目每个编译的编译命令,输入源文件和输出obj,类似于给工具一个符号表,clang-tidy通过这张符号表进行解析。

在CMake可以设置set(CMAKE_EXPORT_COMPILE_COMMANDS ON)生成compiler_command.json给clang-tidy,我于是去看scons有没有对应设置,还好有,见:https://scons.org/doc/latest/HTML/scons-user/ch27.html

在SConstruct和SConscript分别添加env.Tool('compilation_db')env.CompilationDatabase()[0], scons就能在BIN_DIR得到compiler_command.json,我试过在SConscript把两个指令一起加进去,不知道为什么在windows会报找不到AttributeError: 'SConsEnvironment' object has no attribute '__COMPILATIONDB_Entry':的错误,Linux上就正常。我这里的情形是跨平台,windows和linux都需要考虑使用。

SConstruct

+env = helper.call(DefaultEnvironment)
+env.Tool('compilation_db')

SConscript

sources = Glob('**/*.c') + Glob('*.c')
+compile_database = env.CompilationDatabase()[0]

scons编译,在BIN_DIR得到compiler_command.json,验证通过。

接下来写clang-tidy调用逻辑,scons本身是python脚本,直接封装成函数就好了,难点在于如何让scons执行时,如果需要将封装函数也一并调用,对此,scons提供了Options选项, 可以自定义选项来控制调用逻辑,见:https://scons.org/doc/latest/HTML/scons-user/ch10.html

这里就提供一个--clang-tidy选项给scons,修改后的SConscript:

import os
import subprocess
from SCons.Script import *env = DefaultEnvironment().Clone()
BIN_DIR = os.environ['BIN_DIR']
LIB_DIR = os.environ['LIB_DIR']sources = Glob('**/*.c') + Glob('*.c')AddOption('--clang-tidy',dest='clang_tidy',metavar='BOOL',action='store_true',default=False,help="Don't run clang-tidy static code analysis automatically")
compile_database = env.CompilationDatabase()[0]
program = env.Program(os.path.join(BIN_DIR, 'demo'), sources, LIBS = env['LIBS'])def run_clang_tidy(target, source, env):source_file = str(source[0])compilation_db = str(source[1])cmd = f"clang-tidy -p {compilation_db} -header-filter=.* {source_file}"print(f"Running: {cmd}")subprocess.run(cmd, shell=True)return 0print(f"clang_tidy option = {GetOption('clang_tidy')}")if GetOption('clang_tidy') == True:for source in sources:run_clang_tidy('',[source, compile_database], env)

执行,会看到clang-tidy把三方库的头文件也加进了检测,原因是设置用了-header-filter=.*来将项目头文件加入检测,不加这个选项是只能检测c文件的,而且函数只能检测到没有加入头文件声明的函数。

/home/zhangdalin/AWStudioProjects/awtk/3rd/SDL/include/SDL_stdinc.h:569:9: warning: invalid case style for macro definition 'SDL_malloc' [readability-identifier-naming]
#define SDL_malloc malloc^~~~~~~~~~SDL_MALLOC
/home/zhangdalin/AWStudioProjects/awtk/3rd/SDL/include/SDL_stdinc.h:570:9: warning: invalid case style for macro definition 'SDL_calloc' [readability-identifier-naming]
#define SDL_calloc calloc^~~~~~~~~~SDL_CALLOC
/home/zhangdalin/AWStudioProjects/awtk/3rd/SDL/include/SDL_stdinc.h:571:9: warning: invalid case style for macro definition 'SDL_realloc' [readability-identifier-naming]
#define SDL_realloc realloc^~~~~~~~~~~SDL_REALLOC

解决方法是在.clang-tidy下设置HeaderFilterRegex, 原理是clang-tidy搜索的文件是绝对路径的列表,设置HeaderFilterRegex将只检测和设置的正则匹配的文件路径。

注意原来脚本的-header-filter=.*要去掉,否则会覆盖.clang-tidy上的设置,那就失效了。

.clang-tidy

HeaderFilterRegex: 'awtk_clang_tidy_test\\src\\.*|awtk_clang_tidy_test/src/.*' #左边匹配windows下项目路径,右边匹配linux下项目路径

SConscript

def run_clang_tidy(target, source, env):source_file = str(source[0])compilation_db = str(source[1])cmd = f"clang-tidy -p {compilation_db} {source_file}"print(f"Running: {cmd}")subprocess.run(cmd, shell=True)return 0

自此一个基础的集成就完成了。

Running: clang-tidy -p compile_commands.json common/navigator.c
2024 warnings generated.
Suppressed 2024 warnings (2024 in non-user code).
Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
Running: clang-tidy -p compile_commands.json modules/libframebuffer.c
679 warnings generated.
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/modules/libframebuffer.c:4:17: warning: invalid case style for function 'LibFrameBufferInit' [readability-identifier-naming]
LibFrameBuffer* LibFrameBufferInit()^~~~~~~~~~~~~~~~~~lib_frame_buffer_init
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/modules/libframebuffer.c:9:6: warning: invalid case style for function 'LibFrameBufferFree' [readability-identifier-naming]
void LibFrameBufferFree(LibFrameBuffer* fb)^~~~~~~~~~~~~~~~~~lib_frame_buffer_free

尝试集成2.0

看似简单,实际引用到项目还是有问题,scons不像CMake, 编译compiler_command.json的过程和编译项目的过程是绑死的,一定要编译完项目才会生成compiler_command.json,我曾经试过把env.Program只生成json,发现json能够出来,但是里面不带任何项目相关的文件编译命令,只有编译整个项目才能更新compiler_command.json

这就是scons的不便之处了,项目是没法先静态检测再编译的,只能放到编译后再检测了。如果是CMake,大可以通过cmake -S. -B buildcmake --build build把构建compiler_command.json和编译项目的过程按先后分开。

怎么让scons在env.Program后执行自定义命令?可以用env.Command, scons编译先后顺序涉及到builder的概念,这里就不深入了,参考:

https://stackoverflow.com/questions/36273482/scons-strange-execution-order

https://scons.org/doc/1.3.0/HTML/scons-user/c3721.html

修改后的SConscript如下:

import os
import subprocess
from SCons.Script import *env = DefaultEnvironment().Clone()
BIN_DIR = os.environ['BIN_DIR']
LIB_DIR = os.environ['LIB_DIR']sources = Glob('**/*.c') + Glob('*.c')AddOption('--clang-tidy',dest='clang_tidy',metavar='BOOL',action='store_true',default=False,help="Don't run clang-tidy static code analysis automatically")
compile_database = env.CompilationDatabase()[0]
program = env.Program(os.path.join(BIN_DIR, 'demo'), sources, LIBS = env['LIBS'])def run_clang_tidy(target, source, env):source_file = str(source[0])compilation_db = str(source[1])cmd = f"clang-tidy -p {compilation_db}  {source_file}"print(f"Running: {cmd}")subprocess.run(cmd, shell=True)return 0print(f"clang_tidy option = {GetOption('clang_tidy')}")if GetOption('clang_tidy') == True:for source in sources:# 会在env.Program之后执行env.Command(target=f"{os.path.basename(str(source))}.clang-tidy.log",source=[source, compile_database],action=Action(run_clang_tidy, cmdstr="Running clang-tidy on ${SOURCE}"))

把编译出的compile_commands.json删了,然后scons --clang-tidy测试,正常运行:

zhangdalin@huwyi-ubuntu:~/AWStudioProjects/awtk_clang_tidy_test$ scons --clang-tidy
scons: Reading SConscript files ...
APP_SCRIPTS_ROOT:/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/scripts
AWTK_ROOT: /home/zhangdalin/AWStudioProjects/awtk
AWTK_SCRIPTS_ROOT: /home/zhangdalin/AWStudioProjects/awtk/scripts
...
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/bin exist.
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/lib exist.
False
AWTK_ROOT: /home/zhangdalin/AWStudioProjects/awtk
TKC_ONLY: False
{}
/home/zhangdalin/AWStudioProjects/awtk/bin/libawtk.so==>/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/bin
clang_tidy option = True
scons: done reading SConscript files.
scons: Building targets ...
Building compilation database src/compile_commands.json
Running clang-tidy on src/application.c
Running: clang-tidy -p src/compile_commands.json  src/application.c
2024 warnings generated.
Suppressed 2024 warnings (2024 in non-user code).
Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
Running clang-tidy on src/pages/home_page.c
Running: clang-tidy -p src/compile_commands.json  src/pages/home_page.c
2027 warnings generated.
src/pages/home_page.c:8:13: warning: unused variable 'win' [clang-diagnostic-unused-variable]widget_t* win = WIDGET(ctx);^
Suppressed 2026 warnings (2026 in non-user code).
Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
Running clang-tidy on src/modules/libframebuffer.c
Running: clang-tidy -p src/compile_commands.json  src/modules/libframebuffer.c
679 warnings generated.
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/modules/libframebuffer.c:4:17: warning: invalid case style for function 'LibFrameBufferInit' [readability-identifier-naming]
LibFrameBuffer* LibFrameBufferInit()^~~~~~~~~~~~~~~~~~lib_frame_buffer_init
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/modules/libframebuffer.c:9:6: warning: invalid case style for function 'LibFrameBufferFree' [readability-identifier-naming]
void LibFrameBufferFree(LibFrameBuffer* fb)^~~~~~~~~~~~~~~~~~lib_frame_buffer_free
Suppressed 677 warnings (677 in non-user code).
Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
Running clang-tidy on src/main.c
Running: clang-tidy -p src/compile_commands.json  src/main.c
2028 warnings generated.
Suppressed 2028 warnings (2028 in non-user code).
Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
Running clang-tidy on src/common/navigator.c
Running: clang-tidy -p src/compile_commands.json  src/common/navigator.c
2024 warnings generated.
Suppressed 2024 warnings (2024 in non-user code).
Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
scons: done building targets.

能正常运行了,这时候就可以加--fix选项去自动改naming错误了,我具体测试了下,还是没法全部一次性修改的,有些引用函数的文件就没有改到,在这个例子是只改了libframebuffer.c,libframebuffer.h没有修改。好在数量少,自动化工具能解决大部分。

然而还是有问题,这个方案只能在linux下使用,在windows上会报unable to handle compilation错误:

Running clang-tidy on src\modules\libframebuffer.c
Running: clang-tidy --fix -p src\compile_commands.json  src\modules\libframebuffer.c
Error while processing D:\AWStudioProjects\awtk_clang_tidy_test\src\modules\libframebuffer.c.
error: unable to handle compilation, expected exactly one compiler job in '' [clang-diagnostic-error]
warning: @C:\Users\z5843\AppData\Local\Temp\tmp_8x5etvp.lnk: 'linker' input unused [clang-diagnostic-unused-command-line-argument]
Found compiler errors, but -fix-errors was not specified.
Fixes have NOT been applied.

原因是我awtk在windows上设置的用msvc编译,scons对于msvc生成的compiler_command.json里面调用指令实际上要通过一层快捷方式文件去中转,称之为响应文件(response files), 因为windows的命令行有长度限制,没法一次调用太长的命令。

[{"command": "cl @C:\\Users\\z5843\\AppData\\Local\\Temp\\tmpwbrtpkke.lnk","directory": "D:\\AWStudioProjects\\awtk_clang_tidy_test","file": "src\\common\\navigator.c","output": "src\\common\\navigator.obj"},{"command": "cl @C:\\Users\\z5843\\AppData\\Local\\Temp\\tmpmf4y8gp4.lnk","directory": "D:\\AWStudioProjects\\awtk_clang_tidy_test","file": "src\\modules\\libframebuffer.c","output": "src\\modules\\libframebuffer.obj"},{"command": "cl @C:\\Users\\z5843\\AppData\\Local\\Temp\\tmpjye3ldpl.lnk","directory": "D:\\AWStudioProjects\\awtk_clang_tidy_test","file": "src\\pages\\home_page.c","output": "src\\pages\\home_page.obj"},{"command": "cl @C:\\Users\\z5843\\AppData\\Local\\Temp\\tmpjtyr5i36.lnk","directory": "D:\\AWStudioProjects\\awtk_clang_tidy_test","file": "src\\application.c","output": "src\\application.obj"},{"command": "cl @C:\\Users\\z5843\\AppData\\Local\\Temp\\tmpn639b_bf.lnk","directory": "D:\\AWStudioProjects\\awtk_clang_tidy_test","file": "src\\main.c","output": "src\\main.obj"},{"command": "cl @C:\\Users\\z5843\\AppData\\Local\\Temp\\tmpghyoi9f_.lnk","directory": "D:\\AWStudioProjects\\awtk_clang_tidy_test","file": "D:\\AWStudioProjects\\awtk\\3rd\\gtest\\googletest\\src\\gtest-all.cc","output": "D:\\AWStudioProjects\\awtk\\3rd\\gtest\\googletest\\src\\gtest-all.obj"},{"command": "cl @C:\\Users\\z5843\\AppData\\Local\\Temp\\tmpubp7ar8o.lnk","directory": "D:\\AWStudioProjects\\awtk_clang_tidy_test","file": "tests\\main.cc","output": "tests\\main.obj"}
]

这些快捷方式第一次编译才会创建,在第二次编译时就能复用,而且如果修改了源文件(比如修改代码或者开关宏),这些快捷方式就失效了,这就是为什么我改源文件或者删除compiler_command.json都会导致报unable to handle compilation错误。我在网上找了一圈都不知道怎么关掉这个响应文件。

其实也可以设置awtk编译方式为MINGW, 这样就没有问题了,可惜我实际项目一些三方库是msvc编译的,和mingw不兼容,这个选择就废了。

没法泛化到windows,方案还要继续改进。

BTW: CMake+MSVC在windows上是不会生成compiler_command.json的,不知道scons怎么做到的生成。

msvc就你离谱

尝试集成3.0

其实不使用compiler_command.json, 也可以通过给clang-tidy直接指明源文件和头文件路径的方式来绕过,这样检测就不依赖于编译了。

可以通过直接用scons的配置文件,比较方便,也可以独立脚本,不过路径需要脚本去暴力匹配,如果项目庞大,写起匹配逻辑会比较麻烦,这里选用前者。

之前的脚本其实还有个问题,run_clang_tidy执行一次只检测一个文件,一个文件就执行一次clang-tidy命令,这样使用是低效的,实际上clang-tidy可以批量集成多个文件一起去分析。

run_clang_tidy修改,拆除来放到clang_tidy_helper.py,置于scripts文件夹:

import subprocessdef run_clang_tidy(flags, source_file_str, cxxflags, include_file_str):cmd = f"clang-tidy {flags} {source_file_str} -- {cxxflags} {include_file_str}"print(f"Running: {cmd}")subprocess.run(cmd, shell=True)return 0

修改后的SConscript如下:

import os
import subprocess
from scripts.clang_tidy_helper import run_clang_tidy
from SCons.Script import *env = DefaultEnvironment().Clone()
BIN_DIR = os.environ['BIN_DIR']
LIB_DIR = os.environ['LIB_DIR']sources = Glob('**/*.c') + Glob('*.c')AddOption('--clang-tidy',dest='clang_tidy',metavar='BOOL',action='store_true',default=False,help="Don't run clang-tidy static code analysis automatically")program = env.Program(os.path.join(BIN_DIR, 'demo'), sources, LIBS = env['LIBS'])if GetOption('clang_tidy') == True:sources_file_str = ' '.join(os.path.normpath(str(s)) for s in sources)    flags = env['CCFLAGS'] include_file_str = ' '.join([f'-I{os.path.normpath(str(i))}' for i in env['CPPPATH']])run_clang_tidy('', sources_file_str, flags, include_file_str)

运行,可以看到输出不一样了,会统计文件数,累计warning和error数,能检测出libframebuffer的naming错误,不过这次头文件和源文件的error都可以显示了,加入–fix选项运行,发现头文件,源文件,引用的文件都被修正了。

/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/modules/libframebuffer.c:4:17: warning: invalid case style for function 'LibFrameBufferInit' [readability-identifier-naming]
LibFrameBuffer* LibFrameBufferInit()^~~~~~~~~~~~~~~~~~lib_frame_buffer_init
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/modules/libframebuffer.c:4:17: note: FIX-IT applied suggested code changes
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/modules/libframebuffer.c:9:6: warning: invalid case style for function 'LibFrameBufferFree' [readability-identifier-naming]
void LibFrameBufferFree(LibFrameBuffer* fb)^~~~~~~~~~~~~~~~~~lib_frame_buffer_free
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/modules/libframebuffer.c:9:6: note: FIX-IT applied suggested code changes
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/modules/libframebuffer.h:10:17: warning: invalid case style for function 'LibFrameBufferInit' [readability-identifier-naming]
LibFrameBuffer* LibFrameBufferInit();^~~~~~~~~~~~~~~~~~lib_frame_buffer_init
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/modules/libframebuffer.h:10:17: note: FIX-IT applied suggested code changes
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/modules/libframebuffer.h:12:6: warning: invalid case style for function 'LibFrameBufferFree' [readability-identifier-naming]
void LibFrameBufferFree(LibFrameBuffer* fb);^~~~~~~~~~~~~~~~~~lib_frame_buffer_free
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/modules/libframebuffer.h:12:6: note: FIX-IT applied suggested code changes
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/pages/../modules/libframebuffer.h:10:17: warning: invalid case style for function 'LibFrameBufferInit' [readability-identifier-naming]
LibFrameBuffer* LibFrameBufferInit();^~~~~~~~~~~~~~~~~~lib_frame_buffer_init
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/pages/../modules/libframebuffer.h:10:17: note: FIX-IT applied suggested code changes
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/pages/home_page.c:28:3: note: FIX-IT applied suggested code changesLibFrameBufferInit();^
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/pages/../modules/libframebuffer.h:12:6: warning: invalid case style for function 'LibFrameBufferFree' [readability-identifier-naming]
void LibFrameBufferFree(LibFrameBuffer* fb);^~~~~~~~~~~~~~~~~~lib_frame_buffer_free
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/pages/../modules/libframebuffer.h:12:6: note: FIX-IT applied suggested code changes
/home/zhangdalin/AWStudioProjects/awtk_clang_tidy_test/src/pages/home_page.c:8:13: warning: unused variable 'win' [clang-diagnostic-unused-variable]widget_t* win = WIDGET(ctx);^
clang-tidy applied 7 of 7 suggested fixes.
Suppressed 8802 warnings (8802 in non-user code).
Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
scons: done reading SConscript files.
scons: Building targets ...
scons: `.' is up to date.
scons: done building targets.

如果不想编译项目,单独做静态检测,也比较简单:

AddOption('--no-compile',dest='no_compile',metavar='BOOL',action='store_true',default=False,help="Don't compile the code")if GetOption('no_compile') == False:program = env.Program(os.path.join(BIN_DIR, 'demo'), sources, LIBS = env['LIBS'])

从这边开始,总算就没有什么比较难受的使用问题了,而且可以集成检测工具一起编译,也可以单独检测。

还有很多细节,比如改用run-clang-tidy.py脚本提速,ci/cd集成,时间原因先写到这里,后面有时间看看如何实现。

最终的脚本:

SConscript

import os
import subprocess
from scripts.clang_tidy_helper import run_clang_tidy
from SCons.Script import *env = DefaultEnvironment().Clone()
BIN_DIR = os.environ['BIN_DIR']
LIB_DIR = os.environ['LIB_DIR']sources = Glob('**/*.c') + Glob('*.c')AddOption('--clang-tidy',dest='clang_tidy',metavar='BOOL',action='store_true',default=False,help="Don't run clang-tidy static code analysis automatically")AddOption('--no-compile',dest='no_compile',metavar='BOOL',action='store_true',default=False,help="Don't compile the code")if GetOption('no_compile') == False:program = env.Program(os.path.join(BIN_DIR, 'demo'), sources, LIBS = env['LIBS'])if GetOption('clang_tidy') == True:sources_file_str = ' '.join(os.path.normpath(str(s.get_abspath())) for s in sources)    flags = env['CCFLAGS'] include_file_str = ' '.join([f'-I{os.path.normpath(str(i))}' for i in env['CPPPATH']])run_clang_tidy('--fix ', sources_file_str, flags, include_file_str)

SConstruct

import os
import scripts.app_helper as app
import clang_tidy as tidyCUSTOM_WIDGET_LIBS = [{"root" : '3rd/awtk-widget-label-rotate','shared_libs': ['label_rotate'],'static_libs': []
}]DEPENDS_LIBS = CUSTOM_WIDGET_LIBS + []helper = app.Helper(ARGUMENTS)
helper.set_deps(DEPENDS_LIBS)# app.prepare_depends_libs(ARGUMENTS, helper, DEPENDS_LIBS)
env = helper.call(DefaultEnvironment)SConscriptFiles = ['src/SConscript', 'tests/SConscript']
helper.SConscript(SConscriptFiles)

scripts/clang_tidy_helper.py

import os
import sys
import platform
import re
import subprocessdef run_clang_tidy(flags, source_file_str, cxxflags, include_file_str):cmd = f"clang-tidy {flags} {source_file_str} -- {cxxflags} {include_file_str}"print(f"Running: {cmd}")subprocess.run(cmd, shell=True)return 0

补充

clang-tidy不一定能一次解决所有的代码规范问题,比如私有函数习惯写__前缀,即使是用匈牙利写法也会被误报:

D:\AWStudioProjects\awtk_clang_tidy_test\src\modules\lib_framebuffer.c:11:6: warning: invalid case style for function '__lib_framebuffer_load_format' [readability-identifier-naming]      11 | void __lib_framebuffer_load_format(lib_framebuffer *fb){|      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~|      lib_framebuffer_load_format
D:\AWStudioProjects\awtk_clang_tidy_test\src\modules\lib_framebuffer.c:31:6: warning: invalid case style for function '__lib_framebuffer_init' [readability-identifier-naming]31 | void __lib_framebuffer_init(lib_framebuffer *fb, const char *path) {|      ^~~~~~~~~~~~~~~~~~~~~~|      lib_framebuffer_init
D:\AWStudioProjects\awtk_clang_tidy_test\src\modules\lib_framebuffer.c:73:6: warning: invalid case style for function '__lib_framebuffer_destroy' [readability-identifier-naming]73 | void __lib_framebuffer_destroy(lib_framebuffer *fb) {|      ^~~~~~~~~~~~~~~~~~~~~~~~~|      lib_framebuffer_destroy

clang-tidy做了一个workaround, 允许用正则的方式去忽略匹配的命名样式。

注意必须是全字匹配,部分匹配是不起效的。

Checks: >readability-identifier-naming
CheckOptions:- key:             readability-identifier-naming.ClassCasevalue:           lower_case- key:             readability-identifier-naming.VariableCasevalue:           lower_case- key:             readability-identifier-naming.FunctionIgnoredRegexpvalue:           '__.*'- key:             readability-identifier-naming.ConstexprVariableCasevalue:           UPPER_CASE

参考

https://github.com/SCons/scons/issues/4637

https://scons.org/doc/latest/HTML/scons-user/ch27.html

https://lrita.github.io/2023/03/21/auto-clang-tidy-cpp-code/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/13209.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ES面试题

1、Elasticsearch的基本构成&#xff1a; &#xff08;1&#xff09;index 索引&#xff1a; 索引类似于mysql 中的数据库&#xff0c;Elasticesearch 中的索引是存在数据的地方&#xff0c;包含了一堆有相似结构的文档数据。 &#xff08;2&#xff09;type 类型&#xff1a…

硬件电路基础

目录 1. 电学基础 1.1 原子 1.2 电压 1.3 电流 1.电流方向&#xff1a; 正极->负极,正电荷定向移动方向为电流方向&#xff0c;与电子定向移动方向相反。 2.电荷&#xff08;这里表示负电荷&#xff09;运动方向&#xff1a; 与电流方向相反 1.4 测电压的时候 2. 地线…

【VM】VirtualBox安装ubuntu22.04虚拟机

阅读本文之前&#xff0c;请先根据 安装virtualbox 教程安装virtulbox虚拟机软件。 1.下载Ubuntu系统镜像 打开阿里云的镜像站点&#xff1a;https://developer.aliyun.com/mirror/ 找到如图所示位置&#xff0c;选择Ubuntu 22.04.3(destop-amd64)系统 Ubuntu 22.04.3(desto…

零代码搭建个人博客—Zblog结合内网穿透发布公网

目录 一、准备工作二、Z-blog 网站搭建1. XAMPP 环境设置2. Z-blog 安装3. Z-blog 网页测试 三、内网穿透工具 Cpolar 的安装和配置1. Cpolar 安装2. Cpolar 云端设置3. Cpolar 本地设置 四、本地网页发布五、注意六、本次经历总结 大家好&#xff0c;我是学问小小谢。 最近心血…

动静态库的学习

动静态库中&#xff0c;不需要包含main函数 文件分为内存级(被打开的)文件和磁盘级文件 库 每个程序都要依赖很多基础的底层库&#xff0c;本质上来说库是一种可执行代码的二进制形式&#xff0c;可以被载入内存执行 静态库 linux .a windows .lib 动态库 linux .…

esp32如何接入豆包

要在 ESP32 上接入豆包工具&#xff0c;本质上是让 ESP32 设备与豆包的 API 进行通信&#xff0c;以获取相关服务。以下是具体步骤&#xff1a; 1. 准备工作 硬件准备 ESP32 开发板&#xff1a;确保你的 ESP32 开发板能正常工作&#xff0c;可以使用 USB 线连接到电脑上。电…

neo4j-在Linux中安装neo4j

目录 切换jdk 安装neo4j 配置neo4j以便其他电脑可以访问 切换jdk 因为我安装的jdk是1.8版本的&#xff0c;而我安装的neo4j版本为5.15,Neo4j Community 5.15.0 不支持 Java 1.8&#xff0c;它要求 Java 17 或更高版本。 所以我需要升级Java到17 安装 OpenJDK 17 sudo yu…

Docker使用教程

文章目录 Docker启动和校验镜像和容器常见命令 存储目录挂载卷映射数据卷命令 网络网络命令自定义网络实现redis主从同步集群 DockerCompose语法 Docker启动和校验 # 启动Docker systemctl start docker# 查看Docker运行状态 systemctl status docker# 停止Docker systemctl s…

Mac M1 ComfyUI 中 AnyText插件安装问题汇总?

Q1&#xff1a;NameError: name ‘PreTrainedTokenizer’ is not defined ? 该项目最近更新日期为2024年12月&#xff0c;该时间段的transformers 版本由PyPI 上的 transformers 页面 可知为4.47.1. A1: transformers 版本不满足要求&#xff0c;必须降级transformors &#…

大语言模型的个性化综述 ——《Personalization of Large Language Models: A Survey》

摘要&#xff1a; 本文深入解读了论文“Personalization of Large Language Models: A Survey”&#xff0c;对大语言模型&#xff08;LLMs&#xff09;的个性化领域进行了全面剖析。通过详细阐述个性化的基础概念、分类体系、技术方法、评估指标以及应用实践&#xff0c;揭示了…

SpringBoot+Dubbo+zookeeper 急速入门案例

项目目录结构&#xff1a; 第一步&#xff1a;创建一个SpringBoot项目&#xff0c;这里选择Maven项目或者Spring Initializer都可以&#xff0c;这里创建了一个Maven项目&#xff08;SpringBoot-Dubbo&#xff09;&#xff0c;pom.xml文件如下&#xff1a; <?xml versio…

算法:线性同余法(LCG,Linear Congruential Generator)

1. 线性同余法&#xff08;LCG&#xff09;是什么&#xff1f; 线性同余法&#xff08;LCG&#xff0c;Linear Congruential Generator&#xff09; 是一种最简单、最常见的伪随机数生成算法。它使用一个递推公式&#xff0c;通过线性变换生成一系列的伪随机数。 LCG 的特点&…

分析用户请求K8S里ingress-nginx提供的ingress流量路径

前言 本文是个人的小小见解&#xff0c;欢迎大佬指出我文章的问题&#xff0c;一起讨论进步~ 我个人的疑问点 进入的流量是如何自动判断进入iptables的四表&#xff1f;k8s nodeport模式的原理&#xff1f; 一 本机环境介绍 节点名节点IPK8S版本CNI插件Master192.168.44.1…

CommonAPI学习笔记-2

一. 概述 ​ 这篇文章主要是想整理并且分析CommonAPI代码生成工具根据fidl和fdepl配置文件生成出来的代码的结构和作用。 二. fidl ​ 用户根据业务需求在fidl文件中定义业务服务接口的结构以及自定义数据类型&#xff0c;然后使用core生成工具传入fidl文件生成该fidl的核心…

什么叫DeepSeek-V3,以及与GPT-4o的区别

1. DeepSeek 的故事 1.1 DeepSeek 是什么&#xff1f; DeepSeek 是一家专注于人工智能技术研发的公司&#xff0c;致力于打造高性能、低成本的 AI 模型。它的目标是让 AI 技术更加普惠&#xff0c;让更多人能够用上强大的 AI 工具。 1.2 DeepSeek-V3 的问世 DeepSeek-V3 是…

数据结构:队列篇

图均为手绘,代码基于vs2022实现 系列文章目录 数据结构初探: 顺序表 数据结构初探:链表之单链表篇 数据结构初探:链表之双向链表篇 链表特别篇:链表经典算法问题 数据结构:栈篇 文章目录 系列文章目录前言一.队列的概念和结构1.1概念一、动态内存管理优势二、操作效率与安全性…

MySQL

二进制方式&#xff1a; 下载并上传安装包到设备 创建组与用户 [rootlocalhost ~]# groupadd mysql [rootlocalhost ~]# useradd -r -g mysql -s /bin/false mysql解压安装包&#xff1a; [rootlocalhost ~]# tar xf mysql-8.0.36-linux-glibc2.28-x86_64.tar.xz -C /usr/l…

Windows电脑本地部署运行DeepSeek R1大模型(基于Ollama和Chatbox)

文章目录 一、环境准备二、安装Ollama2.1 访问Ollama官方网站2.2 下载适用于Windows的安装包2.3 安装Ollama安装包2.4 指定Ollama安装目录2.5 指定Ollama的大模型的存储目录 三、选择DeepSeek R1模型四、下载并运行DeepSeek R1模型五、常见问题解答六、使用Chatbox进行交互6.1 …

洛谷网站: P3029 [USACO11NOV] Cow Lineup S 题解

题目传送门&#xff1a; P3029 [USACO11NOV] Cow Lineup S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 前言&#xff1a; 这道题的核心问题是在一条直线上分布着不同品种的牛&#xff0c;要找出一个连续区间&#xff0c;使得这个区间内包含所有不同品种的牛&#xff0c;…

如何利用maven更优雅的打包

最近在客户现场部署项目&#xff0c;有两套环境&#xff0c;无法连接互联网&#xff0c;两套环境之间也是完全隔离&#xff0c;于是问题就来了&#xff0c;每次都要远程到公司电脑改完代码&#xff0c;打包&#xff0c;通过网盘&#xff08;如果没有会员&#xff0c;上传下载慢…