diff --git a/ci-scripts/Jenkinsfile-GitLab-Container b/ci-scripts/Jenkinsfile-GitLab-Container
index d75b1888d4..721c89b77f 100644
--- a/ci-scripts/Jenkinsfile-GitLab-Container
+++ b/ci-scripts/Jenkinsfile-GitLab-Container
@@ -219,28 +219,6 @@ pipeline {
}
}
}
- stage ("cppcheck") {
- steps {
- script {
- triggerSlaveJob ('RAN-cppcheck', 'cppcheck')
- }
- }
- post {
- always {
- script {
- // Using a unique variable name for each test stage to avoid overwriting on a global variable
- // due to parallel-time concurrency
- cppcheckStatus = finalizeSlaveJob('RAN-cppcheck')
- }
- }
- failure {
- script {
- currentBuild.result = 'FAILURE'
- failingStages += cppcheckStatus
- }
- }
- }
- }
stage ("ARM-Cross-Compile") {
steps {
script {
diff --git a/ci-scripts/cls_oai_html.py b/ci-scripts/cls_oai_html.py
index 2f59ae1852..66d4b804ae 100644
--- a/ci-scripts/cls_oai_html.py
+++ b/ci-scripts/cls_oai_html.py
@@ -379,90 +379,6 @@ class HTMLManagement():
self.htmlFile.write(' \n')
self.htmlFile.close()
- def CreateHtmlTestRowCppCheckResults(self, CCR):
- if (self.htmlFooterCreated or (not self.htmlHeaderCreated)):
- return
- self.htmlFile = open('test_results.html', 'a')
- vId = 0
- for version in CCR.versions:
- self.htmlFile.write('
\n')
- self.htmlFile.write(' | Results for cppcheck v ' + version + ' | \n')
- self.htmlFile.write('
\n')
- self.htmlFile.write(' \n')
- self.htmlFile.write(' | \n')
- self.htmlFile.write(' NB ERRORS | \n')
- if CCR.nbErrors[vId] == 0:
- myColor = 'lightgreen'
- elif CCR.nbErrors[vId] < 20:
- myColor = 'orange'
- else:
- myColor = 'lightcoral'
- self.htmlFile.write(' ' + str(CCR.nbErrors[vId]) + ' | \n')
- self.htmlFile.write('
\n')
- self.htmlFile.write(' \n')
- self.htmlFile.write(' | \n')
- self.htmlFile.write(' NB WARNINGS | \n')
- if CCR.nbWarnings[vId] == 0:
- myColor = 'lightgreen'
- elif CCR.nbWarnings[vId] < 20:
- myColor = 'orange'
- else:
- myColor = 'lightcoral'
- self.htmlFile.write(' ' + str(CCR.nbWarnings[vId]) + ' | \n')
- self.htmlFile.write('
\n')
- self.htmlFile.write(' \n')
- self.htmlFile.write(' | ----------------- | \n')
- self.htmlFile.write('
\n')
- self.htmlFile.write(' \n')
- self.htmlFile.write(' | \n')
- self.htmlFile.write(' Memory leak | \n')
- self.htmlFile.write(' ' + str(CCR.nbMemLeaks[vId]) + ' | \n')
- self.htmlFile.write('
\n')
- self.htmlFile.write(' \n')
- self.htmlFile.write(' | \n')
- self.htmlFile.write(' Possible null pointer deference | \n')
- self.htmlFile.write(' ' + str(CCR.nbNullPtrs[vId]) + ' | \n')
- self.htmlFile.write('
\n')
- self.htmlFile.write(' \n')
- self.htmlFile.write(' | \n')
- self.htmlFile.write(' Uninitialized variable | \n')
- self.htmlFile.write(' ' + str(CCR.nbUninitVars[vId]) + ' | \n')
- self.htmlFile.write('
\n')
- self.htmlFile.write(' \n')
- self.htmlFile.write(' | \n')
- self.htmlFile.write(' Undefined behaviour shifting | \n')
- self.htmlFile.write(' ' + str(CCR.nbTooManyBitsShift[vId]) + ' | \n')
- self.htmlFile.write('
\n')
- self.htmlFile.write(' \n')
- self.htmlFile.write(' | \n')
- self.htmlFile.write(' Signed integer overflow | \n')
- self.htmlFile.write(' ' + str(CCR.nbIntegerOverflow[vId]) + ' | \n')
- self.htmlFile.write('
\n')
- self.htmlFile.write(' \n')
- self.htmlFile.write(' | | \n')
- self.htmlFile.write('
\n')
- self.htmlFile.write(' \n')
- self.htmlFile.write(' | \n')
- self.htmlFile.write(' Printf formatting issues | \n')
- self.htmlFile.write(' ' + str(CCR.nbInvalidPrintf[vId]) + ' | \n')
- self.htmlFile.write('
\n')
- self.htmlFile.write(' \n')
- self.htmlFile.write(' | \n')
- self.htmlFile.write(' Modulo result is predetermined | \n')
- self.htmlFile.write(' ' + str(CCR.nbModuloAlways[vId]) + ' | \n')
- self.htmlFile.write('
\n')
- self.htmlFile.write(' \n')
- self.htmlFile.write(' | \n')
- self.htmlFile.write(' Opposite Condition -> dead code | \n')
- self.htmlFile.write(' ' + str(CCR.nbOppoInnerCondition[vId]) + ' | \n')
- self.htmlFile.write('
\n')
- self.htmlFile.write(' \n')
- self.htmlFile.write(' | \n')
- self.htmlFile.write(' Wrong Scanf Nb Args | \n')
- self.htmlFile.write(' ' + str(CCR.nbWrongScanfArg[vId]) + ' | \n')
- self.htmlFile.write('
\n')
- vId += 1
-
def CreateHtmlTestRowPhySimTestResult(self, testSummary, testResult):
if (self.htmlFooterCreated or (not self.htmlHeaderCreated)):
return
diff --git a/ci-scripts/cls_static_code_analysis.py b/ci-scripts/cls_static_code_analysis.py
index 603df581e3..4e388847c7 100644
--- a/ci-scripts/cls_static_code_analysis.py
+++ b/ci-scripts/cls_static_code_analysis.py
@@ -27,28 +27,6 @@ import constants as CONST
import cls_cmd
from cls_ci_helper import archiveArtifact
-#-----------------------------------------------------------
-# Class Declaration
-#-----------------------------------------------------------
-class CppCheckResults():
-
- def __init__(self):
-
- self.variants = ['bionic', 'focal']
- self.versions = ['','']
- self.nbErrors = [0,0]
- self.nbWarnings = [0,0]
- self.nbNullPtrs = [0,0]
- self.nbMemLeaks = [0,0]
- self.nbUninitVars = [0,0]
- self.nbInvalidPrintf = [0,0]
- self.nbModuloAlways = [0,0]
- self.nbTooManyBitsShift = [0,0]
- self.nbIntegerOverflow = [0,0]
- self.nbWrongScanfArg = [0,0]
- self.nbPtrAddNotNull = [0,0]
- self.nbOppoInnerCondition = [0,0]
-
class StaticCodeAnalysis():
def __init__(self):
@@ -60,104 +38,6 @@ class StaticCodeAnalysis():
self.ranTargetBranch = ''
self.eNBSourceCodePath = ''
- def CppCheckAnalysis(self, ctx, node, HTML):
- if self.ranRepository == '' or self.ranBranch == '' or self.ranCommitID == '':
- HELP.GenericHelp(CONST.Version)
- sys.exit('Insufficient Parameter')
- lSourcePath = self.eNBSourceCodePath
-
- if not lSourcePath or not node:
- raise ValueError(f"{lSourcePath=} {node=}")
- logging.debug('Building on server: ' + node)
- cmd = cls_cmd.getConnection(node)
- # on RedHat/CentOS .git extension is mandatory
- result = re.search(r'([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository)
- if result is not None:
- full_ran_repo_name = self.ranRepository.replace('git/', 'git')
- else:
- full_ran_repo_name = self.ranRepository + '.git'
-
- cmd.cd(lSourcePath)
- logDir = f'{lSourcePath}/cmake_targets/log'
- cmd.run(f'mkdir -p {logDir}')
- cmd.run('docker image rm oai-cppcheck:bionic oai-cppcheck:focal')
- cmd.run(f'sed -e "s@xenial@bionic@" {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.xenial > {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.bionic')
- cmd.run(f'docker build --tag oai-cppcheck:bionic --file {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.bionic . > {logDir}/cppcheck-bionic.txt 2>&1')
- cmd.run(f'sed -e "s@xenial@focal@" {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.xenial > {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.focal')
- cmd.run(f'docker build --tag oai-cppcheck:focal --file {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.focal . > {logDir}/cppcheck-focal.txt 2>&1')
- cmd.run('docker image rm oai-cppcheck:bionic oai-cppcheck:focal')
-
- bionic = archiveArtifact(cmd, ctx, f'{logDir}/cppcheck-bionic.txt')
- focal = archiveArtifact(cmd, ctx, f'{logDir}/cppcheck-focal.txt')
- cmd.close()
-
- CCR = CppCheckResults()
- CCR_ref = CppCheckResults()
- vId = 0
- for variant in CCR.variants:
- filename = ctx.baseFilename() + '-cppcheck-'+ variant + '.txt'
- logging.info(f"will check file '{filename}'")
- if not os.path.isfile(filename):
- raise FileNotFoundError(f"{filename} is not a file")
- else:
- xmlStart = False
- with open(filename, 'r') as logfile:
- for line in logfile:
- ret = re.search(r'cppcheck version="(?P[0-9\.]+)"', str(line))
- if ret is not None:
- CCR.versions[vId] = ret.group('version')
- if re.search('RUN cat cmake_targets/log/cppcheck.xml', str(line)) is not None:
- xmlStart = True
- if xmlStart:
- if re.search('severity="error"', str(line)) is not None:
- CCR.nbErrors[vId] += 1
- if re.search('severity="warning"', str(line)) is not None:
- CCR.nbWarnings[vId] += 1
- if re.search('id="memleak"', str(line)) is not None:
- CCR.nbMemLeaks[vId] += 1
- if re.search('id="nullPointer"', str(line)) is not None:
- CCR.nbNullPtrs[vId] += 1
- if re.search('id="uninitvar"', str(line)) is not None:
- CCR.nbUninitVars[vId] += 1
- if re.search('id="invalidPrintfArgType_sint"|id="invalidPrintfArgType_uint"', str(line)) is not None:
- CCR.nbInvalidPrintf[vId] += 1
- if re.search('id="moduloAlwaysTrueFalse"', str(line)) is not None:
- CCR.nbModuloAlways[vId] += 1
- if re.search('id="shiftTooManyBitsSigned"', str(line)) is not None:
- CCR.nbTooManyBitsShift[vId] += 1
- if re.search('id="integerOverflow"', str(line)) is not None:
- CCR.nbIntegerOverflow[vId] += 1
- if re.search('id="wrongPrintfScanfArgNum"|id="invalidScanfArgType_int"', str(line)) is not None:
- CCR.nbWrongScanfArg[vId] += 1
- if re.search('id="pointerAdditionResultNotNull"', str(line)) is not None:
- CCR.nbPtrAddNotNull[vId] += 1
- if re.search('id="oppositeInnerCondition"', str(line)) is not None:
- CCR.nbOppoInnerCondition[vId] += 1
- vMsg = ''
- vMsg += '======== Variant ' + variant + ' - ' + CCR.versions[vId] + ' ========\n'
- vMsg += ' ' + str(CCR.nbErrors[vId]) + ' errors\n'
- vMsg += ' ' + str(CCR.nbWarnings[vId]) + ' warnings\n'
- vMsg += ' -- Details --\n'
- vMsg += ' Memory leak: ' + str(CCR.nbMemLeaks[vId]) + '\n'
- vMsg += ' Possible null pointer deference: ' + str(CCR.nbNullPtrs[vId]) + '\n'
- vMsg += ' Uninitialized variable: ' + str(CCR.nbUninitVars[vId]) + '\n'
- vMsg += ' Undefined behaviour shifting: ' + str(CCR.nbTooManyBitsShift[vId]) + '\n'
- vMsg += ' Signed integer overflow: ' + str(CCR.nbIntegerOverflow[vId]) + '\n'
- vMsg += '\n'
- vMsg += ' Printf formatting issue: ' + str(CCR.nbInvalidPrintf[vId]) + '\n'
- vMsg += ' Modulo result is predetermined: ' + str(CCR.nbModuloAlways[vId]) + '\n'
- vMsg += ' Opposite Condition -> dead code: ' + str(CCR.nbOppoInnerCondition[vId]) + '\n'
- vMsg += ' Wrong Scanf Nb Args: ' + str(CCR.nbWrongScanfArg[vId]) + '\n'
- for vLine in vMsg.split('\n'):
- logging.debug(vLine)
- vId += 1
-
- HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK)
- HTML.CreateHtmlTestRowCppCheckResults(CCR)
- logging.info('\u001B[1m Static Code Analysis Pass\u001B[0m')
-
- return True
-
def LicenceAndFormattingCheck(self, ctx, node, HTML):
# Workspace is no longer recreated from scratch.
# It implies that this method shall be called last within a build pipeline
diff --git a/ci-scripts/docker/Dockerfile.cppcheck.xenial b/ci-scripts/docker/Dockerfile.cppcheck.xenial
deleted file mode 100644
index fcb7f814ae..0000000000
--- a/ci-scripts/docker/Dockerfile.cppcheck.xenial
+++ /dev/null
@@ -1,41 +0,0 @@
-# SPDX-License-Identifier: LicenseRef-CSSL-1.0
-
-#---------------------------------------------------------------------
-#
-# Dockerfile for the Open-Air-Interface BUILD service
-# Valid for Ubuntu 22.04
-#
-#---------------------------------------------------------------------
-FROM ubuntu:xenial AS oai-cppcheck
-
-
-ENV DEBIAN_FRONTEND=noninteractive
-
-RUN apt-get update && \
- apt-get upgrade --yes && \
- apt-get install --yes \
- build-essential \
- vim \
- cppcheck
-
-WORKDIR /oai-ran
-COPY . .
-
-WORKDIR /oai-ran/common/utils/T
-RUN make
-
-WORKDIR /oai-ran
-RUN mkdir -p cmake_targets/log && \
- cppcheck --enable=warning --force --xml --xml-version=2 \
- --inline-suppr \
- -i openair1/PHY/CODING/nrLDPC_decoder/nrLDPC_decoder.c \
- --suppressions-list=ci-scripts/cppcheck_suppressions.list \
- -I common/utils \
- -I openair3/NAS/COMMON/UTIL \
- -j`nproc` . 2> cmake_targets/log/cppcheck.xml 1> cmake_targets/log/cppcheck_build.txt
-
-RUN grep -E -c 'severity="error' cmake_targets/log/cppcheck.xml
-
-RUN grep -E -c 'severity="warning' cmake_targets/log/cppcheck.xml
-
-RUN cat cmake_targets/log/cppcheck.xml
diff --git a/ci-scripts/main.py b/ci-scripts/main.py
index 27cf36bcf8..906f73f433 100644
--- a/ci-scripts/main.py
+++ b/ci-scripts/main.py
@@ -198,9 +198,6 @@ def ExecuteActionWithParam(action, ctx, node):
elif action == 'LicenceAndFormattingCheck':
success = SCA.LicenceAndFormattingCheck(ctx, node, HTML)
- elif action == 'Cppcheck_Analysis':
- success = SCA.CppCheckAnalysis(ctx, node, HTML)
-
elif action == 'Push_Local_Registry':
tag_prefix = test.findtext('tag_prefix') or ""
success = CONTAINERS.Push_Image_to_Local_Registry(node, HTML, tag_prefix)
diff --git a/ci-scripts/xml_class_list.yml b/ci-scripts/xml_class_list.yml
index 52b44e0e96..d762c4b316 100755
--- a/ci-scripts/xml_class_list.yml
+++ b/ci-scripts/xml_class_list.yml
@@ -23,7 +23,6 @@
- Deploy_Object
- Stop_Object
- Undeploy_Object
- - Cppcheck_Analysis
- Deploy_Run_OC_PhySim
- Build_Deploy_PhySim
- LicenceAndFormattingCheck
diff --git a/ci-scripts/xml_files/cppcheck.xml b/ci-scripts/xml_files/cppcheck.xml
deleted file mode 100644
index 061c39b5b8..0000000000
--- a/ci-scripts/xml_files/cppcheck.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
- build-tab
- CPPCHECK Analysis
- wrench
-
-
- Create_Workspace
- Create new Workspace
- carabe
-
-
-
- Cppcheck_Analysis
- Static Code Analysis with cppcheck
- carabe
-
-
-
diff --git a/doc/README.md b/doc/README.md
index 37fe2a980d..d1dfc2dc4d 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -135,6 +135,7 @@ The other SDRs (AW2S, LimeSDR, ...) have no READMEs.
- [clang-format.md](./clang-format.md): how to format the code. See also the
next entry for an error detection tool.
- [formatting](../tools/formatting/README.md) is a clang-format error detection tool
+- [cppcheck](../tools/cppcheck/README.md) for static code analysis
- [sanitizers.md](./dev_tools/sanitizers.md): how to run with ASan/UBSan/MemSAN/TSan
- [iwyu](../tools/iwyu/README.md) is a tool to detect `#include` errors
- [docker-dev-env](../tools/docker-dev-env/README.md) is a ubuntu24 docker development environment
diff --git a/doc/TESTBenches.md b/doc/TESTBenches.md
index d348ffe355..361d774215 100644
--- a/doc/TESTBenches.md
+++ b/doc/TESTBenches.md
@@ -96,10 +96,6 @@ information on how the images are built.
- orion: Cross-compilation from Intel to ARM
- base image from `Dockerfile.base.ubuntu.cross-arm64`
- build image from `Dockerfile.build.ubuntu.cross-arm64` (no target images)
-- [RAN-cppcheck](https://jenkins-oai.eurecom.fr/job/RAN-cppcheck/)
- ~BUILD-ONLY ~4G-LTE ~5G-NR ~nrUE
- - bellatrix
- - performs static code analysis, currently not actively enforced
- [RAN-RHEL8-Cluster-Image-Builder](https://jenkins-oai.eurecom.fr/job/RAN-RHEL8-Cluster-Image-Builder/)
~BUILD-ONLY ~4G-LTE ~5G-NR ~nrUE
- cluster (`Asterix-OC-oaicicd-session` resource): RHEL image build using the OpenShift Cluster (using gcc/clang)
diff --git a/tools/cppcheck/README.md b/tools/cppcheck/README.md
new file mode 100644
index 0000000000..414f35a68a
--- /dev/null
+++ b/tools/cppcheck/README.md
@@ -0,0 +1,28 @@
+
+
+# Static code analysis
+
+How to run static code analysis tool(s).
+
+[[_TOC_]]
+
+## cppcheck Overview
+
+This can run a (dockerized) cppcheck static code analysis. The docker-compose
+file represents the status as run as in the CI until it's removal (because it
+was not enforced). It is now here so that people could use it to find bugs (and
+use the tool more easily).
+
+## cppcheck Usage
+
+From the openairinterface5g root, simply run
+
+ ./tools/cppcheck/run-cppcheck.sh
+
+This will run cppcheck with options listed in this file, assuming you have it
+installed. Alternatively, you can run the CI version as of Ubuntu 20 with
+
+ cd tools/cppcheck && docker compose up --no-log-prefix
+
+You can upgrade to a newer version by modifying the `FROM` line in the
+docker-compose file.
diff --git a/tools/cppcheck/docker-compose.yaml b/tools/cppcheck/docker-compose.yaml
new file mode 100644
index 0000000000..8b8c5878d0
--- /dev/null
+++ b/tools/cppcheck/docker-compose.yaml
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: MIT
+
+services:
+ cppcheck:
+ image: cppcheck
+ build:
+ dockerfile_inline: |
+ FROM ubuntu:focal
+ RUN DEBIAN_FRONTEND=noninteractive apt update && apt install -y cppcheck
+ volumes:
+ - ../../:/code/
+ command:
+ bash -c "cd /code/ && ./tools/cppcheck/run-cppcheck.sh"
+
diff --git a/tools/cppcheck/run-cppcheck.sh b/tools/cppcheck/run-cppcheck.sh
new file mode 100755
index 0000000000..5a5df0c752
--- /dev/null
+++ b/tools/cppcheck/run-cppcheck.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+# SPDX-License-Identifier: MIT
+
+cppcheck --enable=warning --force \
+ --inline-suppr \
+ --quiet \
+ -i openair1/PHY/CODING/nrLDPC_decoder/nrLDPC_decoder.c \
+ --suppressions-list=tools/cppcheck/suppressions.list \
+ -I common/utils \
+ -I openair3/NAS/COMMON/UTIL \
+ -j`nproc` .
diff --git a/ci-scripts/cppcheck_suppressions.list b/tools/cppcheck/suppressions.list
similarity index 100%
rename from ci-scripts/cppcheck_suppressions.list
rename to tools/cppcheck/suppressions.list