SonarQube安装及配置方法见 CentOS 8 安装 sonarqube 7.9.1 LTS
安装C语言插件
SonarQube的Community版本是不支持对C语言代码进行质量分析的,Developer及以上付费版本才支持。还好有一些开源的社区插件支持,比较著名的一个就是sonar-cxx。
在如下地址下载SonarQube开源C和C++的插件
C插件:sonar-c-plugin-1.3.1.1807.jar
https://github.com/SonarOpenCommunity/sonar-cxx/releases/download/cxx-1.3.1/sonar-c-plugin-1.3.1.1807.jar
C++插件:sonar-cxx-plugin-1.3.1.1807.jar
https://github.com/SonarOpenCommunity/sonar-cxx/releases/download/cxx-1.3.1/sonar-cxx-plugin-1.3.1.1807.jar
将下载下来的插件放到SonarQube的安装目录中的extensions/plugins中
[sonar@localhost plugins]$ pwd
# 写这个文档用的SonarQube环境的安装目录为/opt/sonarqube-7.9.1
/opt/sonarqube-7.9.1/extensions/plugins
[sonar@localhost plugins]$ ls -lh
total 103M
-rw-r--r--. 1 sonar sonar 224 Jul 10 12:21 README.txt
-rw-r--r--. 1 sonar sonar 8.1M Dec 9 17:45 sonar-c-plugin-1.3.1.1807.jar
-rw-r--r--. 1 sonar sonar 8.1M Dec 9 17:46 sonar-cxx-plugin-1.3.1.1807.jar
# 为了节省显示,省略掉了一些输出
安装完成后,使用安装目录下/bin/linux-x86-64/sonar.sh脚本重启SonarQube使安装生效。
[sonar@localhost linux-x86-64]$ pwd
/opt/sonarqube-7.9.1/bin/linux-x86-64
[sonar@localhost linux-x86-64]$ ./sonar.sh restart
Gracefully stopping SonarQube...
Waiting for SonarQube to exit...
Waiting for SonarQube to exit...
Stopped SonarQube.
Starting SonarQube...
Started SonarQube.
[sonar@localhost linux-x86-64]$
重启完成后,登录SonarQube页面,在“质量配置”页面中可以看到增加了关于C和C++的质量配置(Profile),如下所示:
需要注意,因Sonar way为内建不可更改的质量规则,即使安装了C和C++的插件,关于C和C++的代码规则都还是0,需要新建C和C++的质量规则。另外,从右侧的侧栏可以看到安装完C和C++的插件后,新增了13K的未激活的规则。
设置C语言配置
在"质量配置"页面,点击右上角的“创建”按钮,创建新的C语言的配置,在弹出的对话框中输出配置名称,如C-Profile, 语言选择C(Community)。
创建完成后,在"质量配置"页面,可以看到新创建的C语言配置和已有的Sonar way配置,默认配置为Sonar way, 两个配置的规则数都为0。先将新建的C-Profile设置为默认配置:
设置完成后,在“质量配置”页面可以看到C语言的配置规则为C-Profile:
接下来需为为C-Profile填加规则。C-Profile右侧的设置按钮有”激活更多规则“命令,点击它。
在弹出的"代码规则"页面,使用"批量修改"命令里面的"活动..."在弹出的对话框里选择新创建的C-Profile,点击“应用”。
完成后,在“质量配置”页面,可以看到C-Profile的规则数的变化:
到此C语言的质量配置已经完成,可以创建项目对C语言工程进行质量检查了。
创建项目
在"项目"页面右上角的"+"按钮中有“创建新的项目”命令,如果没有创建过项目,在页面中间也有“创建新项目”按键,使用任一方法进行项目创建的页面:
在"创建新项目"页面输入"项目标识"和”显示名“点击“设置”按钮进入项目页面。
在项目页面,按照步骤提示,第1步需要创建项目的令牌。项目令牌用于项目分析中的认证,可以避免使用用户名和密码。
令牌创建完成后,第2步需要设置被分析代码的语言,有"Java", "C# 或 VB.NET", "其它(JS, Python, PHP ...)"三个选项,C语言项目选择 "其它(JS, Python, PHP ...)"。
然后选择代码分析工具运行的操作系统。SonarQube不要求代码分析与SonarQube服务器在同一个机器或系统上,可以在其它机器上使用sonar-scanner进行代码分析,sonar-scanner会自动将分析结果传到SonarQube服务器中。
接下来,如果没有扫描器,即sonar-scanner工具,需要先下载他,使用页面中的下载按钮即可下载。在下载页面有相应的安装说明,参考它安装即可。
最后,需要将执行sonar-scanner的命令复制下来,以便在进行代码分析时使用。
使用SonarQube进行代码分析
创建需要进行质量分析的代码,如果已有代码可以跳过这步。本文使用如下代码进行验证:
[bfx@vFedora sonar-test]$ ls -lh
总用量 12K
-rw-rw-r-- 1 bfx bfx 341 12月 2 14:22 example.c
-rw-rw-r-- 1 bfx bfx 1.1K 12月 2 15:15 example-utest.c
-rw-rw-r-- 1 bfx bfx 482 12月 2 14:59 SConstruct
[bfx@vFedora sonar-test]$
/**
* example.c
* 测试示例代码
* */
#include
#include
#include
#ifdef UTEST
extern void utest();
#endif
/**
* CUint单元测试被测示例函数
*/
int func1(int a){
if (a > 200){
return 1;
}else{
return 2;
}
}
/**
* SonarQube代码质量分析示例函数
* 返回临时变量的地址,用来检查SonarQube是否正常工作
*/
char *func2(void){
char *name[256];
return name;
}
int main(){
printf("Hello\n");
#ifdef UTEST
utest();
#endif
return 0;
}
/**
* example-utest.c
* 单元测试示例代码
* */
#include
#include
#include
#include
extern int func1(int a);
/**
* CUnit单元测试用例
*/
void test_func1_case1(void){
CU_ASSERT(func1(0) == 1);
}
/**
* CUnit单元测试用例
*/
void test_func1_case2(void){
CU_ASSERT(func1(100) == 2);
}
/**
* CUnit单元测试套件初始化函数
*/
int suit_init(void){
return 0;
}
/**
* CUnit单元测试套件退出函数
*/
int suit_clean(void){
return 0;
}
/**
* 单元测试入口
*/
void utest(void){
CU_ErrorCode ret;
CU_pSuite suit;
CU_pTest test;
/* 注册CUnit单元测试组件*/
ret = CU_initialize_registry();
if (ret != CUE_SUCCESS){
printf("Regist unit test failed!\n");
exit(-1);
}
/* 添加单元测试套件*/
suit = CU_add_suite("Sonar-Test", suit_init, suit_clean);
if (suit == NULL){
printf("Add suit failed!\n");
exit(-1);
}
/* 添加单元测试用例*/
test = CU_add_test(suit, "Test Case1", test_func1_case1);
if (test == NULL){
printf("Add test failed!\n");
exit(-1);
}
/* 添加单元测试用例*/
test = CU_add_test(suit, "Test Case2", test_func1_case2);
if (test == NULL){
printf("Add test failed!\n");
exit(-1);
}
/* 设置单元测试用例结果输出到文件*/
CU_set_output_filename("CUnit-example.xml");
/* 将测试用例输出到文件*/
CU_list_tests_to_file();
/* 运行测试用例*/
CU_automated_run_tests();
return;
}
# SConstruct
# 基于Scons的编译脚本
# 编译Release版本,用于程序正常发布
release = Environment()
robj = release.Object("example-r", "example.c")
rbin = release.Program("example", robj)
# 默认编译Release版本
Default(rbin)
# 编译单元测试版本,用于测试目的。
utest = Environment(LIBS = ['cunit'],
CCFLAGS = ['-fprofile-arcs', '-ftest-coverage','-DUTEST'],
LINKFLAGS = ['-fprofile-arcs', '-ftest-coverage']
)
utobj = utest.Object("example-ut", "example.c")
utobj += utest.Object("example-utest.c")
utbin = utest.Program("example-ut", utobj)
说明:本文使用CUint单元测试的示例代码及scons编译脚本,仅是因为手头正好有一套这样的代码,SonarQube与CUnit和scons无任何关系
开源的SonarQube C和C++插件本身没有代码分析的功能,仅对分析结果进行解析及显示,需要使用cppcheck进行代码分析。另外,需要在项目中设置cppcheck结果的保存位置:
在“项目”->项目名称->“配置”->"(2) Code analysis"->"Cppcheck report(s)"中输出cppcheck检查结果的文件, 如cppcheck.xml, 保存。
在源代码目录运行cppcheck并将结果保存到XML文件中,如下:
[bfx@vFedora sonar-test]$ cppcheck --enable=all --xml-version=2 ./ 2>cppcheck.xml
Checking example-utest.c ...
1/2 files checked 75% done
Checking example.c ...
Checking example.c: UTEST...
2/2 files checked 100% done
[bfx@vFedora sonar-test]$
在代码目录运行在项目创建时复制的sonar-scanner命令,如下:
[bfx@vFedora sonar-test]$ ls
example.c example-utest.c SConstruct
[bfx@vFedora sonar-test]$ sonar-scanner \
> -Dsonar.projectKey=C-Test \
> -Dsonar.sources=. \
> -Dsonar.host.url=http://172.17.7.166:9000 \
> -Dsonar.login=65d0fadd90121b65a8448dd03b7ea6abccb801c2
INFO: Scanner configuration file: /opt/sonar-scanner-4.2.0.1873-linux/conf/sonar-scanner.properties
INFO: Project root configuration file: NONE
# 为了节省显示,省略掉了一些输出
INFO: Load active rules (done) | time=9449ms
WARN: SCM provider autodetection failed. Please use "sonar.scm.provider" to define SCM of your project, or disable the SCM Sensor in the project settings.
INFO: Indexing files...
INFO: Project configuration:
INFO: ------------------------------------------------------------------------
INFO: EXECUTION FAILURE
INFO: ------------------------------------------------------------------------
INFO: Total time: 13.845s
INFO: Final Memory: 5M/20M
INFO: ------------------------------------------------------------------------
ERROR: Error during SonarQube Scanner execution
ERROR: Language of file 'example-utest.c' can not be decided as the file matches patterns of both sonar.lang.patterns.c++ : **/*.cxx,**/*.cpp,**/*.cc,**/*.c,**/*.hxx,**/*.hpp,**/*.hh,**/*.h and sonar.lang.patterns.c : **/*.c,**/*.h
ERROR:
ERROR: Re-run SonarQube Scanner using the -X switch to enable full debug logging.
[bfx@vFedora sonar-test]$
运行到最后会报错,错误原因是不能决定使用sonar.lang.patterns.c++还是使用sonar.lang.patterns.c解析.c文件。这是因为前面安装的sonar-c-plugin和sonar-cxx-plugin插件都有对.c和.h扩展名的设置,因为我们分析的是C语言工程,可以将sonar-cxx-plugin的配置去掉,如下:
去掉后重新运行sonar-scanner命令如下:
[bfx@vFedora sonar-test]$ sonar-scanner \
> -Dsonar.projectKey=C-Test \
> -Dsonar.sources=. \
> -Dsonar.host.url=http://172.17.7.166:9000 \
> -Dsonar.login=65d0fadd90121b65a8448dd03b7ea6abccb801c2
INFO: Scanner configuration file: /opt/sonar-scanner-4.2.0.1873-linux/conf/sonar-scanner.properties
INFO: Project root configuration file: NONE
INFO: SonarQube Scanner 4.2.0.1873
# 为了节省显示,省略掉了一些输出
INFO: Turn debug info on to get more details (sonar-scanner -X -Dsonar.verbose=true ...).
INFO: Analysis total time: 14.121 s
INFO: ------------------------------------------------------------------------
INFO: EXECUTION SUCCESS
INFO: ------------------------------------------------------------------------
INFO: Total time: 16.078s
INFO: Final Memory: 6M/30M
INFO: ------------------------------------------------------------------------
[bfx@vFedora sonar-test]$
运行成功后,可以在SonarQube的"项目"页面查看运行的结果:
SonarQube C语言的代码覆盖率
从上面的运行结果可以看到项目的覆盖率为0%. 其实可以借助lconv和gconvr使SonarQube跟踪代码运行的覆盖率。
要生产覆盖率的数据,需要要编译和链接时进行如下设置:
编译时:增加-fprofile-arcs -ftest-coverage或–coverage,
链接时:增加 -fprofile-arcs 或者 –lgcov。
打开–g3 选项,去掉-O2以上级别的代码优化选项;否则编译器会对代码做一些优化,例如行合并,从而影响行覆盖率结果;
如在我们前面的示例代码的编译:
[bfx@vFedora sonar-test]$ gcc -o example-ut -fprofile-arcs -ftest-coverage -DUTEST -g3 -lcunit example.c example-utest.c
example.c: 在函数‘func2’中:
example.c:30:12: 警告:returning ‘char **’ from a function with incompatible return type ‘char *’ [-Wincompatible-pointer-types]
30 | return name;
| ^~~~
example.c:30:12: 警告:函数返回局部变量的地址 [-Wreturn-local-addr]
[bfx@vFedora sonar-test]$ ls -lh
总用量 112K
-rw-rw-r-- 1 bfx bfx 1.4K 12月 9 20:43 cppcheck.xml
-rw-rw-r-- 1 bfx bfx 512 12月 9 20:13 example.c
-rw-rw-r-- 1 bfx bfx 952 12月 10 10:52 example.gcno
-rwxrwxr-x 1 bfx bfx 88K 12月 10 10:52 example-ut
-rw-rw-r-- 1 bfx bfx 1.6K 12月 9 20:20 example-utest.c
-rw-rw-r-- 1 bfx bfx 2.9K 12月 10 10:52 example-utest.gcno
-rw-rw-r-- 1 bfx bfx 496 12月 9 20:24 SConstruct
[bfx@vFedora sonar-test]$
可以看到编译生成的可执行文件及用户生成覆盖率数据的*.gcno文件。
运行./example-ut 测试程序,运行结束后,会针对所有的源代码文件产生相应的*.gcda文件
[bfx@vFedora sonar-test]$ ./example-ut
Hello
[bfx@vFedora sonar-test]$ ls -lh
总用量 128K
-rw-rw-r-- 1 bfx bfx 1.4K 12月 9 20:43 cppcheck.xml
-rw-rw-r-- 1 bfx bfx 1.8K 12月 10 10:57 CUnit-example.xml-Listing.xml
-rw-rw-r-- 1 bfx bfx 1.8K 12月 10 10:57 CUnit-example.xml-Results.xml
-rw-rw-r-- 1 bfx bfx 512 12月 9 20:13 example.c
-rw-rw-r-- 1 bfx bfx 164 12月 10 10:57 example.gcda
-rw-rw-r-- 1 bfx bfx 952 12月 10 10:52 example.gcno
-rwxrwxr-x 1 bfx bfx 88K 12月 10 10:52 example-ut
-rw-rw-r-- 1 bfx bfx 1.6K 12月 9 20:20 example-utest.c
-rw-rw-r-- 1 bfx bfx 364 12月 10 10:57 example-utest.gcda
-rw-rw-r-- 1 bfx bfx 2.9K 12月 10 10:52 example-utest.gcno
-rw-rw-r-- 1 bfx bfx 496 12月 9 20:24 SConstruct
[bfx@vFedora sonar-test]$
使用gconvr生成XML格式的覆盖率报告,开源SonarQube C语言插件sonar-c-plugin需要使用XML格式的覆盖率报告,这可能与付费版本的不同。
sonar-c-plugin关于覆盖率跟踪的WIKI描述如下,链接:https://github.com/SonarOpenCommunity/sonar-cxx/wiki/Coverage-tracers
SonarQube官方CFamily的设置如下,链接
https://docs.sonarqube.org/latest/analysis/coverage/
生成覆盖率报告:
[bfx@vFedora sonar-test]$ gcovr -r ./ -x --object-directory=$(pwd) > report.xml
[bfx@vFedora sonar-test]$ ls -lh
总用量 132K
-rw-rw-r-- 1 bfx bfx 1.4K 12月 9 20:43 cppcheck.xml
-rw-rw-r-- 1 bfx bfx 1.8K 12月 10 10:57 CUnit-example.xml-Listing.xml
-rw-rw-r-- 1 bfx bfx 1.8K 12月 10 10:57 CUnit-example.xml-Results.xml
-rw-rw-r-- 1 bfx bfx 512 12月 9 20:13 example.c
-rw-rw-r-- 1 bfx bfx 164 12月 10 10:57 example.gcda
-rw-rw-r-- 1 bfx bfx 952 12月 10 10:52 example.gcno
-rwxrwxr-x 1 bfx bfx 88K 12月 10 10:52 example-ut
-rw-rw-r-- 1 bfx bfx 1.6K 12月 9 20:20 example-utest.c
-rw-rw-r-- 1 bfx bfx 364 12月 10 10:57 example-utest.gcda
-rw-rw-r-- 1 bfx bfx 2.9K 12月 10 10:52 example-utest.gcno
-rw-rw-r-- 1 bfx bfx 3.1K 12月 10 11:35 report.xml
-rw-rw-r-- 1 bfx bfx 496 12月 9 20:24 SConstruct
[bfx@vFedora sonar-test]$
设置SonarQube读取覆盖率报告:在"项目"->项目名称->"配置"->"C (Community)"->"(3) Testing & Coverage"->"Unit test coverage report(s)"中输入覆盖率报告的路径:
运行sonar-scanner工具进行代码分析
[bfx@vFedora sonar-test]$ sonar-scanner \
> -Dsonar.projectKey=C-Test \
> -Dsonar.sources=. \
> -Dsonar.host.url=http://172.17.7.166:9000 \
> -Dsonar.login=65d0fadd90121b65a8448dd03b7ea6abccb801c2
INFO: Scanner configuration file: /opt/sonar-scanner-4.2.0.1873-linux/conf/sonar-scanner.properties
INFO: Project root configuration file: NONE
# 为了节省显示,省略了部分输出
INFO: Turn debug info on to get more details (sonar-scanner -X -Dsonar.verbose=true ...).
INFO: Analysis total time: 14.160 s
INFO: ------------------------------------------------------------------------
INFO: EXECUTION SUCCESS
INFO: ------------------------------------------------------------------------
INFO: Total time: 16.597s
INFO: Final Memory: 6M/30M
INFO: ------------------------------------------------------------------------
[bfx@vFedora sonar-test]$
在SonarQube项目页面可以看到项目的覆盖率信息,也可以看到每行代码的覆盖率情况: