Imagine you’re managing a Software Product that is built on a language with the great C in its name, and you’re wondering what the quality of the source code of this project is. You’re responsible for ensuring your product meets deadlines and is working as expected. You could try to do code reviews yourself to see the quality of the code, but your engineers produce code at a steady rate, and it takes time to do code reviews. Even doing this, you find that it only gives you a peek at the code produced by your engineers. You are stressed as you scramble to get things under control.
It’s a story I’ve heard time and again. Fortunately, there’s a tool that can help.
A New Tool Arrives
SonarQube is made for this purpose. It can give you insight into the health of a project and provide metrics to see how quality is affected by every commitment. Unit test coverage is provided as a metric. Quality gates can be set up that will fail the code if it does not meet your expectations. While it will not perform functional tests for you, it will improve your ability to sleep at night, knowing where the living breathing creature that is your software project is at in terms of health. Using SonarQube with a C family (C, C++, Objective C, C#) project is a bigger undertaking than with another type of project, such as a JavaScript or a Python project. You look at the environment and you see you have the project in a Continuous Integration and Delivery Pipeline (CI/CD) with containers that facilitate your build process. You set up a server, scour the internet for tutorials and hints, and land on this blog post…
One Tool, Multiple Configurations
SonarQube has multiple configurations. Let’s focus on using a private server for Sonar Server and using Docker containers in our build process, including scanning stages. When doing this it is important to point your Sonar properties to this server. We are using a C family language (C, C++, Objective C, C#), therefore we are using a build wrapper.
Nice Touches
For multiple projects, you may choose to create a template for easier adjustments to the process of setting up a build with a wrapper or a scan with SonarScanner. You will need to create a location for the wrapper to output processing files. This, however, is in addition to the build directory which must also be available, as those build files are also necessary for SonarQube to analyze the project being scanned.
The SonarQube CWrapper generates files about the build that are used in assessing the project. Those files are necessary, so you will need to either put them in a cache or mark the build directories as artifacts. Outside of that build directory, there are other changes that require you to use the same container image that you use to build your code with to do the scanning. This means the container used to build your project must have the wrapper in it and be prepared to scan as well.
I normally use an image to perform an action at each stage that is specific to the stage itself. I do not entirely understand why it is necessary to use the same container image for these stages, as there isn’t continuity outside of cached files and artifacts, but that seems to work well.
Keep the Files Around
Between each stage of many CI/CD tools, all files that are not part of the repository or the specified artifacts of that stage are wiped away. Since those other files are wiped away between stages, and stages may be done on different runners, all the other files should not be there and thus it should not matter what image is used. Using a cache, or creating artifacts, will keep the files in them available for future steps.
Up to Date
In order to ensure that your wrapper is the freshest version that your server can provide, it is advisable to add the wrapper from the server as a pre-step to your build stage. Putting all of these things together and we get an efficient formula for setting up SonarQube to work with C, C++, Objective C and C# based projects.
Essence
Assuming a C based project that builds already, the formula is as follows:
- Ensure your build container image or machine is the same as the one you will be using for the sonar scan stage
- Decide on a location for the cache
- Specify that location in the SonarQube properties file. Specify that same location in the wrapper call that surrounds the build call.
- Label the cache directory as an artifact or make it accessible to future stages
Don’t Fret
My experiences setting up SonarQube with projects that needed the CWrapper were troublesome in the beginning. My first issue was that the scanner was looking for files that did not exist as they were not preserved as artifacts. The other issue was that projects using a separate SonarScanner image to perform the scan were having trouble scanning a project, while projects using the same image that included the wrapper and the scanner worked fine.
Conclusion
Using SonarQube is a great way to get your projects under control, but unforeseen complications arise when we try to use it with projects that use C and related languages. Using the lessons learned here will help to set up SonarQube with these kinds of projects.
Example Implementation:
gitlab-ci.yml file contents
#build image above
variables:
WRAPPEROUTPUTDIR: “CWOD” #short for C Wrapper Output Directory
include:
– project: ‘SonarScannerSupportProject’ #Example project in the local gitlab server
ref: main #This line can be omitted
file: ‘CWrapperTemplateFile.yml’ #Example file that would have both template stages
stages:
– build
– scan
#any other stages you use afterwards
build:
stage: build
extends: .install-c-wrapper
script:
– build-wrapper-linux-x86-64 –out-dir $WRAPPEROUTPUTDIR make build #or however you build
artifacts:
paths:
– build
– “$WRAPPEROUTPUTDIR”
sonar-project.properties file contents
sonar.projectName=projectNameInSonarServer
sonar.projectVersion=1.0
sonar.sources=src #this is where the source code files are for scanning
sonar.cfamily.build-wrapper-output=CWOD
sonar.sourceEncoding=UTF-8
sonar.host.url=YourSonarCloud/SonarQubeURL
CWrapperTemplateFile.yml file contents
scan
script
– sonar-scanner
.install-c-wrapper:
before_script:
– curl -o test.jpg private-sonarqube-server.example…
– (other installation steps….)