sonar + ocLint + swiftLint 实践

2,693 阅读8分钟

sonar + ocLint + swiftLint 实践

在前面文章 sonar android上的实践Kotlin 静态代码扫描和IDE实时提醒的工具实践文章上有详细介绍android上怎么结合sonar和IDE进行静态代码扫描的调研,不过都是检查android项目,语言也是java和kotlin的,没有针对oc和swift的静态代码扫描,但是最近公司有在整理这一套,想在sonar上进行对oc和swift语言进行静态代码扫描,本人主要介绍怎么结合soanr进行静态代码扫描

技术方案选型

sonar本身其实是对oc和swift这两种语言支持的,但是不是免费,需要收费,即使付费使用他们的插件也不能自定义自己的检查规则,这肯定不能满足我们现在的需求,所以只能调用有没有别的方案,在调研方案之前,我们首先要了解sonar整体运作流程: sonar整体架构:

image.png

从上图可以看出SonarQube构架包含以下四部分:

  • Project

    对应Project其实就是我们需要分析的源码,比如本人需要分析的ios工程源码

  • SonarQube Scanner

    SonarQube Scanner 是用于执行代码分析的功能,在Project的根目录下执行,指定了工程的相关信息,还指定了SonarQube Server的地址,SonarQube Scanner分析完毕之后,会将结果上报到该Server

  • SonarQube Server

    显示分析结果的Web Server,在SonarQube Scanner第一次将一个工程的分析结果上报给SonarQube Server后,Server上会自动创建一个工程显示分析的结果,可以在Server上设置代码质量管理相关的各种配置,如设置代码检查规则(Rule)和质量门限(Quality Gate)等。

  • SonarQube Database

    SonarQube数据存储的地方,作为数据源供SonarQube Server使用,支持常见的数据库

从上图,我们还可以看到,在SonarQube Scanner上有一个很重要的一部分叫SonarQube Plugins,这里是提供给开发者自己去扩展自己的插件,比如自定义插件语言,插件规则,插件规则代码扫描等功能,当实现自己的插件包的时候,进行代码扫描的时候,会统一通知各个插件,插件可以进行自己的代码扫描,或者直接读取别的扫描结果直接上报到SonarQube Server进行展示,例如之前写的一个将android lint 生成的报告进行读取上报到SonarQube Server服务器上展示就是基于这个原理,插件地址,需要可以参考一下

技术方案制定

基于以上SonarQube工作原理,如果我们想实现对oc和swift语言进行代码扫描的话,那么可以通过下面流程进行扫描:

sonar+oclint+swiftlint流程图.png

从上面看,要实现对oc和swift代码的扫描,需要做两件事情:

  1. 使用开源工具对oc和swift代码进行扫描和报告生成

  2. 编写sonar plugin插件读报告读写和上报

针对第一个问题,目前有比较成熟的有ocLint和swiftLint了,并且都能自定义规则,第二点其实跟之前写的读取android lint 扫描结果一样,编写插件就行,本来想自己去实现这两个,但是在github上看到一个别人已经写好的库,下面主要是对这个库使用过程遇到的一些问题进行总结

sonar-objective-c 这个库主要是通过编写好shell脚本,运行OCLint生成报告,再运行sonar Scanner读取报告上传,里面也有内置对oc语法树分析,不过内置规则很少,而且很久没有更新了,只适配都sonar 4.x & 5.x版本 sonar-swift 这个库是基于sonar-objective-c添加了swiftLint的支持,同时兼容到sonar最新版本8.x了,每个版本都有,功能全面,强大

sonar-swift实践

基于对oc和swift代码扫描方案探讨,最终选用sonar-swift方案,下面对sonar-swift实践过程遇到的一些问题说明,本人在mac系统上进行相关配置

  • 环境准备
  1. SonarQube服务安装,这个是基础,可以自行google安装过程
  2. 下载sonar-swift sonar plugin插件,两种方式:1.clone sonar-swift github上的项目运行生成插件 2:在sonar-swift官网下载作者发布的最新插件 地址

image.png 下载完插件后,放到自己搭建的sonar 服务器plugin目录,重启sonar服务就行,这个时候就看到已经加载了插件里面的规则了 OC规则 image.png Swift规则

