iOS大工程接入SonarQube之静态分析

2,456 阅读5分钟

前言

sonar文档:docs.sonarqube.org/latest/arch…

这篇文章主要讲的是iOS大工程接入SonarQube平台(主要是静态分析,依赖OCLint)及持续集成的大致流程和遇到的坑。sonar平台的搭建在此不再赘述,大家可以网上搜索下。(最新的版本,有jdk,mysql的前提下,下载直接运行脚本就可以把服务启动起来,很方便)

总览及准备工作

先放两张图,通过这两张图大家基本可以对SonarQube及iOS工程接入有个大概了解

官方介绍流程

iOS接入流程

准备工作

我们需要的xcodebuild,xcpretty,oclint,sonar-project.properties,sonar-scanner,oc的非官方插件

  • xcpretty [增加xcodebuild输出的可读性]:github.com/xcpretty/xc… ,gem install xcpretty直接安装
  • oclint [静态分析工具]:github.com/oclint/ocli… 直接下载可执行文件最方便
  • sonar-project.properties:sonar-scanner扫描必要的配置文件。(下面提供示例)
  • sonar-scannerdocs.sonarqube.org/latest/anal…
  • 插件 github.com/Backelite/s… 使用mvn编译完生成jar,放到sonarQube对应的插件目录下。 这个项目还提供了一个run-sonar.sh的脚本,不需要打xcodebuildoclint这些命令,都集成到这个脚本里,一键完成上传。这个项目的sonar-objc版本已不再维护。

以下提供几个示例或命令:

xcodebuild+xcpretty: xcodebuild -workspace xxx -project xxx -scheme xxx -configure Debug | tee xcodebuild.log | xcpretty -r json-compilation-database -o compile_commands.json

oclint: oclint-json-compilation-database -- -report-type pmd -o oclint.xml -e Pods -max-priority-1 100000 -max-priority-2 100000 -max-priority-3 100000

sonar-project.properties:

#sonarServer地址 (这两行sonarServer管理员会提供)
sonar.host.url=http://sonar.xxx.com:9000
sonar.login=adasdadadadadadad

# Sonar项目标识,在 SonarQube实例下必须唯一
sonar.projectKey=ObjcTest2

# 在 SonarQube UI 中显示的项目名称
sonar.projectName=ObjcDoc2

# 项目版本
sonar.projectVersion=1.0
 
# 项目代码与 sonar-project.properties 文件的相对路径
sonar.sources=SonarTestProject
 
# 代码文件的编码
sonar.sourceEncoding=UTF-8

# 排除不参与代码分析的文件或目录
sonar.exclusions=node_modules/**/*,.idea/**/*

sonar.language=objc
sonar.objectivec.workspace=SonarTestProject.xcworkspace
sonar.objectivec.project=SonarTestProject.xcodeproj
sonar.objectivec.appScheme=SonarTestProject
sonar.objectivec.simulator=platform=iOS Simulator,name=iPhone 8,OS=latest

#这里需要注意,特别是oc,cpp混编的工程,下面一章节会讲到
sonar.cxx.suffixes.headers=-
sonar.c.suffixes.headers=-
sonar.c.suffixes.sources=-

#这步针对sonar-scanner 4.2没啥作用,下一章会讲到
#sonar.objectivec.oclint.report=oclint.xml

sonar-scanner 在工程目录下,直接运行sonar-scanner即可(记得增加环境变量)

一步步走下来,开始我们升级打怪阶段。

遇到的问题

** 1. sonarQube有对应的项目,但是显示 the main branch of this project is empty**

错误1

这里需要安装插件,原本我以为插件是对sonarQube的扩展,装不装插件和功能丰富度有关,但是针对oc项目静态分析必须安装插件。官方有收费的插件,比较贵。所以我们用开源的插件。而且sonarQube社区版是不支持oc的静态分析的。

sonarQube说明

