--- tags: SSSTC Summer Intern --- # Cppcheck Applied on GitLab --- ## Cppcheck 在建興實習的這兩個月,我跟Skyler最主要的任務就是幫忙架一個叫作「Cppcheck」的測試平台。Cppcheck是一種用來分析C語言/C++在coding上的問題,因為有些語法雖然編譯得過,但可能會對整個程式造成一些潛在問題。 ```c= #include<stdio.h> int main(){ int a; int b; int c = 1; int d = 2; b++; c++; d = 4; return 0; } ``` 例如上述的程式碼,在編譯上沒有問題,但很明顯的有一些變數沒用到及變數未宣告。![](https://i.imgur.com/Bx6zlPr.png ) <br> ## GitLab 稍微瞭解了Cppcheck後,接下來簡單介紹一下GitLab。GitLab是基於Git的軟體整合開發平台,且具有線上編輯、CI/CD等功能。其中CI/CD是GitLab內建的一款工具,CI(Continuous Integration)指的是持續整合,CD(Continuous Delivery)指的是持續交付。當開發者建立了CI/CD,那麼當開發者使用git commit,那麼就會觸發CI/CD相關的一系列操作,這一系列操作由Runner執行,相關組態寫在yml檔中,每一個Commit會發出一條Pipeline,Pipeline是不同Stage的Job的集合,同一Stage的Job以並列的方式執行,然後每個Stage之間是順序執行。 囉囉唆唆的說了一大堆,簡單來說,GitLab就是一棵大樹,有一個主幹叫作「Master」,然後每個人會建立一些分支叫作「Branch」,每個人先在自己的Branch完成自己的工作,然後再將Branch「Merge」到Master裡,完成分工合作。 ![](https://i.imgur.com/ZlRMAOD.png =80%x) <br> ## Application 有了Cppcheck這個套件和GitLab的基本知識後,我們就要將Cppcheck套用在Git上了。我和Skyler利用Git CI/CD的整合優勢分工合作,我們各自開了一個Branch,將我們要完成的Project在自己的Branch上完成,然後再整合起來。Skyler負責寫一個資料整理的Python檔,而我則負責寫GitLab Pipeline要執行的yml檔。 由於Cppcheck跑完整包Code後會產生許多資料,所以Skyler將所有輸出依照不同的檔案分類,也將不同的檢測類型分類,最後再Sumary起來。以下是他所寫的cppcheck_trans.py ```python= import sys class CppCheck: def __init__(self): self.printData = ["Line= ", "Column= ", "Check:", "Problem: "] self.previous_file = "init" # Count problems in dif folders self.core_count = 0 self.hal_count = 0 self.host_count = 0 self.media_count = 0 self.others_count1 = 0 # Count num of different problems self.style_count = 0 self.error_count = 0 self.note_count = 0 self.others_count2 = 0 def WriteCppcheckFile(self, fileInput, fileOutput): lines = fileInput.readlines() for count1, line in enumerate(lines): if count1 % 3 == 2: fileOutput.write("") elif count1 % 3 == 1: fileOutput.write(str(line)) else: for count2, data1 in enumerate(line.split(':')): if data1 == "nofile": break if count2 == 0: write_r = "" for count3, word in enumerate(data1.strip().split('/')): if count3 == 0: write_r = ("Location: ") else: write_r = write_r + ("/" + str(word)) # Count problems in dif folders if count3 == 1: if word == "Core": self.core_count += 1 elif word == "Hal": self.hal_count += 1 elif word == "Host": self.host_count += 1 elif word == "Media": self.media_count += 1 else: self.others_count1 += 1 # If dif file, print location if word != self.previous_file: fileOutput.write("\n\n\n{:=^100s}".format("")) fileOutput.write("\n" + str(write_r) + "\n") fileOutput.write("{:=^100s}\n".format("")) else: fileOutput.write("\n") previous_file = word # Print info elif count2 == 1: fileOutput.write("Line= " + str(data1)) elif count2 == 2: fileOutput.write(" Column= " + str(data1)) elif count2 == 3: fileOutput.write(" Check:" + str(data1)) if data1 == " style": self.style_count += 1 elif data1 == " error": self.error_count += 1 elif data1 == " note": self.note_count += 1 else: self.others_count2 += 1 elif count2 == 4: fileOutput.write(" Problem:" + str(data1)) else: fileOutput.write(":" + str(data1)) # Print summary fileOutput.write("\n\n\n{:=^50s}".format("Summary")) fileOutput.write("\nTotal problems: " + str(count1 / 3) + "\n") fileOutput.write("{:=^50s}".format("")) fileOutput.write("\nProblems in folder:\n") fileOutput.write(" Core: " + str(self.core_count) + "\n") fileOutput.write(" Hal: " + str(self.hal_count) + "\n") fileOutput.write(" Host: " + str(self.host_count) + "\n") fileOutput.write(" Media: " + str(self.media_count) + "\n") fileOutput.write(" Others: " + str(self.others_count1) + "\n") fileOutput.write("{:=^50s}".format("")) fileOutput.write("\nNumber of different problems:\n") fileOutput.write(" Style: " + str(self.style_count) + "\n") fileOutput.write(" Error: " + str(self.error_count) + "\n") fileOutput.write(" Note: " + str(self.note_count) + "\n") fileOutput.write(" Others: " + str(self.others_count2) + "\n") fileOutput.write("{:=^50s}".format("") + "\n") ######################### Main ########################## def Main(): cpp = CppCheck() # Open file fileInput = open(sys.argv[1], "r") fileOutput = open(sys.argv[2], "w") cpp.WriteCppcheckFile(fileInput, fileOutput) return #################### Main Script Start ################### Main() #################### Main Script End ##################### ``` <br> 當把寫好的Code丟上GitLab時,GitLab會透過Pipeline來執行任務。依照Stage的順序,執行Stage裡的Job。所以我就是負責寫yml檔,告訴Git要如何執行我們的程式。yml檔中最重要的就是Stage及Job,如果寫錯了程式就不會動,然後script的部份就是程式該如何執行。 ```yaml= #----------------------------------------------------------------------------- # # Stage : Cppcheck # Job : cppcheck # Rule : N/A # Tag : $RUNNER_TAG_MASTER # #----------------------------------------------------------------------------- Cppcheck:cppcheck: stage: Cppcheck variables: CPPCHKOUT_FILE : 'cppcheck_out.txt' OUTPUT_FILE : 'Cppcheck_Output.txt' script: - echo "Start cppcheck" - ls - git clone http://gitlab-ci-token:${CI_JOB_TOKEN}@10.6.201.211/SSD/$PROJECT_NAME - cppcheck --enable=$CHECK_TYPE --output-file=$CPPCHKOUT_FILE $PROJECT_NAME # python cppcheck_trans.py $CPPCHKOUT_FILE [input file] $OUTPUT_FILE [output file] - python cppcheck_trans.py $CPPCHKOUT_FILE $OUTPUT_FILE artifacts: name: "Cppcheck_$PROJECT_NAME$BRANCH_NAME" paths: - $OUTPUT_FILE expire_in: 3 day tags: - $RUNNER_TAG_MASTER ``` <br> ## Result 當我們各自的Project都在Local完成Commit後,就可以Push上Global來進行Merge。![](https://i.imgur.com/kxKrSBK.png) ![](https://i.imgur.com/00NhiAv.png) 當所有Project都推上Server後,就能利用Runner來跑我們的Job了。 ![](https://i.imgur.com/m9AAp9n.png) 最後輸出的檔案如下圖所示: ![](https://i.imgur.com/cmh70D1.png) <br> ## Conclusion GitLab是一個很方便的開發平台,聽說業界大部分的公司也都是透過GitLab來進行整合開發。不過也許是我們剛開始學習Git,在整合的過程遇到了一些問題:當我們要把Commit完的Branch推上Global時,需要Rebase後才能Run,但在過程中發現一直抓到舊的程式,所以導致我們Run Pipeline一直Failed。 後來我們仔細弄清楚Commit點,並且分清楚彼此的Branch後,再重新建立一條新的Branch,最後終於完成了我們Project的整合。![](https://i.imgur.com/KuB9q8f.png)