image.png 3.安装OCLint ,xcpretty和 SwiftLint,因为执行项目代码扫描需要执行这两个命令进行扫描生成报告,当然如果用到Faux PasTailor等其他代码扫描工具的话,自行安装,具体安装方法sonar-swift上有介绍

image.png 4.复制sonar-project.properties到项目根目录下,然后编辑

#
# Swift SonarQube Plugin - Enables analysis of Swift and Objective-C projects into SonarQube.
# Copyright © 2015 Backelite (${email})
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#对应sonar上生产的项目信息,自己命名
sonar.projectKey=joyy_mid_sdk_test
sonar.projectName=joyy_mid_sdk_test
# Number version (can be found automatically in plist, just comment this line)
sonar.projectVersion=1.0

# Comment if you have a project with mixed ObjC / Swift
sonar.language=swift

# Project description
sonar.projectDescription=prjDescription

# Path to source directories
sonar.sources=athliveroom
# Path to test directories (comment if no test)
#sonar.tests=TestDir

# Destination Simulator to run surefire
# As string expected in destination argument of xcodebuild command
# Example = sonar.swift.simulator=platform=iOS Simulator,name=iPhone 6,OS=9.2
sonar.swift.simulator=platform=iOS Simulator,name=iPhone 11,OS=14.2

# Xcode project configuration (.xcodeproj)
# and use the later to specify which project(s) to include in the analysis (comma separated list)
# Specify either xcodeproj or xcodeproj + xcworkspace
sonar.swift.project=athliveroom.xcodeproj
sonar.swift.workspace=athliveroom.xcworkspace

# Scheme to build your application
sonar.swift.appScheme=athliveroom

# Specify your appname when different from targeted scheme.
# Or when slather fails with 'No product binary found'
# You can also provide a list of framework names to analyse for coverage.
# This will be something like "myApp" or "myApp,myFramework"
sonar.coverage.binaryNames=athliveroom

# Configuration to use for your scheme. if you do not specify that the default will be Debug
#sonar.swift.appConfiguration=MyConfiguration

##########################
# Optional configuration #
##########################
# Encoding of the source code
sonar.sourceEncoding=UTF-8
# SCM
# sonar.scm.enabled=true
# sonar.scm.url=scm:git:http://xxx

# JUnit report generated by run-sonar.sh is stored in sonar-reports/TEST-report.xml
# Change it only if you generate the file on your own
# The XML files have to be prefixed by TEST- otherwise they are not processed
# sonar.junit.reportsPath=sonar-reports/

# Lizard report generated by run-sonar.sh is stored in sonar-reports/lizard-report.xml
# Change it only if you generate the file on your own
# sonar.swift.lizard.report=sonar-reports/lizard-report.xml

# Cobertura report generated by run-sonar.sh is stored in sonar-reports/coverage-swift.xml
# Change it only if you generate the file on your own
# sonar.swift.coverage.reportPattern=sonar-reports/coverage-swift*.xml

# OCLint report generated by run-sonar.sh is stored in sonar-reports/oclint.xml
# Change it only if you generate the file on your own
# sonar.swift.swiftlint.report=sonar-reports/*swiftlint.txt

# Change it only if you generate the file on your own
# sonar.swift.tailor.report=sonar-reports/*tailor.txt

# Paths to exclude from coverage report (surefire, 3rd party libraries etc.)
# sonar.swift.excludedPathsFromCoverage=pattern1,pattern2
sonar.swift.excludedPathsFromCoverage=.*Tests.*

# Ability to skip tests (such as UI Tests running long time)
# Example = sonar.swift.skipTests=UITests

##########################
# Tailor configuration #
##########################
# Tailor configuration
# -l,--max-line-length=<0-999>                  maximum Line length (in characters)
#    --list-files                               display Swift source files to be analyzed
#    --max-class-length=<0-999>                 maximum Class length (in lines)
#    --max-closure-length=<0-999>               maximum Closure length (in lines)
#    --max-file-length=<0-999>                  maximum File length (in lines)
#    --max-function-length=<0-999>              maximum Function length (in lines)
#    --max-name-length=<0-999>                  maximum Identifier name length (in characters)
#    --max-severity=<error|warning (default)>   maximum severity
#    --max-struct-length=<0-999>                maximum Struct length (in lines)
#    --min-name-length=<1-999>                  minimum Identifier name length (in characters)
sonar.swift.tailor.config=--no-color --max-line-length=100 --max-file-length=500 --max-name-length=40 --max-name-length=40 --min-name-length=4

