【DIY】DIY一個代碼質量管理平臺


DIY一個代碼質量管理平臺

這不是一篇技術文章,所以請不要把她當成技術文章來讀。只是想表達一個開發人員對美好生活的憧憬。

背景

代碼質量,衡量開發人員成果物的重要標準。那麼,作爲開發人員如何能交付高質量代碼呢?不好意思,我也不知道正確答案:(。不過,我們代碼要經過,“靜態掃描、動態掃描、單元測試、集成測試、系統測試、性能測試……”,一系列的流程,我想這點大家應該是認同的。
我們再具體一點,一個開發人員從編碼到交付集成測試至少需要經過,靜態掃描、單元測試、動態掃描,並提供各階段的成果物。各階段成果物包括:靜態檢查結果、動態檢查結果、單元測試代碼、單元測試結果、單元測試覆蓋率等等。
如此多的交付內容,是否有統一的規範、格式以及審覈流程呢?根據各個項目具體要求這部分流程一定會有,但形式可能會有所不同,大多可能是以文本或表格的形式存在。但不管以哪種形式都不夠直觀(如果是直接看各種檢查工具的命令行輸出,我想一定會有頭暈、眼花等不同程度的生理反應:),我們總是期待我們世界會變得更加美好。
那麼……,好吧……,讓我們回到主題,《DIY一個代碼質量管理平臺》。

原材料

—— 君子生非異也,善假於物也。
人類社會的飛速發展主要仰仗於對已有資源、工具,經驗的利用和改造,不斷創造新的資源、新的工具並積累更多的寶貴經驗,並把她們不斷的傳承下去。
所謂原材料本質上也是一系列的工具,把它們定位成原材料是因爲要對它們的輸出結果進行再加工。

開始動手前,讓我們先了解一下原材料需要哪些,當然,所有的原材料都是免費的,所以不用太多考慮成本。

原材料清單如下:

靜態檢查(Cppcheck)

先看看市面上C/C++靜態檢查的原材料都有哪些?PC-Lint/FlexeLint、Splint、Cppcheck。
1.PC-Lint/FlexeLint:功能很強大。但是是商用的,要錢。
2.Splint:不要錢,但只適用於C。
3.Cppcheck:不要錢,C++可以用,而且可以和 jenkins集成,也可以和許多IDE進行集成(Eclipse,Codeblock,VS)。
·Cppcheck放進購物車`。

動態檢查(valgrind)

動態檢查材料選擇起來就比較簡單了,個人認爲valgrind是功能最強大的,開源的動態檢查工具,沒有之一。有不同見解的小夥伴可以交流一下(其實我是想說,不服來辯)。
valgrind不是一個工具,它是一個工具集,包括以下工具包:

Callgrind:它主要用來檢查程序中函數調用過程中出現的問題。
Cachegrind:它主要用來檢查程序中緩存使用出現的問題。
Helgrind:它主要用來檢查多線程程序中出現的競爭問題。
Massif:它主要用來檢查程序中堆棧使用中出現的問題。
Extension:可以利用core提供的功能,自己編寫特定的內存調試工具

valgrind添加到購物車
PS. kachegrind這貨不錯,感興趣的可以試試。

單元測試(cppunit)

選單元測試材料就有點鬱悶了,我的首選是gtest,但由於要傳承之前項目積累的成果,所以,退而求其次,cppunit進入了購物車,gtest暫時收藏
本質上沒有太大差別,但setup()/teardown(),選擇性執行用例,包括打樁,gtest的優勢還是比較明顯的。

代碼覆蓋率(gcovr/lcov)

這個也沒有必要多說,可選擇的不多。我們主要是用lcov,沒有別的原因,界面友好,幾乎不需要做加工。
再推薦一個gcovr,是python寫的C/C++的覆蓋率統計工具,效果和lcov差不多,可以考慮用gcovr來代替lcov,原因是lcov對so的支持不好,恰巧,我們項目使用了大量的so。必要時更換原材料。
lcov加入購物車,gcovr添加收藏

工具

—— 工慾善其事必先利其器。
原材料準備好了,找幾個能加工這些材料的工具。這塊不多說,工具太多,找幾個順手來用就可以了。

先選兩瓶膠水(語言);shell,python
再來一個能放在口袋裏的數據庫:sqlite
找個盒子把我們作品裝起來:Django/Beego
最後弄個漂亮點的包裝:bootstrap,jQuery,jqplot

開始動手

東北有句話,“能動手的,就儘量少說話。”
我們的目標是用最短的時間,最簡單的方法,實現一個實用的界面友好日常工具。
好吧……,儘量少說話!開工!!!

第一步 初加工

所謂初加工就是利用原材料自帶的屬性,通過參數,命令行選項得到我們想好的半成品。

導出cppcheck檢查結果

cppcheck --enable=all --xml you_code_dir 2 > check.xml

以xml格式保存靜態檢查結果。

導出valgrind檢查結果

valgrind --tool=memcheck --leak-check-full --xml=yes --xml-file=valgrind.xml

以xml格式保存動態檢查結果。

導出單元測試結果

上面說了,cppunit沒有gtest那麼好用了。需要改代碼才能輸出我們想要的結果。

std::ofstream xmlFileOut("unit.xml");
CppUnit::XmlOutputter xmlOut(&result, xmlFileOut);

統計代碼覆蓋率

# 先要加兩個編譯選項
-fprofile-arcs -ftest-coverage

# 再鏈一個庫
-lgcov

編譯執行後導出結果,直接用lcov

lcov -c -o cov.info -d ${.gcno .gcda目錄} -b ${源代碼目錄}

# 生成html
```bash
genhtml cov.info -o cov_html

至此,我們需要的靜態掃描,動態掃描,單元測試,測試覆蓋率的半成品結果都有了。

第二步 深加工

—— 言治骨角者,既切之而復磋之;治玉石者,既琢之而復磨之;治之已精,而益求其精也。

那麼,我們對上面的半成品再切磋琢磨一番。

數據入庫

個人習慣是把有規律的數據放到數據庫裏面,一是歸類保存方便,二是用sql查找要比grep更實用。如果有相應的客戶端,那麼你還會有個友好的操作界面。
處了代碼覆蓋率是html,先不管它。其它都是xml。

拿出一瓶標籤是python膠水,把xml粘到sqlit上。

#!/usr/bin/python
#coding=utf8
"""
# Author: frank
# Created Time : 2017-08-26 14:46:49

# File Name: import_cppcheck.py
# Description:

"""

from xml.dom.minidom import parse
import sqlite3
import os
import sys, getopt

def help():
    print '''usage:
    -h help
    -f cppcheck result file *.xml_file
    -d import sqlite3 db file *.db'''
    sys.exit(1)

opts, args = getopt.getopt(sys.argv[1:], "hf:d:")

xml_file = ''
db_file = ''

for op, value in opts:
    if op == "-f":
        xml_file = value
    elif op == "-d":
        db_file = value
    elif op == "-h":
        help()

if xml_file == '' or db_file == '':
    help()

# if os.path.exists(db_file):
#     os.remove(db_file)

conn = sqlite3.connect(db_file)
c = conn.cursor()

c.execute('''CREATE TABLE CPPCHECK_RESULT 
        (ID INT PRIMARY KEY     NOT NULL,
        FILE           TEXT,     
        LINE           INT, 
        TYPE           TEXT     NOT NULL,
        SEVERITY       TEXT     NOT NULL,
        MSG            TEXT     NOT NULL);
        ''')

id = 0
#打開xml文件
dom = parse(xml_file)

results = dom.documentElement
errors = results.getElementsByTagName("error")
for error in errors:
    id = id + 1
    if error.getAttribute("file"):
        f_file = error.getAttribute("file")
    else:
        f_file = ''

    if error.getAttribute("line"):
        f_line = error.getAttribute("line")
    else:
        f_line = 0
    c.execute ('''INSERT INTO CPPCHECK_RESULT (ID,FILE,LINE,TYPE,SEVERITY,MSG)  
VALUES (''' + str(id) + ''',"''' + f_file + '''",''' +  str(f_line) + ''',"''' + error.getAttribute("id") + '''","''' +  error.getAttribute("severity") + '''","''' +  error.getAttribute("msg") + '''");''')


conn.commit()
conn.close()

很容易,去掉註釋也就50~60行。這個是將cppcheck結果導入sqlite的代碼。另外兩個xml的導入,如法炮製。稍微調整一下表結構,分分鐘就可以搞定。
但有人會問,爲啥分開寫呢?一個腳本把3個xml都讀到sqlite不行嗎?分開寫,可以分開用。拿出來也是個單獨的工具。
那麼,問題來了,分開寫調用的時候就需要調用3次,多麻煩!好吧,拿出另一瓶標籤是shell的膠水。

#########################################################################
# File Name: import.sh
# Author: Frank
# mail: [email protected]
# Created Time: 2017-08-26 21:51:15
#########################################################################
#!/bin/bash

set -e

function help()
{
    echo "usage:
    -h help
    -c cppcheck result file *.xml
    -u cppunit result file *.xml
    -v valgrind result file *.xml
    -d import sqlite3 db file *.db"
}

while getopts "c:u:v:d:h" arg
do
    case $arg in
        c)
        check_file=$OPTARG
        ;;
        u)
        unit_file=$OPTARG
        ;;
        v)
        valgrind_file=$OPTARG
        ;;
        d)
        db_file=$OPTARG
        ;;
        h)
        help
        exit 1
        ;;
        ?)
        help
        exit 1
        ;;
    esac