** 2.sonar-scanner执行成功,oclint.xml也包含对应的错误,sonarQube显示全是0或者数量有误** 2.1 sonar-scanner扫描的时候回去当前工程下的sonar-reports文档中扫描,需要创建文件夹并复制进去,错误信息才能被真正上传。(参考的文章中说道在sonar-project.properties中配置sonar.objectivec.oclint.report=oclint.xml,试了几次没效果。sonar-scanner 4.2;后面是了插件提供的run-sonar.sh脚本成功) 2.2 oclint.xml中的错误不包含在oclint规则库中。默认规则库可以在sonarQube平台上看到或者在oclint规则文档中看

sonarQube-oclint规则
2.3 oclint.xml中有大量的compiler error,这个规则没有在规则库中,认为这个在开发的过程中编译错误就应该被解决掉,所以没加到苦衷。个人觉得这个信息特别有用,特别是一些类型错误,这些不算编译错误,不一定会产生错误,但是隐患非常大。可以自己写规则来进行展示。

** 3.sonar-scanner扫描的时候可能遇到 Language of file can’t be decided (default language patterns sonar.lang.patterns.c and sonar.lang.patterns.c++)**

文件类型配置

混编的工程中,可能有.c,.h后面谁的文件,在同一个工程中,无法被认定为两种以上类型的语言。所以在上一节的文章中提到的,sonar-project.properties中c,c++的文件设置置为空。

sonar.cxx.suffixes.headers=-
sonar.c.suffixes.headers=-
sonar.c.suffixes.sources=-

参考:community.sonarsource.com/t/language-…

** 4. oclint argument list too long** 在大工程,文件特别多,产生的compiler_commands.json文件也会特别大,导致oclint报错。解决方案:github.com/wuwen1030/o… 原理:compiler_commands.json分片成compiler_commands01.jsoncompiler_commands02.jsoncompiler_commands03.json... 分别进行oclint处理(这里有一步特别重要,需要将处理的compiler_commands%d.json重命名为compiler_commands,因为oclint只认compiler_commands,相当于这个参数oclint是写死的)生成oclint01.xml,oclint02.xml,oclint03.xml...,最后把以上xml合并为最终的oclint.xml

** 5.分析时间过长** 我们的工程有4个G,产生的compile_commands会有70M,oclint分析的时间会达到一个小时左右。CI过程会非常频繁,接入集群的Mac机器非常少,资源有限,1个小时的处理时间肯定是不允许的。我们的主要处理方案是:对compile_commands.json进行瘦身。 5.1. xcodebuild阶段:配置和-destination,不然会处理多个多个架构,导致compile_commands翻倍。 **5.2. 有针对性的静态分析:**既然sonar-scanner可以指定文件夹上传,是不是可以指定文件夹进行oclint,这样在集成到CI流程中也有个非常好的对应。既然compile_commands.jsonjson文件,那我们就对compile_commands.json的数据进行删减,只匹配我们需要的文件。

[{
  "directory":"""command":"/Application/xxxxx/clang -x objective-c -target ... ",可以看到主要是clang干了活
  "file":""
},
...]

直接对file或者directory进行处理就好了,需要的进行保留。最后oclint的时间大大缩减,指定了某个模块的代码,时间从原来的一小时锐减到几分钟。

TODO

  1. 自定义规则,需要自己写oclint的规则。
  2. compiler error,这个规则没有在规则库中,认为这个在开发的过程中编译错误就应该被解决掉,所以没加到库中。个人觉得这个信息特别有用,特别是一些类型错误,这些不算编译错误,不一定会产生错误,但是隐患非常大。希望能在项目中体现。
  3. 增量分析,针对我们的大规模工程,增量分析减少分析时间是很有必要的。可以采用5.2的方式,但是这样不够完美。因为这样会覆盖之前的分析结果。考虑到的方案是,先全局静态分析拿到基准oclint.xml. 然后增量分析采用5.2方式,最后替换掉oclint.xml中对应文件的分析结果。