这部分没有什么好修改的,安装官网的样本修改一下成自己的就行 5.复制run-sonar-swift.sh到项目跟目录,这个脚本是执行扫描的关键所在,里面会执行oclint,swiftlint,sonarscanner等命令

#!/bin/bash
#
# backelite-sonar-swift-plugin - Enables analysis of Swift and Objective-C projects into SonarQube.
# Copyright © 2015 Backelite (${email})
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

## INSTALLATION: Copy this script somewhere in your PATH
## USAGE: ./run-sonar-swift.sh
## DEBUG: ./run-sonar-swift.sh -v
## WARNING: edit your project parameters in sonar-project.properties rather than modifying this script
#

# Global parameters
SLATHER_CMD=slather
SWIFTLINT_CMD=swiftlint
TAILOR_CMD=tailor
XCPRETTY_CMD=xcpretty
LIZARD_CMD=lizard
XCODEBUILD_CMD=xcodebuild


trap "echo 'Script interrupted by Ctrl+C'; stopProgress; exit 1" SIGHUP SIGINT SIGTERM

function startProgress() {
	while true
	do
    	echo -n "."
	    sleep 5
	done
}

function stopProgress() {
	if [ "$vflag" = "" -a "$nflag" = "" ]; then
		kill $PROGRESS_PID &>/dev/null
	fi
}

function testIsInstalled() {

	hash $1 2>/dev/null
	if [ $? -eq 1 ]; then
		echo >&2 "ERROR - $1 is not installed or not in your PATH"; exit 1;
	fi
}

function readParameter() {

	variable=$1
	shift
	parameter=$1
	shift

	eval $variable=$(printf %q "$(sed '/^\#/d' sonar-project.properties | grep $parameter | tail -n 1 | cut -d '=' -f2-)")
}

# Run a set of commands with logging and error handling
function runCommand() {

	# 1st arg: redirect stdout
	# 2nd arg: command to run
	# 3rd..nth arg: args
	redirect=$1
	shift

	command=$1
	shift

	if [ "$nflag" = "on" ]; then
		# don't execute command, just echo it
		echo
		if [ "$redirect" = "/dev/stdout" ]; then
			if [ "$vflag" = "on" ]; then
				echo "+" $command "$@"
			else
				echo "+" $command "$@" "> /dev/null"
			fi
		elif [ "$redirect" != "no" ]; then
			echo "+" $command "$@" "> $redirect"
		else
			echo "+" $command "$@"
		fi

	elif [ "$vflag" = "on" ]; then
		echo

		if [ "$redirect" = "/dev/stdout" ]; then
			set -x #echo on
			$command "$@"
			returnValue=$?
			set +x #echo off
		elif [ "$redirect" != "no" ]; then
			set -x #echo on
			$command "$@" > $redirect
			returnValue=$?
			set +x #echo off
		else
			set -x #echo on
			$command "$@"
			returnValue=$?
			set +x #echo off
		fi

		if [[ $returnValue != 0 && $returnValue != 5 ]] ; then
			stopProgress
			echo "ERROR - Command '$command $@' failed with error code: $returnValue"
			exit $returnValue
		fi
	else

		if [ "$redirect" = "/dev/stdout" ]; then
			$command "$@" > /dev/null
		elif [ "$redirect" != "no" ]; then
			$command "$@" > $redirect
		else
			$command "$@"
		fi

        returnValue=$?
		if [[ $returnValue != 0 && $returnValue != 5 ]] ; then
			stopProgress
			echo "ERROR - Command '$command $@' failed with error code: $returnValue"
			exit $returnValue
		fi


		echo
	fi
}

## COMMAND LINE OPTIONS
vflag=""
nflag=""
unittests=""
swiftlint="on"
tailor="on"
lizard="on"
oclint="on"
fauxpas="on"
sonarscanner="/Users/dengqu/Downloads/sonar-scanner-4.0.0.1744-macosx"
sonarurl="http://localhost:9000/"
sonarlogin="admin"
sonarpassword="admin"