done


if  [ ! -n "$check_file" -o ! -n "$unit_file" -o ! -n "$valgrind_file" -o ! -n "$db_file" ] ;then
    help
    exit 1
fi

rm -rf $db_file


python import_cppcheck.py -f $check_file -d $db_file
python import_cppunit.py -f $unit_file -d $db_file
python import_valgrind.py -f $valgrind_file -d $db_file

粘上了,一個腳本搞定。

展示結果

把各種檢查結果寫到sqlite不是最終目的,要把它展示出來。
既簡單又快的方式,B/S結構是首選。做這種小東西其實php是最佳選擇,因爲*“php是世界上最好的編程語言”*,-這個段子大家應該聽過。用php的一個小問題就是部署稍稍有一點點麻煩(其實已經很簡單了,但有更簡單的,我們一定不用簡單的,這個是大原則!),雖然httpd服務linux默認安裝了,但還免不了一些配置。

好吧……,再見php!那用啥呢?Django,nodejs還是beego,部署簡單,不需要服務器,你想要的腳手架功能它們都有。
我先選了了Django,必要時還可以把sqlite這層去掉,直接python解析xml然後展示,看上去不錯!

說幹就幹。

pip install django -g

看似簡單的命令,但在向內網遷移的時候……,一萬頭XXX在我胸口奔騰而過。(XXX這玩意兒學名叫羊駝),各種包依賴,各種包版本問題,一個一個下載,一個一個手動安裝。本來是一根網線加一條pip命令可以解決的問題,結果鼓搗了一個多小時。
好吧……,不帶你玩了!有這時間都搞完了~~

