OCLint + Jenkins + SonarQube build iOS code static analysis system

background

With the increasing amount of code and the expansion of the team, when we want to iterate quickly, we often want to get the requirements online as soon as possible, resulting in the code is not very standardized, leaving a pile of technical debt over a long time, and the quality of the code is not guaranteed. So I began to try some code quality related construction, hoping to help us scan out some code vulnerabilities through code static scanning, and then try to repair vulnerabilities and bug s.

Tools and platforms

Tools and platforms involved in this paper:

Introduction and configuration of open source solutions

Most of the static analysis of iOS platform code is basically based on open source oclint or infer. Although this paper uses sonarqube, the core of the free analysis scheme is still oclint and infer

sonarqube is an open-source static code analysis platform, which provides free community version. The free community version does not support Objective-C, but github provides open-source plug-ins, Objective-C, and the paid community version plus has analysis plug-ins that support Objective-C SonarCFamily for C

Both the free scheme and the paid scheme are based on the logs in the xcodebuild process. This time, we mainly focus on the related configuration process of the open source scheme.

For the use of open source solutions, the essential process is as follows:

xcodebuild

The iOS core tool comes with Xcode installed. Because the core of oclint analysis is the log of xcodebuild during app compilation, xcodebuild is required (if build fails, the built log will also be analyzed, but try to ensure that the build can succeed)

If the project is in workspace and you need to specify - workspace and the corresponding scheme, you can view it through xcodebuild -list

//implement
$ xcodebuild -list

//implement
$ xcodebuild -workspace CsdnPlus.xcworkspace -scheme CsdnPlus

We only need to print out the package of IPA during the installation process, so we only need to print out the package of IPA during the installation process.

//implement
$ xcodebuild -showsdks

Because xcodebuild has a cache, we need clean before each execution

$ xcodebuild -workspace CsdnPlus.xcworkspace -scheme CsdnPlus clean

Execute build compilation

$ xcodebuild -scheme CsdnPlus -workspace CsdnPlus.xcworkspace -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 12 Pro Max' -configuration Debug



If you can compile successfully, you can go to the next step

xcpretty

xcpretty is a fast and flexible formatter for xcodebuild.
It does one thing, and it should do it well.

Xcretty is a tool for formatting xcodebuild output. Installation:

$ gem install xcpretty

-r. -- report specifies the format of the generated report, which can be JUnit, HTML and JSON compilation database.

-o. -- output specifies the name of the generated file.
Here we use JSON compilation database format, and the output file name is compile_commands.json
(note that the output name cannot be changed, otherwise oclint will report an error later, because the verification name is built in the oclint source code, which can be viewed for details Source code)

Usage:

Immediately follow the xcodebuild related statements, such as:

$ xcodebuild [flags] | xcpretty

Log collection can be combined with tee

$ xcodebuild [flags] | tee xcodebuild.log | xcpretty

Execute the complete command to generate compiled data_ commands. JSON file:

First, you need to use xcodebuild clean and build projects, and add COMPILER_INDEX_STORE_ENABLE=NO parameter, otherwise an error may be reported: oclint: error: one compiler command contains multiple jobs

$ xcodebuild -scheme CsdnPlus -workspace CsdnPlus.xcworkspace -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 12 Pro Max' -configuration Debug GCC_PRECOMPILE_PREFIX_HEADER=YES CLANG_ENABLE_MODULE_DEBUGGING=NO COMPILER_INDEX_STORE_ENABLE=NO | tee xcodebuild.log | xcpretty -r json-compilation-database -o compile_commands.json

OCLint

OCLint is a static code analysis tool for improving quality and reducing defects by inspecting C, C++ and Objective-C code

OCLint is a static analysis tool developed based on Clang Tooling, which is mainly used to find potential key technical problems that the compiler cannot check. It is the core tool for OC code analysis, mainly for the compile generated in the previous step_ commands. JSON to analyze and generate reports