while [ "$1" != "" ]; do
  param=$(echo "$1" | awk -F= '{print $1}')
  value=$(echo "$1" | sed 's/^[^=]*=//g')

  case $param in
    -v)
      vflag=on
      ;;
    -n)
      nflag=on
      ;;
    -nounittests)
      unittests=""
      ;;
    -noswiftlint)
      swiftlint=""
      ;;
    -notailor)
      tailor=""
      ;;
    -usesonarscanner)
      sonarscanner="on"
      ;;
    -sonarurl)
      sonarurl="$value"
      ;;
    -sonarlogin)
      sonarlogin="$value"
      ;;
    -sonarpassword)
      sonarpassword="$value"
      ;;
    *)
      echo >&2 "Usage: $0 [-v] [-n] [-nounittests] [-noswiftlint)] [-notailor] [-usesonarscanner] [-sonarurl=value] [-sonarlogin=value] [-sonarpassword=value]"
      exit 1
      ;;
  esac
  shift
done

# Usage OK
echo "Running run-sonar-swift.sh..."

## CHECK PREREQUISITES

# sonar-project.properties in current directory
if [ ! -f sonar-project.properties ]; then
	echo >&2 "ERROR - No sonar-project.properties in current directory"; exit 1;
fi

## READ PARAMETERS from sonar-project.properties

#.xcodeproj filename
projectFile=''; readParameter projectFile 'sonar.swift.project'
workspaceFile=''; readParameter workspaceFile 'sonar.swift.workspace'

# Count projects
if [[ ! -z "$projectFile" ]]; then
	projectCount=$(echo $projectFile | sed -n 1'p' | tr ',' '\n' | wc -l | tr -d '[[:space:]]')
	if [ "$vflag" = "on" ]; then
	    echo "Project count is [$projectCount]"
	fi
fi

# Source directories for .swift files
srcDirs=''; readParameter srcDirs 'sonar.sources'
# The name of your application scheme in Xcode
appScheme=''; readParameter appScheme 'sonar.swift.appScheme'
# The app configuration to use for the build
appConfiguration=''; readParameter appConfiguration 'sonar.swift.appConfiguration'
# The name of your test scheme in Xcode
testScheme=''; readParameter testScheme 'sonar.swift.testScheme'
# The name of your other binary files (frameworks)
binaryNames=''; readParameter binaryNames 'sonar.coverage.binaryNames'
# Get the path of plist file
plistFile=`xcodebuild -showBuildSettings -project "${projectFile}" | grep -i 'PRODUCT_SETTINGS_PATH' -m 1 | sed 's/[ ]*PRODUCT_SETTINGS_PATH = //'`
# Number version from plist if no sonar.projectVersion
numVersionFromPlist=`defaults read "${plistFile}" CFBundleShortVersionString`

# Read destination simulator
destinationSimulator=''; readParameter destinationSimulator 'sonar.swift.simulator'

# Read tailor configuration
tailorConfiguration=''; readParameter tailorConfiguration 'sonar.swift.tailor.config'

# The file patterns to exclude from coverage report
excludedPathsFromCoverage=''; readParameter excludedPathsFromCoverage 'sonar.swift.excludedPathsFromCoverage'

# Skipping tests
skipTests=''; readParameter skipTests 'sonar.swift.skipTests'

# Check for mandatory parameters
if [ -z "$projectFile" -o "$projectFile" = " " ] && [ -z "$workspaceFile" -o "$workspaceFile" = " " ]; then
	echo >&2 "ERROR - sonar.swift.project or/and sonar.swift.workspace parameter is missing in sonar-project.properties. You must specify which projects (comma-separated list) are application code or which workspace and project to use."
	exit 1
elif [ ! -z "$workspaceFile" ] && [ -z "$projectFile" ]; then
	echo >&2 "ERROR - sonar.swift.workspace parameter is present in sonar-project.properties but sonar.swift.project and is not. You must specify which projects (comma-separated list) are application code or which workspace and project to use."
	exit 1
fi
if [ -z "$srcDirs" -o "$srcDirs" = " " ]; then
	echo >&2 "ERROR - sonar.sources parameter is missing in sonar-project.properties. You must specify which directories contain your .swift source files (comma-separated list)."
	exit 1
