从7.04版开始,针对Linux和macOS上的C和C ++语言的PVS-Studio分析仪具有测试功能,可以检查指定文件的列表。 使用新模式,您可以将分析器配置为检查提交和请求。 本文将向您展示如何在Travis CI,Buddy和AppVeyor等流行的CI(持续集成)系统中配置检查GitHub项目的已修改文件的列表。
文件列表检查模式
PVS-Studio是用于检测用C,C ++,C#和Java编写的程序的源代码中的错误和潜在漏洞的工具。 它适用于Windows,Linux和macOS上的64位系统。
在适用于Linux和macOS的PVS-Studio版本7.04中,出现了检查源文件列表的模式。 这适用于其生成系统允许生成
compile_commands.json文件的项目。 分析器需要提取有关指定文件编译的信息。 如果您的构建系统不支持compile_commands.json文件的生成,则可以尝试使用
Bear实用程序生成这样的文件。
另外,文件列表检查模式可与编译器启动的strace跟踪日志(pvs-studio-analyzer跟踪)一起使用。 为此,您首先需要对项目进行完整的组装并对其进行跟踪,以便分析器收集有关所有测试文件的编译参数的完整信息。
但是,此选项有一个很大的缺点-您要么需要在每次启动时对整个项目的组装进行完整的跟踪,这本身就与快速检查提交的想法相矛盾。 或者,如果缓存了跟踪结果本身,如果跟踪后源文件的依赖关系结构发生了变化(例如,新的#include已添加到一个源文件中),则后续的分析器启动可能无法完成。
因此,我们不建议在跟踪日志中使用文件列表检查模式来检查提交或请求请求。 如果在检查提交时可以进行增量汇编,请考虑使用
增量分析模式。
用于分析的源文件列表保存在文本文件中,并使用
-S参数传输到分析器:
pvs-studio-analyzer analyze ... -f build/compile_commands.json -S check-list.txt
在此文件中,指示文件的相对或绝对路径,并且每个新文件都应在新行上。 不仅可以指定分析文件的名称,还可以指定各种文本。 分析器将看到这不是文件,并且将忽略该行。 如果手动指定文件,这对于注释很有用。 但是,通常会在CI中进行分析时生成文件列表,例如,这些文件可能是来自提交或拉取请求的文件。
现在,使用这种模式,您可以在新代码进入主开发分支之前快速对其进行检查。 为了使验证系统能够响应分析器警告,
已将--indicate-warnings标志添加到
plog-converter 实用程序中 :
plog-converter ... --indicate-warnings ... -o /path/to/report.tasks ...
使用此标志,如果分析仪报告中有警告,则转换器将返回非零代码。 使用返回码,您可以阻止预提交的挂接,提交或提取请求,并在屏幕上显示生成的分析器报告,共享或通过邮件发送。
注意事项 第一次运行文件列表分析时,将分析整个项目,因为 分析器需要从头文件生成项目源文件的依赖项文件。 这是C和C ++文件分析的功能。 将来,依赖项文件可以被缓存,并且分析器将自动对其进行更新。 与使用增量分析模式相比,使用文件列表验证模式时检查提交的优势在于,您仅需要缓存此文件,而不是对象文件。拉取请求分析的一般原则
整个项目的分析需要很多时间,因此仅检查其中的一部分是有意义的。 问题是您需要将新文件与其余项目文件分开。
考虑具有两个分支的提交树的示例:
让我们想象一下提交
A1包含了大量已经经过测试的代码。 早些时候,我们从提交
A1分支了,并更改了一些文件。
当然,您注意到在
A1之后还有两次提交,但是它们也是其他分支的合并,因为我们不在
master中进行提交。 现在,
修补程序准备就绪的时间到了。 因此,
出现了合并
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
此命令将在
stderr (标准错误消息输出流)中列出错误。
直到现在,我们不仅需要显示错误,还需要告知我们的服务以组装和测试问题。 为此,将
-W标志(
--indicate-warnings )添加到了转换器中。 如果至少有一个分析器警告,则
plog-converter实用程序的返回码将更改为2,从而将通知CI服务有关拉取请求文件中存在潜在错误的信息。
特拉维斯
该配置作为
.travis.yml文件进行。 为了方便起见,我建议您将所有内容放在一个单独的bash脚本中,该脚本具有将从
.travis.yml文件调用的
函数 (
bash script_name.sh function_name )。
我们将必要的代码添加到
bash脚本中,以便获得更多功能。 在
安装部分中,编写以下内容:
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存储拉取请求号,如果是常规分支,则返回false ;
- $ TRAVIS_REPO_SLUG存储项目存储库的名称。
此函数的算法:
Travis CI会响应返回代码,因此警告的出现将告诉服务将提交标记为包含错误。
现在,让我们仔细看一下这行代码:
git diff --name-only origin/HEAD > .pvs-pr.list
事实是Travis CI在拉取请求分析期间自动合并分支:
因此,我们分析
A4而不是
B3-> A3 。 由于此功能,我们需要计算与
A3的差,
A3恰好是
原点的分支顶部。
一个重要的细节仍然是头文件对已编译转换单元(* .c,* .cc,* .cpp等)的缓存依赖性。 分析器在文件列表检查模式下的第一次启动时计算这些依赖关系,然后将其保存在.PVS-Studio目录中。 Travis CI允许您缓存文件夹,因此我们将保存
.PVS-Studio /目录的数据:
cache: directories: - .PVS-Studio/
此代码需要添加到
.travis.yml文件。 此目录存储分析后收集的各种数据,这将大大加快文件列表分析或增量分析的后续启动速度。 如果不这样做,那么分析器实际上将每次都分析所有文件。
好友
与Travis CI一样,
Buddy提供了自动构建和测试存储在GitHub上的项目的功能。 与Travis CI不同,它是在Web界面中配置的(提供bash支持),因此无需在项目中存储配置文件。
首先,我们需要向装配线添加一个新动作:
我们指出用于构建项目的编译器。 请注意此操作中安装的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_USERNAME 。 如果不是,那么我提醒您,检查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。
使用PVS-Studio在Travis CI,Buddy和AppVeyor中分析提交和请求请求 。