讓sonar帶著你的C++玩轉devops

讓sonar帶著你的C++玩轉devops

來自專欄飛翔眼的小屋

最近一周一直在搞sonar。

越來越喜歡這個東東,眾所周知,sonar本來是Java寫的,對C++的支持一直是有限的。

上一篇文章,說了怎麼讓sonarcloud去聯動travis的C++工程。

但是光靜態檢查代碼實在是太low了。

我想使用它的更高級的功能,那就是代碼覆蓋率,以及對cppunit報告的聯動。

不得不說,這部分耗費了我一周的時間,谷歌上的資料也坑不少,sonar大部分都是Java的項目,C++的項目在github上的例子多數問題也很多。自己一步一個坑摸過來,確實也不容易。不過當一切完成的時候,感覺還是蠻有成就感的。

首先,要說明一下, sonarcloud只支持CFamily插件,額外的擴展插件是不支持的,默認情況下,如果你使用的是自己安裝的sonar伺服器,CFamily插件如果不是giuhub項目是要收費的。這裡推薦你可以使用另一個免費的sonar代碼檢查插件替換之。

這個插件的地址是:

SonarOpenCommunity/sonar-cxx?

github.com圖標

在強調一次,這個插件,不能與sonarcloud聯動,只能安裝於sonar伺服器上,這個插件的好處是可以支持gcovr的xml報告提交,而且對cppunit更友好一些。最關鍵的它是免費的。

因為我的項目在travis上,所以這個東西不能用。

sonarcloud只支持4種格式的代碼覆蓋率報告。他們是gcov,llvm-gcov, Visual Studio Reports和Bullseye。

後三個需要單獨安裝工具,gcov是為了檢測linux的源代碼覆蓋率而原生的。不需要安裝任何其它的組件,一般linux都會默認安裝。

對應CFamily的組件功能介紹,看這裡。

SonarCFamily Coverage Results Import?

docs.sonarqube.org

我直接使用的是gcov生成報告,但是有幾步之前的工作要做。

首先,你需要獨立寫一個make文件,無論是Cmake也好,或者mpc也好,甚至是直接自己寫的make也罷,你需要獨立一個makegov文件,不與你的程序編譯混淆,為什麼呢?因為gcov需要在g++編譯裡面擴展參數。這個參數會讓你的程序性能下降和內存變大(因為加入了樁),所以gcov不適合壓測環境和正常環境,所以獨立的一個make gcov很重要。

在你的make gcov編譯選項里添加:

-fprofile-arcs -ftest-coverage

這個選項是用來開啟gcov報告的。

當你的程序編譯完成的時候,你會發現你的.o(程序編譯中間文件)的目錄下,會對應每個.o多了一個.gcno文件。

如果你看到這個,說明第一步你已經成功了。

第二步,就是完整運行你的程序。

這裡注意,不能使用kill -9 或者類似的命令終止程序,一定要讓程序自然退出。如果強行終止程序的話,你的gcba文件是不會生成出來的。

這裡要多說一句,如果我寫的是一個伺服器程序,怎麼合理關閉呢,這裡你需要增加對信號量的抓取,也就是說,你可以用killall XXX的方式關閉你的程序,這時候你的程序會收到一個signal。你處理它讓你的程序合理的關閉即可。(優雅的關閉)

正常的關閉後,你會在你的.o文件夾上看到一些新的文件。這個文件是.gcba,注意,這個文件也是和你的.o一一對應的。

類似這樣:

ConnectClient.o ConnectClient.gcnoConnectClient.gcda

當你看到這些,gcov實際就成功了大半。我的理解是,編譯器生成gcno的文件是你的代碼中所有樁的位置,gcba是程序實際執行到關閉後的,所有的樁被調用的次數記錄。

剩下的,就是使用gcov指令生成對應的代碼覆蓋率報告了。gcov指令會合併gcno和gcba文件生成一個.gcov的文件報告。注意,這裡也是和.o一一對應的。

這裡要強調一點。

gcov指令,必須在你的程序運行目錄運行,在此之外運行都會報找不到文件的錯誤。也就是說,如果比較合理的配置,你的.o會在一個專門的目錄,你的cpp會在另一個專門的目錄。那麼你應該在你的程序運行目錄上運行gcov,切記。

類似這樣:

- gcov -r -o ./.obj ../*.cpp

這裡後面-o 的路徑,和最後源代碼的路徑,都必須是相對於你的可執行文件路徑的相對路徑。

那麼你也許會問,如果我的代碼會有一堆的子目錄(一般比較大的項目,代碼目錄都是嚴格區分的,不會混在一起的,怎麼辦?)

那麼你可以一一指定它們。

例如:

- gcov -r -o ./.obj ../*.cpp - gcov -r -o ./.obj ../Common/*.cpp - gcov -r -o ./.obj ../Console/*.cpp - gcov -r -o ./.obj ../FileTest/*.cpp - gcov -r -o ./.obj ../TinyXML/*.cpp - gcov -r -o ./.obj ../PacketParse/*.cpp - gcov -r -o ./.obj ../LogSystem/*.cpp - gcov -r -o ./.obj ../Message/*.cpp - gcov -r -o ./.obj ../Reactor/*.cpp - gcov -r -o ./.obj ../Mail/*.cpp

這裡要說明的是,我目前還沒有找到直接遍歷子目錄一句話的方式。

我目前使用的是這種比較笨的方式,因為*.cpp只能支持本級別的文件夾,所以有子文件夾的話,要額外指定出來。

好了,生成了所有的gcov文件。我們就需要sonarcloud去接收我們的文件了。

怎麼寫呢?

在你的sonar-project.properties 文件中添加如下行:

sonar.cfamily.gcov.reportsPath=./Linux_Bin

這裡注意,這個路徑是你的gcov文件所在的路徑。

這樣,sonarcloud就可以接收你的代碼覆蓋率報告並展現出來了。

類似這樣:

Loading...?

sonarcloud.io

你可以點擊你的代碼覆蓋率,查看自己的代碼詳細覆蓋的情況。

那些代碼行在運行過程中,沒有被執行到。

或許你會問,我寫的是伺服器程序,沒法做到完整的全覆蓋呀。

其實這只是一個借口,想做到肯定是可以做到的,那麼下一講,我來說說,怎麼用cppunit來配合你的代碼覆蓋率檢查。

推薦閱讀:

雲時代的來臨及各種自動化平台的湧現,運維工程師的工作還有價值嗎?
大洋電機綜合運維監控項目經典案例
面試時到底在面什麼?
從 0 開始了解 Docker

TAG:運維 | DevOps | 軟體開發 |