穩妥點,不需要遷移,外網搞完了直接可以拿到內網用,妥妥的用Go,直接發佈二進制可執行文件總行了吧。

說幹就幹。儘量少說話,直接上圖吧。

在這裏插入圖片描述

代碼覆蓋率展示方面,lcov做了我們想要的一切,不做任何修改,直接拿來用。在Beego裏面直接設置其靜態目錄爲lcov導出html的文件夾目錄。

beego.SetStaticPath("/datas/cov_html","lcov")

在這裏插入圖片描述

和Django,nodejs一樣Beego做這上面的事情簡單得令人髮指。只需以下步驟:

  1. 寫個router指定跳轉的url與controler的關聯
  2. 利用自帶的orm,將表映射爲Go的struct,並寫一些簡單的查詢,完成model
  3. 通過controler調用model,處理數據,並在view中展示。
  4. 編寫view,靜態html加上template,將數據進行展示。
    典型的MVC架構(這裏不講技術,況且我現在是個C++程序員:))。

至此,數據展示部分完成。“太low了吧,這玩意兒用你做?弄個sqlite客戶端就完了唄。”
好吧……,你說的對。但我們總是嚮往更加美好的世界……。

包裝

上面工序完成後幾乎和Excel沒啥區別,那就讓它更像一個工具吧。對它進行包裝。

