在Linux和macOS上的C和C++語言的PVS-Studio分析器中,從版本7.04開始,出現了檢查指定文件列表的測試機會。 使用新模式,您可以配置分析器來檢查提交和拉取請求。 本文將向您展示如何在 Travis CI、Buddy 和 AppVeyor 等流行的 CI(持續集成)系統中設置 GitHub 項目文件列表檢查。
文件列表檢查模式
適用於 Linux 和 macOS 的 PVS-Studio 7.04 版本具有檢查源文件列表的模式。 這適用於其構建系統允許您生成文件的項目
此外,檢查文件列表的模式可以與編譯器運行的 strace 跟踪(pvs-studio-analyzer 跟踪)一起使用。 為此,您首先需要對項目進行完整構建並對其進行跟踪,以便分析器收集有關所有正在檢查的文件的編譯參數的完整信息。
然而,這個選項有一個顯著的缺點 - 您要么需要在每次啟動時對整個項目執行完整的構建跟踪,這本身就與快速提交檢查的想法相矛盾。 或者,如果您本身緩存跟踪結果,如果跟踪後源文件依賴關係結構發生更改(例如,將新的 #include 添加到源文件之一),則分析器的後續啟動可能會變得不完整。
因此,我們不建議使用帶有跟踪日誌的文件列表檢查模式來檢查提交或拉取請求。 如果您可以在檢查提交時進行增量構建,請考慮使用該模式
用於分析的源文件列表被保存到文本文件中並使用參數傳遞給分析器 -S:
pvs-studio-analyzer analyze ... -f build/compile_commands.json -S check-list.txt
該文件指定文件的相對或絕對路徑,每個新文件必須換行。 不僅可以指定用於分析的文件名,還可以指定各種文本。 解析器將發現這不是一個文件,並將忽略該行。 如果手動指定文件,這對於註釋非常有用。 但是,通常會在 CI 解析期間生成文件列表,例如來自提交或拉取請求的文件。
現在,使用這種模式,您可以在新代碼進入主開發分支之前快速測試新代碼。 為了使驗證系統對分析儀警告做出反應,該實用程序 plog轉換器 添加了標誌 --指示警告:
plog-converter ... --indicate-warnings ... -o /path/to/report.tasks ...
使用此標誌,如果分析器報告中存在警告,轉換器將返回非零代碼。 使用返回代碼,您可以阻止預提交掛鉤、提交或拉取請求,並在屏幕上顯示生成的分析器報告、共享或通過郵件發送。
筆記。 第一次開始分析文件列表時,將分析整個項目,因為分析器需要生成項目源文件對頭文件的依賴關係文件。 這是解析 C 和 C++ 文件的一個功能。 將來,依賴文件可以被緩存,並由分析器自動更新。 與使用增量解析模式相比,使用文件列表檢查模式檢查提交的優點是僅需要緩存該文件,而不是對象文件。
拉取請求分析的一般原則
對整個項目的分析需要花費大量時間,因此只檢查其中的一部分是有意義的。 問題是您需要將新文件與其餘項目文件分開。
考慮一個具有兩個分支的提交樹的示例:
讓我們假設提交 A1 包含相當大量已經檢查過的代碼。 早些時候我們從提交中創建了一個分支 A1 並更改了一些文件。
當然,你注意到之後 A1 還有兩次提交,但這些也是其他分支的合併,因為我們不提交 主。 現在是時候了 修補程序 準備好。 因此,出現了合併請求 B3 и A3.
當然,可以檢查合併的整個結果,但這會太長且不合理,因為只更改了少數文件。 因此,僅分析發生變化的部分會更有效。
為此,我們獲得分支之間的差異,位於我們想要合併到 master 的分支的 HEAD 中:
git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list
$MERGE_BASE 我們稍後會詳細考慮。 事實上,並不是每個 CI 服務都提供有關合併基礎的必要信息,因此每次您都必須想出新的方法來獲取這些數據。 這將在下面描述的每個 Web 服務中詳細說明。
因此,我們得到了分支之間的差異,或者更確切地說,得到了已更改的文件名列表。 現在我們需要提供文件 .pvs-pr.list (我們將上面的輸出重定向到它)到分析器:
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
-S .pvs-pr.list
分析完畢後,我們需要將日誌文件(PVS-Studio.log)轉換為可讀格式:
plog-converter -t errorfile PVS-Studio.log --cerr -w
該命令將列出以下錯誤
只是在這裡,我們不僅需要顯示錯誤,還需要通知我們的組裝和測試服務存在問題。 為此,在轉換器中添加了一個標誌 -W (--指示警告)。 如果至少有一個分析器警告,則實用程序的返回代碼 plog轉換器 將更改為 2,這反過來將通知 CI 服務拉取請求文件中存在潛在錯誤。
特拉維斯CI
配置以文件的形式進行 .travis.yml。 為了方便起見,我建議您將所有內容都放在一個單獨的 bash 腳本中,其中包含將從文件中調用的函數 .travis.yml (bash 腳本名.sh 函數名).
我們將在腳本中添加必要的代碼 打壞,這樣我們就可以獲得更多功能。 在部分 安裝 讓我們寫下以下內容:
install:
- bash .travis.sh travis_install
如果您有任何說明,可以通過刪除連字符將其移至腳本中。
讓我們打開文件 .travis.sh 並將分析器設置添加到函數中 travis_install():
travis_install() {
wget -q -O - https://files.viva64.com/etc/pubkey.txt
| sudo apt-key add -
sudo wget -O /etc/apt/sources.list.d/viva64.list
https://files.viva64.com/etc/viva64.list
sudo apt-get update -qq
sudo apt-get install -qq pvs-studio
}
現在讓我們添加到該部分 腳本 運行分析:
script:
- bash .travis.sh travis_script
在 bash 腳本中:
travis_script() {
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
git diff --name-only origin/HEAD > .pvs-pr.list
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
-S .pvs-pr.list
--disableLicenseExpirationCheck
else
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
--disableLicenseExpirationCheck
fi
plog-converter -t errorfile PVS-Studio.log --cerr -w
}
此代碼必須在項目構建後運行,例如,如果您有 CMake 構建:
travis_script() {
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
cmake $CMAKE_ARGS CMakeLists.txt
make -j8
}
結果會是這樣的:
travis_script() {
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
cmake $CMAKE_ARGS CMakeLists.txt
make -j8
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
git diff --name-only origin/HEAD > .pvs-pr.list
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
-S .pvs-pr.list
--disableLicenseExpirationCheck
else
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
--disableLicenseExpirationCheck
fi
plog-converter -t errorfile PVS-Studio.log --cerr -w
}
您可能已經註意到指定的環境變量。 $TRAVIS_PULL_REQUEST и $TRAVIS_BRANCH。 Travis CI 自己聲明了它們:
- $TRAVIS_PULL_REQUEST 存儲拉取請求編號,或者 假如果是普通分支;
- $TRAVIS_REPO_SLUG 存儲項目存儲庫的名稱。
該函數的算法:
Travis CI 響應返回代碼,因此警告的存在將告訴服務將提交標記為有錯誤。
讓我們仔細看看這行代碼:
git diff --name-only origin/HEAD > .pvs-pr.list
事實上,Travis CI 在分析拉取請求期間會自動合併分支:
因此,我們分析 A4而且不 B3->A3。 由於這個特性,我們需要計算與 A3,這只是分支的頂部 起源.
仍然存在一個重要細節 - 緩存頭文件對已編譯翻譯單元(*.c、*.cc、*.cpp 等)的依賴關係。 分析器在第一次啟動時以檢查文件列表的方式計算這些依賴關係,然後將它們保存在.PVS-Studio目錄中。 Travis CI 允許緩存文件夾,因此我們將保存目錄數據 .PVS-Studio/:
cache:
directories:
- .PVS-Studio/
這段代碼需要添加到文件中 .travis.yml。 該目錄存儲分析後收集的各種數據,這將顯著加快後續文件列表分析或增量分析的運行速度。 如果不這樣做,那麼分析器實際上每次都會分析所有文件。
夥伴
就像 Travis C.I. 一樣,
首先,我們需要向構建行添加一個新操作:
指定用於構建項目的編譯器。 請注意此活動中安裝的 docker 容器。 例如,GCC有一個特殊的容器:
現在讓我們安裝 PVS-Studio 和必要的實用程序:
將以下行添加到編輯器中:
apt-get update && apt-get -y install wget gnupg jq
wget -q -O - https://files.viva64.com/etc/pubkey.txt | apt-key add -
wget -O /etc/apt/sources.list.d/viva64.list
https://files.viva64.com/etc/viva64.list
apt-get update && apt-get -y install pvs-studio
現在讓我們轉到“運行”選項卡(第一個圖標)並將以下代碼添加到相應的編輯器字段中:
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY
if [ "$BUDDY_EXECUTION_PULL_REQUEST_NO" != '' ]; then
PULL_REQUEST_ID="pulls/$BUDDY_EXECUTION_PULL_REQUEST_NO"
MERGE_BASE=`wget -qO -
https://api.github.com/repos/${BUDDY_REPO_SLUG}/${PULL_REQUEST_ID}
| jq -r ".base.ref"`
git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
--disableLicenseExpirationCheck
-S .pvs-pr.list
else
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
--disableLicenseExpirationCheck
fi
plog-converter -t errorfile PVS-Studio.log --cerr -w
如果您已閱讀有關 Travs-CI 的部分,那麼您已經熟悉此代碼,但是,現在有一個新步驟:
事實上,現在我們分析的不是合併的結果,而是發出拉取請求的分支的 HEAD:
所以我們處於有條件提交中 B3 我們需要從中得到差異 A3:
PULL_REQUEST_ID="pulls/$BUDDY_EXECUTION_PULL_REQUEST_NO"
MERGE_BASE=`wget -qO -
https://api.github.com/repos/${BUDDY_REPO_SLUG}/${PULL_REQUEST_ID}
| jq -r ".base.ref"`
git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list
確定 A3 讓我們使用 GitHub API:
https://api.github.com/repos/${USERNAME}/${REPO}/pulls/${PULL_REQUEST_ID}
我們使用了 Buddy 提供的以下變量:
- $BUDDY_EXECUTION_PULL_REQEUST_NO - 拉取請求編號;
- $BUDDY_REPO_SLUG - 用戶名和存儲庫的組合(例如 max / test)。
現在讓我們使用下面的按鈕保存更改,並啟用拉取請求分析:
與 Travis CI 不同,我們不需要指定 .pvs-studio 用於緩存,因為 Buddy 會自動緩存所有文件以供後續啟動。 因此,最後剩下的事情就是在 Buddy 中保存 PVS-Studio 的登錄名和密碼。 保存更改後,我們將返回管道。 我們需要去設置變量並添加 PVS-Studio 的登錄名和密鑰:
之後,新的拉取請求或提交的出現將觸發檢查。 如果提交包含錯誤,那麼 Buddy 會在拉取請求頁面上指出它。
應用程序載體
設置 AppVeyor 與 Buddy 類似,因為一切都發生在 Web 界面中,無需將 *.yml 文件添加到項目存儲庫中。
讓我們轉到項目概述中的“設置”選項卡:
讓我們向下滾動此頁面並啟用緩存保存以構建拉取請求:
現在讓我們轉到“環境”選項卡,在其中指定要構建的映像和必要的環境變量:
如果您已閱讀前面的部分,那麼您對這兩個變量非常熟悉 - PVS_KEY и PVS_用戶名。 如果沒有,那麼我提醒您,有必要檢查PVS-Studio分析儀的許可證。 將來,我們將在 Bash 腳本中再次見到它們。
在下面的同一頁面上,指定緩存文件夾:
如果我們不這樣做,那麼我們將分析整個項目而不是幾個文件,但我們將根據指定的文件獲得輸出。 因此,輸入正確的目錄名稱很重要。
現在是測試腳本的時候了。 打開測試選項卡並選擇腳本:
將以下代碼粘貼到此表單中:
sudo apt-get update && sudo apt-get -y install jq
wget -q -O - https://files.viva64.com/etc/pubkey.txt
| sudo apt-key add -
sudo wget -O /etc/apt/sources.list.d/viva64.list
https://files.viva64.com/etc/viva64.list
sudo apt-get update && sudo apt-get -y install pvs-studio
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY
PWD=$(pwd -L)
if [ "$APPVEYOR_PULL_REQUEST_NUMBER" != '' ]; then
PULL_REQUEST_ID="pulls/$APPVEYOR_PULL_REQUEST_NUMBER"
MERGE_BASE=`wget -qO -
https://api.github.com/repos/${APPVEYOR_REPO_NAME}/${PULL_REQUEST_ID}
| jq -r ".base.ref"`
git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
--disableLicenseExpirationCheck
--dump-files --dump-log pvs-dump.log
-S .pvs-pr.list
else
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
--disableLicenseExpirationCheck
fi
plog-converter -t errorfile PVS-Studio.log --cerr -w
我們看一下以下部分代碼:
PWD=$(pwd -L)
if [ "$APPVEYOR_PULL_REQUEST_NUMBER" != '' ]; then
PULL_REQUEST_ID="pulls/$APPVEYOR_PULL_REQUEST_NUMBER"
MERGE_BASE=`wget -qO -
https://api.github.com/repos/${APPVEYOR_REPO_NAME}/${PULL_REQUEST_ID}
| jq -r ".base.ref"`
git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
--disableLicenseExpirationCheck
--dump-files --dump-log pvs-dump.log
-S .pvs-pr.list
else
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
--disableLicenseExpirationCheck
fi
將 pwd 命令的值相當具體地分配給應該存儲此默認值的變量乍一看似乎很奇怪,但是,我稍後會解釋所有內容。
在 AppVeyor 中設置分析器時,我遇到了分析器極其奇怪的行為。 一方面,一切正常,但分析沒有開始。 我花了很多時間注意到我們位於 /home/appveyor/projects/testcalc/ 目錄中,並且分析器確定我們位於 /opt/appveyor/build-agent/ 中。 然後我意識到 $PWD 變量有點謊言。 因此,我在開始分析之前手動更新了它的值。
然後一切都像以前一樣:
現在考慮以下代碼片段:
PULL_REQUEST_ID="pulls/$APPVEYOR_PULL_REQUEST_NUMBER"
MERGE_BASE=`wget -qO -
https://api.github.com/repos/${APPVEYOR_REPO_NAME}/${PULL_REQUEST_ID}
| jq -r ".base.ref"`
在其中,我們得到了聲明拉取請求的分支之間的差異。 為此,我們需要以下環境變量:
- $APPVEYOR_PULL_REQUEST_NUMBER - 拉取請求編號;
- $APPVEYOR_REPO_NAME - 用戶名和項目存儲庫。
結論
當然,我們並沒有考慮所有可能的持續集成服務,但是,它們都有非常相似的工作細節。 除了緩存之外,每個服務都有自己的“自行車”,因此一切總是不同的。
在某個地方,比如在 Travis-CI 中,幾行代碼和緩存就可以完美地工作; 某個地方,例如在 AppVeyor 中,您只需在設置中指定文件夾即可; 但是您需要在某個地方創建唯一的密鑰並嘗試說服系統給您機會覆蓋緩存的片段。 因此,如果您想在上面未討論的持續集成服務上設置拉取請求分析,那麼首先確保您不會遇到緩存問題。
感謝您的關注。 如果問題沒有解決,請隨時寫信給我們:
如果您想與英語讀者分享這篇文章,請使用翻譯鏈接:Maxim Zvyagintsev。
來源: www.habr.com