Command installation:

$ brew tap oclint/formulae
$ brew install oclint

I recommend using the installation package to install OClint. Homebrew installation can only be installed to version 20.11, and the latest Xcode version corresponds to 22.02. If the installed version does not match, OClint analyzes that there are only a bunch of compiler error s.

Download the installation package:

https://github.com/oclint/oclint/releases

Configure environment variables:

export PATH="/Users/csdn/oclint-22.02/bin:$PATH"
source ~/.zshrc

Enter oclint --version in the terminal to verify whether the installation is successful.

Enter oclint --help on the terminal to view the introduction of the command

We mainly use oclint-json-compilation-database Orders, Github source code

The oclint JSON compilation database command supports specifying verification folders and filtering specified folders. In essence, the oclint -p command is finally executed, which can be viewed by attaching - v. at the same time, it also supports the use of -- followed by the oclint execution parameters.
For example:

// Here -- the parameter after the symbol is passed to oclint
$ oclint-json-compilation-database -v -e Pods -e xxxx -- -report-type html -o report.html

The - rc option of oclint can customize the parameter value of verification, for example:

$ oclint-json-compilation-database -v -e Pods -e xxxx -- -rc LONG_METHOD=60 -rc LONG_LINE=100

In addition, when we need to customize multiple oclint parameters, we can write the configuration in .oclint In the file

disable-rules:                      // Rules not used
   - LongLine
rulePaths:                          // The path where the oclint verification rule is located is / usr/local/lib/oclint/rules by default on the Mac side. If you do not need custom rules, you can not configure this item
  - /etc/rules
rule-configurations:                // Custom configuration parameters
  - key: CYCLOMATIC_COMPLEXITY
    value: 15
  - key: NPATH_COMPLEXITY
    value: 300
output: oclint.xml                  // Generated report
report-type: xml                    // The generated report format supports html, xml, json, etc
max-priority-1: 20                  // The maximum number of problems in level 1. If more than this number of problems are detected, it will be automatically terminated
max-priority-2: 40                  // Maximum number of level 2 problems
max-priority-3: 60                  // Maximum number of level 3 problems
enable-clang-static-analyzer: false //

The following are 72 built-in support of OCLint Rule , you can view it through -- List enabled rules X

$ oclint --list-enabled-rules x
enabled rules:
- TooManyMethods
- DestructorOfVirtualClass
- DeadCode
- EmptyForStatement
- AvoidDefaultArgumentsOnVirtualMethods
- ProblematicBaseClassDestructor
- MisplacedDefaultLabel
- EmptyFinallyStatement
- CallingProhibitedMethod
- RedundantIfStatement
- CollapsibleIfStatements
- UnnecessaryElseStatement
- ConstantConditionalOperator
- DeepNestedBlock
- AssignIvarOutsideAccessors
- UnnecessaryNullCheckForDealloc
- RedundantNilCheck
- RedundantLocalVariable
- EmptyDoWhileStatement
- UnusedMethodParameter
- BitwiseOperatorInConditional
- ReturnFromFinallyBlock
- MultipleUnaryOperator
- DoubleNegative
- MissingCallToBaseMethod
- EmptyWhileStatement
- ShortVariableName
- ParameterReassignment
- UselessParentheses
- ThrowExceptionFromFinallyBlock
- UnnecessaryDefaultStatement
- HighNcssMethod
- PreferEarlyExit
- MissingBreakInSwitchStatement
- TooManyParameters
- CallingProtectedMethod
- AvoidBranchingStatementAsLastInLoop
- MissingAbstractMethodImplementation
- MissingHashMethod
- MisplacedNullCheck
- MisplacedNilCheck
- UseContainerLiteral
- LongLine
- ForLoopShouldBeWhileLoop
- HighNPathComplexity
- LongMethod
- EmptySwitchStatement
- RedundantConditionalOperator
- EmptyTryStatement
- EmptyCatchStatement
- UseObjectSubscripting
- AvoidPrivateStaticMembers
- EmptyElseBlock
- InvertedLogic
- LongClass
- LongVariableName
- GotoStatement
- BrokenOddnessCheck
- UseNumberLiteral
- TooFewBranchesInSwitchStatement
- UseBoxedExpression
- JumbledIncrementer
- EmptyIfStatement
- BranchDivergence
- MissingDefaultStatement
- HighCyclomaticComplexity
- NonCaseLabelInSwitchStatement
- ConstantIfExpression
- BrokenNullCheck
- BrokenNilCheck
- TooManyFields
- UnusedLocalVariable

