sonarQube-iOS-Objective-C客户端搭建手册

sonarQube分为服务端和客户端。服务端相当于一个webService,将客户端代码扫描结果通过web方式进行展示,可以安装在代码本地,也可以安装在另外一台机器。客户端也叫sonar-scanner,用于收集扫描代码结果并上传到服务端,需要安装在代码端。
sonarQube针对OC有一个官方的插件,但是收费的,为了省钱,下面介绍开源的插件。

插件地址:https://github.com/Backelite/sonar-swift

服务端插件安装

插件是运行在sonarQube服务端的,下载https://github.com/Backelite/sonar-swift/releases最新版本(目前为0.4.5),插件文件是个jar包,需要拷贝到sonarQube服务端插件安装目录(/sonarqube/extensions/plugins),然后重启sonarQube服务端,重启后登陆,进入sonarQube->Administration->Marketplace,查看插件列表中是否有Swift (Backelite),如果有,则表示安装成功

代码端本地配置

本地配置工作比较多,且遇到的坑也特别多,下面一一介绍

该插件虽然支持对Objective-CSwift的统计,由于我的项目中没有Swift代码,所以涉及Swift的地方基本一笔带过了

sonar-scanner

sonar-scannersonarQube的客户端,用于将各个命令执行生成的结果上传到服务端

官方参考:https://docs.sonarQube.org/latest/analysis/scan/sonarscanner/

安装完后,要把bin目录加到环境变量中

run-sonar-swift

run-sonar-swift.sh是替代sonar-scanner命令的一个shell脚本,在执行sonar-scanner的时候,使用run-sonar-swift.sh即可

脚本基本原理:

1. 读取命令参数和`sonar-project.properties`的设置
2. 生成compile_commands.json
3. 生成coverage的xml报告
4. 执行SwiftLint和Tailor检查Swift,我这里没有Swift,直接没装这两个工具
5. 执行oclint
6. 执行Lizard
7. 执行sonar-scanner

安装方式:

clone代码到本地https://github.com/Backelite/sonar-swift

然后拷贝sonar-swift/sonar-swift-plugin/src/main/shell/run-sonar-swift.shsonar-scanner/.../bin/目录下,

xcpretty

xcodebuild的输出进行格式化的工具,生成报告,增加可读性

直接安装最新版本,Backelite中说的问题已在最新版中改正
官方参考:https://github.com/xcpretty/xcpretty

SwiftLint

Swift语言的静态检测工具,目前iOS全部为Objective-C语言,没有swift,所以暂时不用安装

Tailor

Swift语言的静态检测工具,目前iOS全部为Objective-C语言,没有swift,所以暂时不用安装

slather

语言:ruby

用于将xcode生成的coverage报告转换成xml格式,核心命令是使用llvm-cov命令

安装方式:gem install slather

官网:https://github.com/SlatherOrg/slather

此处有坑:

坑一:static library在使用llvm-cov时会出错,错误信息如下