fi
if [ -z "$appScheme" -o "$appScheme" = " " ]; then
	echo >&2 "ERROR - sonar.swift.appScheme parameter is missing in sonar-project.properties. You must specify which scheme is used to build your application."
	exit 1
fi
if [ "$unittests" = "on" ]; then
    if [ -z "$destinationSimulator" -o "$destinationSimulator" = " " ]; then
	      echo >&2 "ERROR - sonar.swift.simulator parameter is missing in sonar-project.properties. You must specify which simulator to use."
	      exit 1
    fi
fi

# if the appConfiguration is not specified then set to Debug
if [ -z "$appConfiguration" -o "$appConfiguration" = " " ]; then
	appConfiguration="Debug"
fi



if [ "$vflag" = "on" ]; then
 	echo "Xcode project file is: $projectFile"
	echo "Xcode workspace file is: $workspaceFile"
 	echo "Xcode application scheme is: $appScheme"
    echo "Number version from plist is: $numVersionFromPlist"
  if [ -n "$unittests" ]; then
 	    echo "Destination simulator is: $destinationSimulator"
 	    echo "Excluded paths from coverage are: $excludedPathsFromCoverage"
  else
      echo "Unit surefire are disabled"
  fi
fi

## SCRIPT

# Start progress indicator in the background
if [ "$vflag" = "" -a "$nflag" = "" ]; then
	startProgress &
	# Save PID
	PROGRESS_PID=$!
fi

# Create sonar-reports/ for reports output
if [ "$vflag" = "on" ]; then
    echo 'Creating directory sonar-reports/'
fi
rm -rf sonar-reports
mkdir sonar-reports

# Build and extract project information needed later
buildCmd=($XCODEBUILD_CMD clean build-for-testing)
echo -n 'Building & extracting Xcode project information'
if [[ "$workspaceFile" != "" ]] ; then
    buildCmd+=(-workspace "$workspaceFile")
else
    buildCmd+=(-project "$projectFile")
fi
buildCmd+=(-scheme $appScheme)
if [[ ! -z "$destinationSimulator" ]]; then
    buildCmd+=(-destination "$destinationSimulator" -destination-timeout 360 COMPILER_INDEX_STORE_ENABLE=NO)
fi
runCommand  xcodebuild.log "${buildCmd[@]}"
#oclint-xcodebuild # Transform the xcodebuild.log file into a compile_command.json file
cat xcodebuild.log | $XCPRETTY_CMD -r json-compilation-database -o compile_commands.json

# Objective-C code detection
hasObjC="no"
compileCmdFile=compile_commands.json
minimumSize=3
actualSize=$(stat -f%z "$compileCmdFile")
if [ $actualSize -ge $minimumSize ]; then
    hasObjC="yes"
fi

echo -n "hasObjC:$hasObjC ,buildCmd:$buildCmd"
# Tests : surefire and coverage
if [ "$unittests" = "on" ]; then

    # Put default xml files with no surefire and no coverage...
    echo "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><testsuites name='AllTestUnits'></testsuites>" > sonar-reports/TEST-report.xml
    echo "<?xml version='1.0' ?><!DOCTYPE coverage SYSTEM 'http://cobertura.sourceforge.net/xml/coverage-03.dtd'><coverage><sources></sources><packages></packages></coverage>" > sonar-reports/coverage-swift.xml

    echo -n 'Running tests'
    buildCmd=($XCODEBUILD_CMD test)
    if [[ ! -z "$workspaceFile" ]]; then
        buildCmd+=(-workspace "$workspaceFile")
    elif [[ ! -z "$projectFile" ]]; then
	      buildCmd+=(-project "$projectFile")
    fi
    buildCmd+=( -scheme "$appScheme" -configuration "$appConfiguration" -enableCodeCoverage YES)
    if [[ ! -z "$destinationSimulator" ]]; then
        buildCmd+=(-destination "$destinationSimulator" -destination-timeout 60)
    fi
    if [[ ! -z "$skipTests" ]]; then
    	buildCmd+=(-skip-testing:"$skipTests")
    fi

    runCommand  sonar-reports/xcodebuild.log "${buildCmd[@]}"
    cat sonar-reports/xcodebuild.log  | $XCPRETTY_CMD -t --report junit
    mv build/reports/junit.xml sonar-reports/TEST-report.xml


    echo 'Computing coverage report'

	  firstProject=$(echo $projectFile | sed -n 1'p' | tr ',' '\n' | head -n 1)

    slatherCmd=($SLATHER_CMD coverage)

    # Build the --binary-basename
    if [[ ! -z "$binaryNames" ]]; then
      echo $binaryNames | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh3
      while read word; do
        slatherCmd+=(--binary-basename "$word")
      done < tmpFileRunSonarSh3
      rm -rf tmpFileRunSonarSh3
    fi

    # Build the --exclude flags
    if [ ! -z "$excludedPathsFromCoverage" -a "$excludedPathsFromCoverage" != " " ]; then
	      echo $excludedPathsFromCoverage | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh2
	      while read word; do
		        slatherCmd+=(-i "$word")
	      done < tmpFileRunSonarSh2
	      rm -rf tmpFileRunSonarSh2
    fi

    slatherCmd+=(--input-format profdata --cobertura-xml --output-directory sonar-reports)

    if [[ ! -z "$workspaceFile" ]]; then
        slatherCmd+=(--workspace "$workspaceFile")
    fi
    slatherCmd+=(--scheme "$appScheme" "$firstProject")

    echo "${slatherCmd[@]}"

    runCommand /dev/stdout "${slatherCmd[@]}"
    mv sonar-reports/cobertura.xml sonar-reports/coverage-swift.xml