If we use oclint eventually we will oclint is placed with compile_commands.json and execute the command in this path:

$ oclint-json-compilation-database -v -e Pods

Or execute the command directly:

$ oclint-json-compilation-database -e Pods -- -report-type pmd \
    -rc=LONG_CLASS=1500 \
    -rc=NESTED_BLOCK_DEPTH=5 \
    -rc=LONG_VARIABLE_NAME=80 \
    -rc=LONG_METHOD=200 \
    -rc=LONG_LINE=300 \
    -disable-rule ShortVariableName \
    -disable-rule ObjCAssignIvarOutsideAccessors \
    -disable-rule AssignIvarOutsideAccessors \
    -allow-duplicated-violations=false\
    -max-priority-1=100000 \
    -max-priority-2=100000 \
    -max-priority-3=100000 >> oclint.xml

Finally, oclint will be generated XML (you can also generate your own html format to directly view the effect)

sonarqube

sonarqube is a platform that provides static analysis of code. It provides a complete set of static analysis schemes, including back-end and front-end pages. It can be combined with jenkins, gitlab and other platforms for code analysis. sonarqube is divided into community version and commercial version. It can scan multiple languages and open source. Official website address.
Because the underlying source code is developed in java, the support for java code is relatively perfect, but the free community version does not support OC. Therefore, if we want to use this platform, we have the following two ways:

  • Open source plug-ins sonar-swift
    , support Objective-C and Swift / Java, and import the scanning and analysis results of SwiftLint, Infer, OCLint, Lizard and Fauxpas tools. Latest v1 6 version, compatible with sonarqube 8.9 lts version. The plug-in is developed by TAL R & D team and is open source.
  • Pay for community plus, which provides SonarCFamily for C plug-in
    There are official technical support, 250 + rules to choose from, and rules cannot be customized.

We will use the open source plug-in solution.

Sonar services (sonarqube) installation

There are two ways to install sonarqube

  • docker installation
$ docker pull sonarqube:8.9.7-community

After downloading the installation package, enter the bin/macosx-universal-64 directory. Execute command:

$ sh sonar.sh start

The console outputs Started SonarQube, indicating that the startup is successful.

Access in browser http://localhost:9000/ , the page can be opened, indicating that the startup is successful.
The default account is admin and the password is admin.

Note: if the startup fails, you can check the log under sonarqube/logs.

During installation, it was found that sonarqube version 8.9.7 requires Java 11 environment. Therefore, you need to install the Java 11 environment first.
Installing Java 11:

After successful startup, we will see warning at the bottom

Warning: we are recommended to configure the database ourselves. It should be noted that if SonarQube wants to persist and save data, it needs to rely on the database.

SonarQube provides H2 storage by default, and can only temporarily store some small project results for demonstration purposes only.

In conf / sonar Just configure the database address under properties. MySQL, Oracle and PostgreSQL are optional.

Now let's configure the database( mysql will no longer be supported in the future ): PostgreSQL is used here, Configuration reference

PostgreSQL

Install PostgreSQL with Homebrew command:

$ brew install postgresql //install

After installing the database, start the database and execute the command:

$ pg_ctl -D /usr/local/var/postgres start //start-up
$ createdb                                //Create database
$ psql                                    //Login console

