I think it makes no sense to once again advertise a wonderful tool for static analysis - PVS Studio. On Habré there are already a lot of articles dedicated to her, but I want to touch on another aspect - the use of this tool in the system of continuous integration.
So, there is some organization, there is a CI in it that works simply: Jenkins gets a hook after a push in Git, and then starts some pipeline. By virtue of the tools used, the build is done for projects created in C # (msbuild) and C ++ (msbuild, CMake). At one of the finishing stages, the generation of reports is started, including with the help of PVS Studio (among other things - cppcheck, but this is not important for further narration).
PVS Studio has a console analysis tool that is launched from the command line: PVS-Studio_Cmd.exe --target "${projectFile}" --output report.plog --progress
At the entrance - the name of the project (.sln), at the exit - the report.
Report - a file with the extension .plog, is a regular XML file. The document scheme is embedded, so no surprises on the output format can not be. At least until the developers change the scheme, but we will not consider this option.
A report consists of a set of records, each of which indicates a file and a line in it, an error class, an error level, a description, and other things that are not very interesting.
But reading XML with your eyes is a pleasure for yourself, so you need some way to view and navigate.
The easiest and most working is the PVS Studio for Visual Studio plugin, with the ability to navigate through the code. But to force a technical manager or another interested person to upload a project to VS every time is a bad tone, and the history of the project’s development is not visible.
So let's go the other way and see what can be done. And there is a fairly standard way that allows you to convert XML into something else: XSLT . Now, probably, someone from the readers has shaken, but, nevertheless, I propose to continue reading.
XSLT is a language for transforming XML documents into something else. It simply matches the transformation rule to the input template, but We have made the conversion for ourselves into an HTML report.
I hope no one will judge me for the layout tables, because the data is by its nature tabular. Each record in the report will correspond to a table row with the following columns:
The line number is just convenient for a verbal link when discussing.
The file name together with the name of the string allows you to create a link to the repository. But more on that later.
The error code is framed by a link to the PVS-Studio developer site: http://viva64.com/en/ ErrorCode } (or / ru /, as you like).
Error message - no comments.
There are some points that have to deal with.
First, I would like the messages to be sorted by level of importance, as well as to have the total number of messages of each type. The first task is solved using the xsl:sort
expression, the second is the count([])
.
Second: the file name is specified full, and to form a link to the version control system, you need a relative name. You just need to cut off the prefix corresponding to the name of the directory with the project into which the repository was cloned (we have Git, but this easily adapts). But in order for this path to appear, we need to parameterize the XSL transformation using the xsl:param
construction. Further relatively simple: remove from the line with the file name a common prefix with the name of the directory where the repository is sloped. I must say, in XSLT, this problem is solved quite subtly.
Third: verification refers to a specific revision in the repository, and this must also be borne in mind. It is solved using the parameter with the commit identifier. Similarly for branches.
Fourth: if third-party libraries are used with source codes, do not mix warnings in them with warnings in our project. The problem is solved in the following way: all external projects are added to some directory whose name is not contained in our project. Now, if the file name contains this subdirectory (actually just a substring), then the plog entry is not included in the report, but is considered as "hidden" in the report header. For more flexibility, you can parameterize the transformation and assign a default name to this directory: <xsl:param name="external" select="'External'" />
Well, another little task: to build a link to the repository. We use redmine + gitolite. Again, adaptable.
Since many parameters are constant for conversion, you can prepare a constant URL prefix:
<xsl:variable name="repo"> <xsl:text>http://redmine.your-site.com/projects/</xsl:text> <xsl:value-of select="$project" /> <xsl:text>/revisions/</xsl:text> <xsl:value-of select="$revision" /> <xsl:text>/entry/</xsl:text> </xsl:variable>
Few beautiful with stylization, and you can use. CSS injected into the page, just to have a report in one file. We don't need pictures either.
Full transformation code under spoiler
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns="http://www.w3.org/1999/xhtml" exclude-result-prefixes="msxsl" > <xsl:output method="html" indent="yes"/> <xsl:param name="project" /> <xsl:param name="base-path" /> <xsl:param name="branch" select="'master'" /> <xsl:param name="revision" select="'[required]'" /> <xsl:param name="external" select="'External'" /> <xsl:variable name="repo"> <xsl:text>http://redmine.your-company.com/projects/</xsl:text> <!-- # !!!attention!!! # --> <xsl:value-of select="$project" /> <xsl:text>/revisions/</xsl:text> <xsl:value-of select="$revision" /> <xsl:text>/entry/</xsl:text> </xsl:variable> <xsl:template name="min-len"> <xsl:param name="a" /> <xsl:param name="b" /> <xsl:variable name="la" select="string-length($a)" /> <xsl:variable name="lb" select="string-length($b)" /> <xsl:choose> <xsl:when test="$la < $lb"> <xsl:value-of select="$la"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="$lb" /> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="strdiff-impl"> <xsl:param name="mask" /> <xsl:param name="value" /> <xsl:param name="n" /> <xsl:param name="lim" /> <xsl:choose> <xsl:when test="$n = $lim"> <xsl:value-of select="substring($value, $lim + 1)" /> </xsl:when> <xsl:when test="substring($mask, 0, $n) = substring($value,0, $n)"> <xsl:call-template name="strdiff-impl"> <xsl:with-param name="lim" select="$lim" /> <xsl:with-param name="mask" select="$mask" /> <xsl:with-param name="value" select="$value" /> <xsl:with-param name="n" select="$n + 1" /> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="substring($value, $n - 1)"/> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="strdiff"> <xsl:param name="mask" /> <xsl:param name="value" /> <xsl:choose> <xsl:when test="not($value)" /> <xsl:when test="not($mask)"> <xsl:value-of select="$value" /> </xsl:when> <xsl:otherwise> <xsl:call-template name="strdiff-impl"> <xsl:with-param name="mask" select="$mask" /> <xsl:with-param name="value" select="$value" /> <xsl:with-param name="lim"> <xsl:call-template name="min-len"> <xsl:with-param name="a" select="$mask" /> <xsl:with-param name="b" select="$value" /> </xsl:call-template> </xsl:with-param> <xsl:with-param name="n" select="1" /> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template match="/*"> <xsl:variable select="Solution_Path/SolutionPath" name="solution" /> <xsl:variable select="PVS-Studio_Analysis_Log [not(contains(File, $external))] [ErrorCode!='Renew'] " name="input" /> <html lang="en"> <head> <style type="text/css"> <![CDATA[ #report * {font-family: consolas, monospace, sans-serif; } #report {border-collapse: collapse; border: solid silver 1px;} #report th, #report td {padding: 6px 8px; border: solid silver 1px;} .sev-1 {background-color: #9A2617;} .sev-2 {background-color: #C2571A;} .sev-3 {background-color: #BCA136;} .sev-hidden {background-color: #999; } #report tbody * {color: white;} .fa * { color: #AAA; } a {color: #006;} .stat {padding: 20px;} .stat * {color: white; } .stat span {padding: 8px 16px; } html {background-color: #EEE;} .success {color: #3A3; } ]]> </style> </head> <body> <h1>PVS-Studio report</h1> <h2> <xsl:call-template name="strdiff"> <xsl:with-param name="value"> <xsl:value-of select="$solution" /> </xsl:with-param> <xsl:with-param name="mask"> <xsl:value-of select="$base-path" /> </xsl:with-param> </xsl:call-template> </h2> <div class="stat"> <span class="sev-1"> High: <b> <xsl:value-of select="count($input[Level=1])" /> </b> </span> <span class="sev-2"> Meduim: <b> <xsl:value-of select="count($input[Level=2])" /> </b> </span> <span class="sev-3"> Low: <b> <xsl:value-of select="count($input[Level=3])" /> </b> </span> <span class="sev-hidden" title="Externals etc"> Hidden: <b> <xsl:value-of select="count(PVS-Studio_Analysis_Log) - count($input)"/> </b> </span> </div> <xsl:choose> <xsl:when test="count($input) = 0"> <h2 class="success">No error messages.</h2> </xsl:when> <xsl:otherwise> <table id="report"> <thead> <tr> <th> # </th> <th> File </th> <th> Line </th> <th> Code </th> <th> Message </th> </tr> </thead> <tbody> <xsl:for-each select="$input"> <xsl:sort select="Level" data-type="number"/> <xsl:sort select="DefaultOrder" /> <tr> <xsl:attribute name="class"> <xsl:text>sev-</xsl:text> <xsl:value-of select="Level" /> <xsl:if test="FalseAlarm = 'true'"> <xsl:text xml:space="preserve"> fa</xsl:text> </xsl:if> </xsl:attribute> <th> <xsl:value-of select="position()" /> </th> <td> <xsl:variable name="file"> <xsl:call-template name="strdiff"> <xsl:with-param name="value" select="File" /> <xsl:with-param name="mask" select="$base-path" /> </xsl:call-template> </xsl:variable> <a href="{$repo}{translate($file, '\', '/')}#L{Line}"> <xsl:value-of select="$file" /> </a> </td> <td> <xsl:value-of select="Line"/> </td> <td> <a href="http://viva64.com/en/{ErrorCode}" target="_blank"> <xsl:value-of select="ErrorCode" /> </a> </td> <td> <xsl:value-of select="Message" /> </td> </tr> </xsl:for-each> </tbody> </table> </xsl:otherwise> </xsl:choose> </body> </html> </xsl:template> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
We start the transformation using a small console utility written in C #, but you can do it in a different way (if necessary, I will share it, there is nothing complicated and secret there).
And then from this you can make a dashboard, but this, as they say, is another story.
And now a little crying towards the developers. There is one not that bug, not that feature, which makes it impossible to do the full thing described above, besides this concerns only C ++ projects, in C # there is no such trouble. When a plog file is generated, in the <File>
name is always lowercase. And when redmine (and other web sites) is hosted on UNIX-like systems with case-sensitive file names, the register breaks when generating links to files, which makes links unworkable. Such is the sadness.
I received a reply to the technical support letter that this behavior is due to the external API, but it is not clear why it is so selective and concerns only C ++, and does not concern C #.
Therefore, I so far, as promised, call for the continuation of Paull and hope for fruitful cooperation.
Thanks for attention.
Update : According to the results of correspondence with the developers in the face of Paull and khandeliants , deep excavations were carried out, as a result of which a beta was released, in which the problem described above was solved. But in order for this to work, you need support for shortcuts on at least the disk where the analysis is being performed. To do this, in the registry on the path HKLM \ SYSTEM \ CurrentControlSet \ Control \ FileSystem, you must set the NtfsDisable8dot3NameCreation (DWORD) parameter to a value that allows saving short file names. Read more in MSDN .
The prohibition of the default short names needed to increase the speed of NTFS.
You can either set the value to 0 and not bother, or 3 if the CI tasks are performed in the user profile on the system partition or somewhere else on the system partition, or in 2 and execute the command fsutil 8dot3name set Z: 0
(your disk instead of Z :) where the CI workspace will be deployed (also refers to RAM disks, by the way).
I hope this information will appear somewhere on the viva64 website.
Now it seems like the gestalt is closed, thanks again for your attention.
Source: https://habr.com/ru/post/423829/
All Articles