fi

# SwiftLint
if [ "$swiftlint" = "on" ]; then
	if hash $SWIFTLINT_CMD 2>/dev/null; then
		echo -n 'Running SwiftLint...'

		# Build the --include flags
		currentDirectory=${PWD##*/}
		echo "$srcDirs" | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh
		while read word; do

			# Run SwiftLint command
		    $SWIFTLINT_CMD lint --path "$word" > sonar-reports/"$(echo $word | sed 's/\//_/g')"-swiftlint.txt

		done < tmpFileRunSonarSh
		rm -rf tmpFileRunSonarSh
	else
		echo "Skipping SwiftLint (not installed!)"
	fi

else
	echo 'Skipping SwiftLint (test purposes only!)'
fi

# Tailor
if [ "$tailor" = "on" ]; then
	if hash $TAILOR_CMD 2>/dev/null; then
		echo -n 'Running Tailor...'

		# Build the --include flags
		currentDirectory=${PWD##*/}
		echo "$srcDirs" | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh
		while read word; do

			  # Run tailor command
		    $TAILOR_CMD $tailorConfiguration "$word" > sonar-reports/"$(echo $word | sed 's/\//_/g')"-tailor.txt

		done < tmpFileRunSonarSh
		rm -rf tmpFileRunSonarSh
	else
		echo "Skipping Tailor (not installed!)"
	fi

else
	echo 'Skipping Tailor!'
fi

if [ "$oclint" = "on" ] && [ "$hasObjC" = "yes" ]; then

	echo -n 'Running OCLint...'

	# Options
	maxPriority=10000
    longLineThreshold=250

	# Build the --include flags
	currentDirectory=${PWD##*/}
	echo "$srcDirs" | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh
	while read word; do
		numberOfObjcFiles=$(find "${word}/" -name '*.m' | wc -l | tr -d ' ')
		if [ $numberOfObjcFiles -gt 0 ]; then
#			includedCommandLineFlags=" --include .*/${currentDirectory}/${word}"
            includedCommandLineFlags=" --include .*/${currentDirectory}/${word}"
			if [ "$vflag" = "on" ]; then
            	echo
            	echo -n "Path included in oclint analysis is:$includedCommandLineFlags"
        	fi
		    oclint-json-compilation-database -- -max-priority-1 $maxPriority -max-priority-2 $maxPriority -max-priority-3 $maxPriority -rc LONG_LINE=150 -report-type pmd -o sonar-reports/$(echo $word | sed 's/\//_/g')-oclint.xml
		else
			echo "$word has no Objective-C, skipping..."
		fi
	done < tmpFileRunSonarSh
	rm -rf tmpFileRunSonarSh


else
	echo 'Skipping OCLint (test purposes only!)'
fi

#FauxPas
if [ "$fauxpas" = "on" ] && [ "$hasObjC" = "yes" ]; then
    hash fauxpas 2>/dev/null
    if [ $? -eq 0 ]; then

        echo -n 'Running FauxPas...'

        if [ "$projectCount" = "1" ]
        then

            fauxpas -o json check $projectFile --workspace $workspaceFile --scheme $appScheme > sonar-reports/fauxpas.json


        else

            echo $projectFile | sed -n 1'p' | tr ',' '\n' > tmpFileRunSonarSh
            while read projectName; do

                $XCODEBUILD_CMD -list -project $projectName | sed -n '/Schemes/,$p' | while read scheme
                do

                if [ "$scheme" = "" ]
                then
                exit
                fi

                if [ "$scheme" == "${scheme/Schemes/}" ]
                then
                    if [ "$scheme" != "$testScheme" ]
                    then
                        projectBaseDir=$(dirname $projectName)
                        workspaceRelativePath=$(python -c "import os.path; print os.path.relpath('$workspaceFile', '$projectBaseDir')")
                        fauxpas -o json check $projectName --workspace $workspaceRelativePath --scheme $scheme > sonar-reports/$(basename $projectName .xcodeproj)-$scheme-fauxpas.json
                    fi
                fi

                done

            done < tmpFileRunSonarSh
            rm -rf tmpFileRunSonarSh

	    fi

    else
        echo 'Skipping FauxPas (not installed)'
    fi
else
    echo 'Skipping FauxPas'
fi

# Lizard Complexity
if [ "$lizard" = "on" ]; then
	if hash $LIZARD_CMD 2>/dev/null; then
		echo -n 'Running Lizard...'
  		$LIZARD_CMD --xml "$srcDirs" > sonar-reports/lizard-report.xml
  	else
  		echo 'Skipping Lizard (not installed!)'
  	fi
else
 	echo 'Skipping Lizard (test purposes only!)'
fi

# The project version from properties file
numVersionSonarRunner=''; readParameter numVersionSonarRunner 'sonar.projectVersion'
if [ -z "$numVersionSonarRunner" -o "$numVersionSonarRunner" = " " ]; then
	numVersionSonarRunner=" --define sonar.projectVersion=$numVersionFromPlist"
else
	#if we have version number in properties file, we don't overide numVersion for sonar-runner/sonar-scanner command
	numVersionSonarRunner='';
fi
# Build sonar-runner / sonnar-scanner arguments
sonarArguments=();
if [ "$sonarurl" != "" ]; then
  sonarArguments+=(-Dsonar.host.url=$sonarurl)
fi
if [ "$sonarlogin" != "" ]; then
  sonarArguments+=(-Dsonar.login=$sonarlogin)
fi
if [ "$sonarpassword" != "" ]; then
  sonarArguments+=(-Dsonar.password=$sonarpassword)
fi

# SonarQube
if [ "$sonarscanner" = "on" ]; then
    echo -n 'Running SonarQube using SonarQube Scanner'
    if hash /dev/stdout sonar-scanner 2>/dev/null; then
        runCommand /dev/stdout sonar-scanner "${sonarArguments[@]}" $numVersionSonarRunner
    else
        echo 'Skipping sonar-scanner (not installed!)'
    fi
else
    echo -n 'Running SonarQube using SonarQube Runner'
    if hash /dev/stdout sonar-runner 2>/dev/null; then
	   runCommand /dev/stdout sonar-runner "${sonarArguments[@]}" $numVersionSonarRunner
    else
	   runCommand /dev/stdout sonar-scanner "${sonarArguments[@]}" $numVersionSonarRunner
    fi
fi
#runCommand /dev/stdout "${slatherCmd[@]}"

# Kill progress indicator
stopProgress

exit 0

由于本身oclint版本可能跟官方当时运行版本有冲突,主要修改了以下位置才能运行起来,本人oclint版本如下

image.png

对比官方修改:

image.png

image.png 目前主要修改这两个点就能运行了,当然运行过程可能会遇到其他问题,到时候可以自行google一下 5.有了上面配置之后,运行run-sonar-swift.sh脚本就行,里面会运行oclint ,swiftlint,sonarscanner等任务

image.png

等待运行结束之后,登陆sonar 服务边看到效果了

image.png

到这里便实现了对oc和swift代码进行扫描和在sonar上展示了,后续会继续结束ios静态代码扫描怎么跟Jenkins集合,如何自定义代码扫描规则等