可能看到這大家會有個疑問,如果只是展示,那爲什麼要把數據存到sqlite裏面,直接用xml一樣可以實現web展示功能。沒錯,但我並不想只是單純的展示,對這些數據指標進行分析是終極目標之一。
先來幾個我們關心的指標吧:

  • 靜態檢查問題數
  • 靜態檢查問題分佈情況
  • 測試用例失敗數
  • 測試通過率
  • 代碼覆蓋率
  • 動態檢查問題數
    這些東西很容易搞定,因爲有sql:)。

數據方面利用數據庫能夠很方便的進行統計分析。如果有新的指標要統計,只要sqlite裏面有數據源,也是很容易實現的。

另一個層面的包裝就是美工了,這個我不專業,但js還是略知一二。其實不知道也沒關係,現成的東西一抓一大把,拿來就用,我們的原則是有現成的就對不去造輪子。

bootstrap,jqplot……,拿來就用。
儘量少說話,上圖~

在這裏插入圖片描述

至此,產品功能基本完成。

裝箱

收工前還有一個重要的事情,裝箱發佈。前面一直在提“拿來就用”這個詞,別人的東西我們拿來直接用,同樣,我們的東西要讓別人也能夠方便的“拿去就用”。那麼我們完成最後一道工序,裝箱發佈。

瞭解Beego的同學一定都知道Bee這個工具,她提供了一些非常方便的工具,其中一個就是打包。
在你的工程目錄下執行

bee pack

她會打包生成一個壓縮包,包括可執行文件、配置文件、靜態文件和自定義文件/文件夾,解壓後直接可以運行。
另外,bee bale命令可以將所有的靜態文件變成一個變量申明文件,全部編譯到二進制文件裏面,用戶發佈的時候攜帶靜態文件(該命令尚未正式發佈)。
你會發現,世界真的是很美好,有如此多的工具可供我們是用,一條命令,分分鐘搞定。

另外一件事就是寫個一鍵腳本。看看腳本內容包括啥(這裏不貼代碼了,因爲所有命令,上文已經提到,剩下的就是寫上你的目錄)?

  1. 靜態檢查,指定生成xml到指定目錄
  2. 編譯程序(確定把上面說的兩個選項和lgcov庫鏈上)
  3. valgrind 執行測試用例,指定單元測試結果xml的目錄,指定valgrind結果文件目錄。
  4. 執行xml導入sqlite的腳本,指定sqlite輸出目錄。
  5. 執行ut_frame。
  6. 休息一下,泡杯茶。
  7. 打開瀏覽器,看看我們這次提交的質量吧。

至此,里程。

演進路線

v0.1 完成基本功能。
v0.2 兼容gcovr,以便支持對so覆蓋率的統計。
v0.3 點擊靜態/動態檢查問題記錄,可以定位到源代碼文件。
v0.5 支持更多的統計指標。
v0.7 支持多次檢查的數據結果對比,分析質量提升項。
v1.0 目標是以插件形式接入ITAS系統(作爲ITAS前期開發人員,再貢獻一點餘熱)

誰用誰拿,隨用隨拿。

尾聲

“更加美好的世界”是我們的嚮往。“拿來主義”是我們的原則。花幾個小時會讓你的世界變得更加美好(慚愧的是寫下上面的文字似乎用去了我更長的時間,所以請小編手下留情,酌情刪減,碼字不易啊~~)。

最後以一段魯迅先生的話作爲結束吧,先生說:”總之,我們要拿來。我們要或使用,或存放,或毀滅。那麼,主人是新主人,宅子也就會成爲新宅子。然而首先要這人沉着,勇猛,有辨別,不自私。沒有拿來的,人不能自成爲新人,沒有拿來的,文藝不能自成爲新文藝“。

參考文獻

  1. 《荀子·勸學》
  2. 《論語·衛靈公》
  3. 《拿來主義》
發佈了51 篇原創文章 · 獲贊 12 · 訪問量 5147
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章