Failed to load coverage: Malformed coverage data
error: Could not load coverage information
Traceback (most recent call last):
    16: from /usr/local/Cellar/ruby/2.6.1/bin/slather:23:in `<main>'
    15: from /usr/local/Cellar/ruby/2.6.1/bin/slather:23:in `load'
    14: from /usr/local/Cellar/ruby/2.6.1/lib/ruby/gems/2.6.0/gems/slather-2.4.7/bin/slather:17:in `<top (required)>'
    13: from /usr/local/Cellar/ruby/2.6.1/lib/ruby/gems/2.6.0/gems/clamp-1.3.1/lib/clamp/command.rb:140:in `run'
    12: from /usr/local/Cellar/ruby/2.6.1/lib/ruby/gems/2.6.0/gems/clamp-1.3.1/lib/clamp/command.rb:66:in `run'
    11: from /usr/local/Cellar/ruby/2.6.1/lib/ruby/gems/2.6.0/gems/clamp-1.3.1/lib/clamp/subcommand/execution.rb:18:in `execute'
    10: from /usr/local/Cellar/ruby/2.6.1/lib/ruby/gems/2.6.0/gems/clamp-1.3.1/lib/clamp/command.rb:66:in `run'
     9: from /usr/local/Cellar/ruby/2.6.1/lib/ruby/gems/2.6.0/gems/slather-2.4.7/lib/slather/command/coverage_command.rb:59:in `execute'
     8: from /usr/local/Cellar/ruby/2.6.1/lib/ruby/gems/2.6.0/gems/slather-2.4.7/lib/slather/command/coverage_command.rb:97:in `post'
     7: from /usr/local/Cellar/ruby/2.6.1/lib/ruby/gems/2.6.0/gems/slather-2.4.7/lib/slather/coverage_service/cobertura_xml_output.rb:18:in `post'
     6: from /usr/local/Cellar/ruby/2.6.1/lib/ruby/gems/2.6.0/gems/slather-2.4.7/lib/slather/project.rb:98:in `coverage_files'
     5: from /usr/local/Cellar/ruby/2.6.1/lib/ruby/gems/2.6.0/gems/slather-2.4.7/lib/slather/project.rb:123:in `profdata_coverage_files'
     4: from /usr/local/Cellar/ruby/2.6.1/lib/ruby/gems/2.6.0/gems/slather-2.4.7/lib/slather/project.rb:123:in `each'
     3: from /usr/local/Cellar/ruby/2.6.1/lib/ruby/gems/2.6.0/gems/slather-2.4.7/lib/slather/project.rb:124:in `block in profdata_coverage_files'
     2: from /usr/local/Cellar/ruby/2.6.1/lib/ruby/gems/2.6.0/gems/slather-2.4.7/lib/slather/project.rb:135:in `pathnames_per_binary'
     1: from /usr/local/Cellar/ruby/2.6.1/lib/ruby/2.6.0/json/common.rb:156:in `parse'
