网站建设设计公司/生成关键词的软件
前言
项目经过一段时间的耕耘终于进入了团队开发阶段,期间出现了很多问题,其中一个就是开会讨论团队的代码风格规范,目前项目代码风格比较混乱,有的模块是驼峰,有的模块是匈牙利,后面经过讨论,决定采用匈牙利,和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 build
和cmake --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怎么做到的生成。
尝试集成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/
相关文章:

尝试把clang-tidy集成到AWTK项目
前言 项目经过一段时间的耕耘终于进入了团队开发阶段,期间出现了很多问题,其中一个就是开会讨论团队的代码风格规范,目前项目代码风格比较混乱,有的模块是驼峰,有的模块是匈牙利,后面经过讨论,…...

一文了解性能优化的方法
背景 在应用上线后,用户感知较明显的,除了功能满足需求之外,再者就是程序的性能了。因此,在日常开发中,我们除了满足基本的功能之外,还应该考虑性能因素。关注并可以优化程序性能,也是体现开发能…...

【怎么用系列】短视频戒断——对推荐算法进行干扰
如今推荐算法已经渗透到人们生活的方方面面,尤其是抖音等短视频核心就是推荐算法。 【短视频的危害】 1> 会让人变笨,慢慢让人丧失注意力与专注力 2> 让人丧失阅读长文的能力 3> 让人沉浸在一个又一个快感与嗨点当中。当我们刷短视频时&#x…...

C#中的委托(Delegate)
什么是委托? 首先,我们要知道C#是一种强类型的编程语言,强类型的编程语言的特性,是所有的东西都是特定的类型 委托是一种存储函数的引用类型,就像我们定义的一个 string str 一样,这个 str 变量就是 string 类型. 因为C#中没有函数类型,但是可以定义一个委托类型,把这个函数…...

PostCss
什么是 PostCss 如果把 CSS 单独拎出来看,光是样式本身,就有很多事情要处理。 既然有这么多事情要处理,何不把这些事情集中到一起统一处理呢? PostCss 就是基于这样的理念出现的。 PostCss 类似于一个编译器,可以将…...

Linux 系统上安装 Docker 并进行配置
Docker 是一种开源的应用容器引擎,它允许开发者打包他们的应用以及应用的依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPh…...

DeepSeek 等 AI 技术能否推动股市的繁荣?
在科技浪潮汹涌澎湃的当下,DeepSeek 等 AI 技术宛如闪耀在天际的耀眼星辰,吸引着全球各界的高度关注。面对这些前沿技术,投资者和市场参与者心中不禁泛起疑问:它们是否能成为推动股市繁荣的强劲动力?这一问题不仅左右着…...