After the database is installed and created, we need to provide a database and account sonarqube to use. Execute command:

CREATE USER sonarqube WITH PASSWORD 'sonarqube';//Create user
CREATE DATABASE sonar OWNER sonarqube;//Create a database that belongs to this user

After creation, execute the command to exit:

\q

Then we go to the sonarqube/conf directory to edit sonar Properties to complete the corresponding configuration of the database

sonar.jdbc.username=sonarqube
sonar.jdbc.password=sonarqube
sonar.jdbc.url=jdbc:postgresql://localhost/sonar

After editing, execute in the sonarqube/bin/macosx-universal-64 / Directory: SH sonar SH restart, and the warning has been eliminated.

So far, the front-end service of our sonarqube has been configured.

Installation of Chinese package

Download the corresponding version through Github Chinese package jar package plug-in.

Download the plug-ins and put them in the / extensions/plugins directory. Restart the sonarqube service and you can see the Chinese interface.

sonar-swift

Download the corresponding through GitHub plug-in unit

Download the plug-ins and put them in the / extensions/plugins directory. Just restart the sonarqube service.

sonar-scanner

Sonar scanner is used to scan local code and upload it to the SonarQube platform.

  • Download installation address ; Select different installation packages according to different operating systems.
  • Configure environment variables:
$ vim ~/.bash_profile

#sonar-scanner for cli
export PATH=$PATH:/Users/csdn/scanner/bin:$PATH

$ source ~/.bash_profile

Sonar scanner can be used in two ways:

  • Configuration file mode:

Create a new sonar project under the root directory of the project to be scanned Properties file, as follows:

sonar.projectKey=CsdnPlus
sonar.projectName=CsdnPlus
sonar.language=objc
sonar.sources=/Users/csdn/.jenkins/workspace/csdn_build_ios/
sonar.objectivec.workspace=CsdnPlus.xcworkspace
sonar.objectivec.appScheme=CsdnPlus
sonar.sourceEncoding=UTF-8
sonar.junit.reportsPath=sonar-reports/
sonar.objectivec.oclint.report=sonar-reports/oclint.xml

Enter the project root directory, and then enter the sonar scanner command to perform code analysis.

  • Command line mode:

Parameters are set in the command:

sonar-scanner -Dsonar.projectKey=CsdnPlus -Dsonar.projectName=CsdnPlus -Dsonar.projectName=CsdnPlus -Dsonar.projectVersion=5.1.0 

The core here is the xml file generated by OCLint in the above steps. In addition, pay attention to OCLint xml must be placed under the sonar reports file.

After the command is executed successfully, you can see the corresponding detection effect on the front page of sonarQube.

Test effect drawing:

Integrated into Jenkins

Our project already has automatic construction service, so it is more convenient.

  • In Jenkins project configuration, OCLint (you can name it yourself) option is added

  • Add OCLint related commands in the build Shell command

if [ "$MODE"x = "OCLint"x ]
then
    sh /Users/csdn/.jenkins/workspace/csdn_build_ios/fastlane/oclint.sh "$GIT_BRANCH"
fi

  • Attach complete oclint SH command:
#!/bin/bash

COLOR_ERR="\033[1;31m"    #Error prompt
COLOR_SUCC="\033[0;32m"  #Success prompt
COLOR_QS="\033[1;37m"  #Problem color
COLOR_AW="\033[0;37m"  #Answer tips
COLOR_END="\033[1;34m"     #Color Terminator



# Find the ProjectName of the project
function searchProjectName () {
  # maxdepth finds the depth of the folder
  find . -maxdepth 1 -name "*.xcodeproj"
}