/usr/local/Cellar/ruby/2.6.1/lib/ruby/2.6.0/json/common.rb:156:in `parse': 767: unexpected token at '' (JSON::ParserError)

原因:
slather使用llvm-cov命令将coverage转换为xml,但llvm-cov貌似对静态库不支持,命令执行会出错

解决方案:

  • 修改run-sonar-swift.sh脚本,添加MACH_O_TYPE=mh_dylibxcodebuild test命令中,生成结果为动态库形式,这样就可以正常生成xml
buildCmd+=( -scheme "$appScheme" -configuration "$appConfiguration" -enableCodeCoverage YES MACH_O_TYPE=mh_dylib)

坑二:slather生成的coverage-swift.xml中如果存在line number="0"的情况,在调用sonar-scanner上传的时候会报错,如下

//coverage-swift.xml中存在number="0"的情况

<class name="uSDKConst.h" filename="Modules/uSDKCommon/uSDKCommon/uSDKConst.h" line-rate="0.7586206896551724" branch-rate="1.0000000000000000" complexity="0.0">
          <methods/>
          <lines>
            <line number="0" branch="false" hits="1910"/>
            <line number="0" branch="false" hits="1500"/>

//在sonar-scanner上传时会出错
ERROR: Error during SonarQube Scanner execution
java.lang.IllegalStateException: Line number must be strictly positive: 0
    at org.sonar.api.internal.google.common.base.Preconditions.checkState(Preconditions.java:197)
    at org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage.validateLine(DefaultCoverage.java:94)
    at org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage.lineHits(DefaultCoverage.java:81)
    at com.backelite.sonarqube.swift.coverage.CoberturaReportParser.collectFileData(CoberturaReportParser.java:109)
    at com.backelite.sonarqube.swift.coverage.CoberturaReportParser.collectClassMeasures(CoberturaReportParser.java:91)
    at com.backelite.sonarqube.swift.coverage.CoberturaReportParser.collectPackageMeasures(CoberturaReportParser.java:79)
    at com.backelite.sonarqube.swift.coverage.CoberturaReportParser.parseReport(CoberturaReportParser.java:61)
    at com.backelite.sonarqube.swift.coverage.CoberturaSensor.execute(CoberturaSensor.java:69)
    at org.sonar.scanner.sensor.AbstractSensorWrapper.analyse(AbstractSensorWrapper.java:48)
    at org.sonar.scanner.sensor.ModuleSensorsExecutor.execute(ModuleSensorsExecutor.java:85)
    at org.sonar.scanner.sensor.ModuleSensorsExecutor.lambda$execute$1(ModuleSensorsExecutor.java:59)
    at org.sonar.scanner.sensor.ModuleSensorsExecutor.withModuleStrategy(ModuleSensorsExecutor.java:77)
    at org.sonar.scanner.sensor.ModuleSensorsExecutor.execute(ModuleSensorsExecutor.java:59)
    at org.sonar.scanner.scan.ModuleScanContainer.doAfterStart(ModuleScanContainer.java:82)
    at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:136)
    at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:122)
    at org.sonar.scanner.scan.ProjectScanContainer.scan(ProjectScanContainer.java:400)
    at org.sonar.scanner.scan.ProjectScanContainer.scanRecursively(ProjectScanContainer.java:395)
    at org.sonar.scanner.scan.ProjectScanContainer.doAfterStart(ProjectScanContainer.java:358)
    at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:136)
    at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:122)
    at org.sonar.scanner.bootstrap.GlobalContainer.doAfterStart(GlobalContainer.java:141)
    at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:136)
    at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:122)
    at org.sonar.batch.bootstrapper.Batch.doExecute(Batch.java:73)
    at org.sonar.batch.bootstrapper.Batch.execute(Batch.java:67)
    at org.sonarsource.scanner.api.internal.batch.BatchIsolatedLauncher.execute(BatchIsolatedLauncher.java:46)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at org.sonarsource.scanner.api.internal.IsolatedLauncherProxy.invoke(IsolatedLauncherProxy.java:60)
    at com.sun.proxy.$Proxy0.execute(Unknown Source)
    at org.sonarsource.scanner.api.EmbeddedScanner.doExecute(EmbeddedScanner.java:189)
    at org.sonarsource.scanner.api.EmbeddedScanner.execute(EmbeddedScanner.java:138)
    at org.sonarsource.scanner.cli.Main.execute(Main.java:112)
    at org.sonarsource.scanner.cli.Main.execute(Main.java:75)
    at org.sonarsource.scanner.cli.Main.main(Main.java:61)

原因:
coverage-swift.xml中存在number="0"的情况

解决方案:

  • 在工程根目录下新增.slather.yml文件,采用配置文件方式执行slather命令,注意yml文件的格式
  • .slather.yml中配置ignore,将coverage-swift.xmlline number="0"的文件添加到ignore中,对ruby实在不懂,目前没找到直接忽略目录的方式,所以采用单个文件添加
# .slather.yml
coverage_service: cobertura_xml
xcodeproj: project_path.xcodeproj
scheme: YourXcodeSchemeName
source_directory: source_dir
output_directory: sonar-reports
input-format: profdata
ignore:
    - number_0_file_1.h
    - number_0_file_2.h
    - number_0_file_3.h
  • run-sonar-swift.sh中的slather命令可改可不改,我是没有改,因为只用.slather.yml中的ignore字段,跟原命令不冲突

待解决 .slather.yml如何精准配置source和ignore,目前不能配置ignore的目录方式,且生成的xml中依然有tests.m文件

lizard

语言:python

复杂度分析工具,安装方式:sudo pip install lizard

官网:https://github.com/terryyin/lizard

此处有坑:

坑一:脚本中的lizard命令不支持sonar.sources为多路径的情况

解决方案:

//修改脚本中lizard命令
paths=`tr ',' ' ' <<< "${srcDirs}"`
$LIZARD_CMD --xml -l objectivec $paths > sonar-reports/lizard-report.xml

OCLint

OCLintObjective-C语言的静态检测工具, 目前使用homebrew所能安装的最新版本是0.13,但不适用于最新的xcode11(可能xcode10都不支持,各种报错), 需要自己源码编译安装最新的0.15版本,下面主要介绍源码编译的方法,编译过程可以在自己本机即可。

编译&安装方法:

  1. https://github.com/oclint/oclint/releases下载0.15版本的源码

  2. http://releases.llvm.org/download.html#9.0.0下载已编译的llvm9.0

注意:

  • 这里安装llvm9.0是因为oclint0.15 release中说明了对llvm版本的要求
  • 下载直接编译好的llvm是因为如果采用oclint官网推荐的./make安装方式,会下载llvm源码并编译,其过程非常非常漫长也容易失败,不可取。。。
  1. 编译
1. cd到oclint-scripts目录下
cd oclint-0.15/oclint-scripts

2. 编译,注意参数为llvm的绝对路径
./makeWithSystemLLVM /absolute/llvm/path/clang+llvm-9.0.0-x86_64-darwin-apple/

3. 编译完成后会在oclint-0.15目录下生成一个build/oclint-release目录,即为编译完成的oclint
  1. 安装
1. cd 到oclint-release目录下
cd oclint-0.15/build/oclint-release

2. 拷贝oclint到代码端的系统路径
cp bin/oclint* /usr/local/bin/
cp -rp lib/* /usr/local/lib/
cp -rp include/* /usr/local/include/

sonar-project.properties

sonar-project.propertiessonarQube在客户端的配置文件,一般放置于工程根目录

sonar-project.properties在工程的根目录,sonar-scanner的安装目录和服务器上都可以配置,优先级是根目录 > sonaer-scanner安装目录 > 服务器,即在自己的工程根目录的配置就会覆盖其他地方的配置

#scm的问题见文章最下方
sonar.scm.disabled=true
sonar.host.url=http://xxx:9000

sonar.projectKey=iOS
sonar.projectName=iOSProjectName
sonar.projectVersion=1.0
sonar.language=objc
sonar.projectDescription=projectDescription
sonar.sourceEncoding=UTF-8

#源文件目录,这里采用精确到具体目录的方式,为了不配置inclusions和exclusions,因为在使用的过程发现,如果oclint或lizard产生的结果集比sonar.sources配置的大,上传时容易出错,所以最好精确的指定sonar.sources
sonar.sources=relative/dir1,relative/dir2,relative/dir3

#要包含的文件,注意不是目录
sonar.inclusions=./**/*.h,./**/*.m
sonar.exclusions=./exclude/**/*,./**/*Tests*

#下面这些配置是run-sonar-swift.sh中定义的
sonar.swift.simulator=platform=iOS Simulator,name=iPhone 11
sonar.swift.appScheme=appScheme
sonar.swift.project=your_project.xcodeproj

运行

cd到工程根目录,即sonar-project.properties所在位置,执行run-sonar-swift就可以了

SCM导致的问题通常表现如下:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容

  • 前言 团队开发中,代码质量的把关,往往决定了一个团队的开发维护效率。成员的增长,业务的扩大,不同风格、不严谨的代码...
    生光阅读 5,779评论 1 3
  • 本文重点说明sonarqube的使用和搭建,以及集成到Jenkins,从因到果进行详细的说明。gitLab+Jen...
    GeekSpring阅读 25,553评论 1 7
  • 前序步骤:一、《MAC Jenkins安装》二、《iOS持续构建-编译打包上传》三、《iOS+Jenkins持续构...
    圣艾修阅读 6,081评论 4 6
  • 一、前言 年初的时候部门各组都给出了自己的规范文档,包括部门工作规范、各语言开发规范、测试规范、数据库规范、安全规...
    dancingking阅读 15,138评论 8 20
  • 我从不相信这个世界是美好的,但我依然会用善心观看这个世界;因为我相信,在这个并不美好的世界上,如果再缺少了善心的关...
    杨嘉利阅读 183评论 0 0