【网络】应用层协议http
文章目录 1. 关于http协议2. 认识URL3. http协议请求与响应格式3.1 请求3.2 响应 3. http的常见方法4. 状态码4.1 常见状态码4.2 重定向 5. Cookie与Session5.1 Cookie5.1.1 认识Cookie5.1.2 设置Cookie5.1.3 Cookie的生命周期 5.2 Session 6. HTTP版本(了解&#x…...

大数据数仓实战项目(离线数仓+实时数仓)2
1.课程目标和课程内容介绍 2.数仓维度建模设计 3.数仓为什么要分层 4.数仓分层思想和作用 下面是阿里的一种分层方式 5.数仓中表的种类和同步策略 6.数仓中表字段介绍以及表关系梳理 订单表itcast_orders 订单明细表 itcast_order_goods 商品信息表 itcast_goods 店铺表 itcast…...

测试csdn图片发布
测试csdn图片发布 ...

站在JavaScript的视角去看,HTML的DOM和GLTF的Json数据。
很多前端小伙伴没有见过、操作过gltf文件,对非常懵逼,本文从前端小伙伴最熟悉的dom模型为切入口,以类别的方式来学习一下gltf文件。 一、结构与组织形式 HTML DOM(文档对象模型): 树形结构:HT…...

传输层协议 UDP 与 TCP
🌈 个人主页:Zfox_ 🔥 系列专栏:Linux 目录 一:🔥 前置复盘🦋 传输层🦋 再谈端口号🦋 端口号范围划分🦋 认识知名端口号 (Well-Know Port Number) 二…...

VSCode源码分析参考资料
VSCode Architecture Analysis - Electron Project Cross-Platform Best Practices 中文版 VSCode 架构分析 - Electron 项目跨平台最佳实践 Sihan Li博客上的vscode源码分析系列:分析了微服务架构、事件体系、资源管理、配置系统等 文召博客上的vscode 源码解析…...

使用VCS对Verilog/System Verilog进行单步调试的步骤
Verilog单步调试: System Verilog进行单步调试的步骤如下: 1. 编译设计 使用-debug_all或-debug_pp选项编译设计,生成调试信息。 我的4个文件: 1.led.v module led(input clk,input rst_n,output reg led );reg [7:0] cnt;alwa…...

ROS-激光雷达-消息包格式-获取激光雷达数据-激光雷达避障
文章目录 激光雷达原理 消息包格式获取激光雷达数据激光雷达避障 激光雷达 原理 激光雷达(LiDAR) 是一种利用激光进行距离测量和环境感知的传感器。它通过发射激光束并接收反射光来测量物体的距离,生成点云数据,用于构建环境的三…...

c++之模板进阶
在前面的文章中,我们已经简单的了解了模板的使用,在这篇文章中,我们将继续深入探讨模板 1.模板的特化 1.1 概念 通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果&a…...

关于Internet Download Manager(IDM)强制下载合并相关二次开发
目录 前言 强制下载视频 强制合并 迁移下载列表 免责声明 附录 前言 那个下载工具IDM不说了,确实有很多便捷的功能,不过也有一些限制 常见的包括但不限于: 1.无法下载有版权保护的视频(不管真假) 2.有时候下载…...

鸿蒙HarmonyOS Next 视频边播放边缓存- OhosVideoCache
OhosVideoCache 是一个专为OpenHarmony开发(HarmonyOS也可以用)的音视频缓存库,旨在帮助开发者轻松实现音视频的边播放边缓存功能。以下是关于 OhosVideoCache 的详细介绍: 1. 核心功能 边播放边缓存:将音视频URL传递给 OhosVideoCache 处理后…...

(10) 如何获取 linux 系统上的 TCP 、 UDP 套接字的收发缓存的默认大小,以及代码范例
(1) 先介绍下后面的代码里要用到的基础函数: 以及: (2) 接着给出现代版的 读写 socket 参数的系统函数 : 以及: (3) 给出 一言的 范例代码,获取…...

程序代码篇---项目目录结构HSV掩膜Opencv图像处理
文章目录 前言第一部分:项目目录结构第二部分:HSV提取HSV色调(Hue)含义取值范围 饱和度(Saturation)含义取值范围 亮度(Value)含义取值范围 第三部分:Opencv图像处理1. 读…...

注解与反射基础
注解 概述 注解(Annotation),从jdk5.0引入。 作用 不是程序本身,可以对程序作出解释(这一点和注释没什么区别)可以被其他程序读取 格式 注释是以“注释名”在代码中存在的,还可以添加一些…...

Vue指令v-html
目录 一、Vue中的v-html指令是什么?二、v-html指令与v-text指令的区别? 一、Vue中的v-html指令是什么? v-html指令的作用是:设置元素的innerHTML,内容中有html结构会被解析为标签。 二、v-html指令与v-text指令的区别…...

院校联合以项目驱动联合培养医工计算机AI人才路径探析
一、引言 1.1 研究背景与意义 在科技飞速发展的当下,医疗人工智能作为一个极具潜力的新兴领域,正深刻地改变着传统医疗模式。从疾病的早期诊断、个性化治疗方案的制定,到药物研发的加速,人工智能技术的应用极大地提升了医疗服务…...

CDDIS从2025年2月开始数据迁移
CDDIS 将从 2025 年 2 月开始将我们的网站从 cddis.nasa.gov 迁移到 earthdata.nasa.gov,并于 2025 年 6 月结束。 期间可能对GAMIT联网数据下载造成影响。...

前端 | JavaScript中的reduce方法
1. 什么是reduce reduce 方法是 JavaScript 中数组的重要方法之一,用于对数组中的元素进行累积计算。它接收一个回调函数作为参数,并返回一个最终计算结果。reduce 在许多场景下都非常有用,比如求和、数组扁平化、对象计数、数据转换等。 2…...

【C++】B2124 判断字符串是否为回文
博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 💯前言💯题目描述输入格式:输出格式:样例: 💯方法一:我的第一种做法思路代码实现解析 💯方法二:我…...

人工智能学习(五)之机器学习逻辑回归算法
深入剖析机器学习逻辑回归算法 一、引言 在机器学习领域,逻辑回归是一种极为经典且应用广泛的算法。虽说名字里带有 “回归”,但它主要用于解决分类问题,在医学、金融、互联网等多个领域都发挥着关键作用。例如,在医学上辅助判断…...

Bash 基础与进阶实践指南
目录 Bash 简介与基础基本命令与文件操作权限管理与用户管理重定向与管道变量与环境变量通配符与正则表达式Shell 脚本结构与控制流常用内建命令与技巧文本处理常用命令作业控制与进程管理别名与函数实用技巧与注意事项更多 Bash 进阶话题参考资源 1. Bash 简介与基础 1.1 什…...

基于开源AI智能名片2 + 1链动模式S2B2C商城小程序视角下的个人IP人设构建研究
摘要:本文深入探讨在开源AI智能名片2 1链动模式S2B2C商城小程序的应用场景下,个人IP人设构建的理论与实践。通过剖析个人IP人设定义中的“诉求”“特质”“可感知”三要素,结合该小程序特点,阐述其对个人IP打造的影响与推动作用&…...

基于springboot+vue的航空散货调度系统
开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:…...