function oclintForProject () {
    # Pre detect whether the required installation package exists
    if which xcodebuild 2>/dev/null; then
        echo 'xcodebuild exist'
    else
        echo 'xcodebuild Not installed, please install Xcode'
    fi

    if which oclint 2>/dev/null; then
        echo 'oclint exist'
    else
        echo 'oclint Not installed, please install OCLint'
    fi
    if which xcpretty 2>/dev/null; then
        echo 'xcpretty exist'
    else
        gem install xcpretty
    fi


    # Specify encoding
    export LANG="zh_CN.UTF-8"
    export LC_COLLATE="zh_CN.UTF-8"
    export LC_CTYPE="zh_CN.UTF-8"
    export LC_MESSAGES="zh_CN.UTF-8"
    export LC_MONETARY="zh_CN.UTF-8"
    export LC_NUMERIC="zh_CN.UTF-8"
    export LC_TIME="zh_CN.UTF-8"
    export xcpretty=/usr/local/bin/xcpretty # The installation location of xcretty can be found at the terminal with which xcretty

    searchFunctionName=`searchProjectName`
    path=${searchFunctionName}
    # String replacement function// Indicates global replacement / indicates the first result replacement matched.
    path=${path//.\//}  # ./BridgeLabiPhone.xcodeproj -> BridgeLabiPhone.xcodeproj
    path=${path//.xcodeproj/} # BridgeLabiPhone.xcodeproj -> BridgeLabiPhone
    
    myworkspace=$path".xcworkspace" # workspace name
    myscheme=$path  # scheme name

    # Clear last compiled data
    if [ -d ./derivedData ]; then
        echo -e $COLOR_SUCC'-----Last compiled data cleared derivedData-----'$COLOR_SUCC
        rm -rf ./derivedData
    fi

    # xcodebuild clean
    xcodebuild -scheme $myscheme -workspace $myworkspace clean


    # # Generate compiled data
    xcodebuild -scheme $myscheme -workspace $myworkspace -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 12 Pro Max' -configuration Debug GCC_PRECOMPILE_PREFIX_HEADER=YES CLANG_ENABLE_MODULE_DEBUGGING=NO COMPILER_INDEX_STORE_ENABLE=NO | tee xcodebuild.log | xcpretty -r json-compilation-database -o compile_commands.json

    if [ -f ./compile_commands.json ]; then
        echo -e $COLOR_SUCC'Compiled data generation completed'$COLOR_SUCC
    else
        echo -e $COLOR_ERR'Compilation data generation failed'$COLOR_ERR
        return -1
    fi

    # Generate report
    oclint-json-compilation-database -e Pods -- -report-type pmd \
    -rc=LONG_CLASS=1500 \
    -rc=NESTED_BLOCK_DEPTH=5 \
    -rc=LONG_VARIABLE_NAME=80 \
    -rc=LONG_METHOD=200 \
    -rc=LONG_LINE=300 \
    -disable-rule ShortVariableName \
    -disable-rule ObjCAssignIvarOutsideAccessors \
    -disable-rule AssignIvarOutsideAccessors \
    -allow-duplicated-violations=false\
    -max-priority-1=100000 \
    -max-priority-2=100000 \
    -max-priority-3=100000 >> oclint.xml

    if [ -f ./oclint.xml ]; then
        rm compile_commands.json
        echo -e $COLOR_SUCC'Code analysis completed'$COLOR_SUCC
        
        mv oclint.xml sonar-reports/
        
        echo -e $COLOR_SUCC'Move to sonar-reports complete'$COLOR_SUCC
        echo -e $COLOR_SUCC'Start execution sonar-scanner Scan file'$COLOR_SUCC
        sonar-scanner  -Dsonar.projectVersion=$1
    else
        echo -e $COLOR_ERR'Analysis failed'$COLOR_ERR
        return -1
    fi
}

oclintForProject $1

  • Jenkins configures PMD (of course, with SonarQube, PMD is very chicken)


Keywords: Swift iOS jenkins

Added by yelvington on Sun, 27 Feb 2022 13:40:45 +0200