>
>
Analyzing Visual Studio / MSBuild / .NE…


Analyzing Visual Studio / MSBuild / .NET projects from the command line using PVS-Studio

This document covers the usage of command-line utilities for the analysis of MSBuild projects (.vcxproj / .csproj) and Visual Studio solutions.

This document covers the usage of command line utilities. Usage of plugins for Visual Studio and JetBrains Rider is described in the following documentation sections: "Getting acquainted with the PVS-Studio static code analyzer on Windows", "Using PVS-Studio with JetBrains Rider".

To analyze C# projects, you may need to install additional .NET SDKs. You can learn more about it in the "System requirements for PVS-Studio analyzer" documentation.

The command-line analyzer of MSBuild projects has various names on different platforms supported by the analyzer:

  • PVS-Studio_Cmd (analysis of solutions, C#, C++ projects on Windows);
  • pvs-studio-dotnet (analysis of solutions, C# projects on Linux / macOS);

The features described below are relevant for both utilities. Examples with PVS-Studio_Cmd / pvs-studio-dotnet are interchangeable unless explicitly stated otherwise.

Note. To analyze C++ projects that don't use the MSBuild build system, on Windows use the compilation monitoring system or direct integration of the analyzer into the build system. Analysis of C++ projects on Linux / macOS is described in detail in this section of the documentation.

Running analysis of sln and csproj/vcxproj files

Command line utilities are unpacked to the following directories by default:

  • PVS-Studio_Cmd.exe
    • Windows: "C:\Program Files (x86)\PVS-Studio\";
  • pvs-studio-dotnet
    • Linux: "/usr/share/pvs-studio-dotnet/";
    • macOS: "/usr/local/share/pvs-studio-dotnet".

'‑‑help' command displays all available arguments of the analyzer:

PVS-Studio_Cmd.exe --help

Here is an example of how to check 'mysolution.sln':

PVS-Studio_Cmd.exe -t "mysolution.sln" -o "mylog.plog"

Let's look at the main arguments of the analyzer:

  • ‑‑target (-t): required parameter. Allows you to specify the object to be tested (sln or csproj/vcxproj file);
  • ‑‑output (-o): path to the file, where the analysis results will be written. If this parameter is omitted, the analyzer report file will be created next to the file specified using the '‑‑target' flag. The analyzer report can be saved in 2 formats: .json and. plog. The format type is determined by the specified extension. By default, without specifying this flag, the report will be generated in the .plog format on Windows, and in the .json format on Linux and macOS;
  • ‑‑platform (-p) and ‑‑configuration (-c): the check will be run for the specified platform and configuration. If these parameters are not specified, the first available "platform|configuration" (when checking the sln file) pair will be selected or "Debug|AnyCPU" (when checking a separate csproj project) or "Debug|Win32" (when checking a separate vcxproj project);
  • ‑‑sourceFiles (-f): path to the text file containing a list of paths to source files for the analysis (each must be on a separate line). The argument cannot be used with the '‑‑regenerateDependencyCacheWithoutAnalysis '(-W) flag in the same command. Relative and absolute paths are supported. In this mode, when analyzing C and C++ files, a cache file of compilation is created (and used). You can control its location of which can be controlled using the '‑‑dependencyRoot' (-D) flag. Note: when this argument is used with the '‑‑selectProjects' (-S) and/or '‑‑excludeProjects' (-E) arguments, the project filtering is applied first, and then the files from '‑‑sourceFiles' (-f) are searched and analyzed among the remaining project files;
  • ‑‑analyzeMoifiedFiles (-F): Analyze only modified files. Information about file modifications is determined by comparing a hash generated based on the contents of the source files. The generated hashes are stored in the dependency cache. The cache can be configured the '‑‑dependencyRoot' (-D) and '‑‑dependencyCacheSourcesRoot' (-R) flags. The argument cannot be used with the '‑‑regenerateDependencyCache' (-G), '‑‑regenerateDependencyCacheWithoutAnalysis' (-W) and '‑‑sourceFiles' (-f) flags; Note: when this argument is used with the '‑‑selectProjects' (-S) and/or '‑‑excludeProjects' (-E) arguments, the project filtering is applied first, and then the modified files are searched and analyzed among the remaining project files;
  • ‑‑regenerateDependencyCache (-G): generates or updates compilation dependency cache for all project source files that is used together with the '‑‑sourceFiles' (-f) flag and starts analysis for all project source files. You can use the '‑‑selectProjects' (-S) and the '‑‑excludeProjects' (-E) flags to filter out projects. Passing the '‑‑sourceFiles' (-f) flag along with this flag causes a complete regeneration of the dependency caches for all project source files, and the analysis is done only for the list of files passed in '‑‑sourceFiles' (-f). The argument cannot be used with the '‑‑analyzeMoifiedFiles' (-F) flag;
  • ‑‑regenerateDependencyCacheWithoutAnalysis (-W): generates or updates the compilation dependency cache for all project source files that is used together with the '‑‑sourceFiles' flag (-f), without running analysis. You can use the '‑‑selectProjects' (-S) and '‑‑excludeProjects' (-E) flags to filter out projects. The argument cannot be used with the '‑‑sourceFiles' (-f) and '‑‑analyzeMoifiedFiles' (-F) flags;
  • ‑‑appendHashToCache (-H): Enables appending a hash of a source file to the dependency cache when the cache is generated or modified with the '‑‑regenerateDependencyCache' (-G), '‑‑regenerateDependencyCacheWithoutAnalysis' (-W) and '‑‑sourceFiles' (-f) flags
  • ‑‑dependencyRoot (-D): an optional path to the directory where the source file dependency caches are located. Works in addition to the '‑‑sourceFiles' (-f), '‑‑regenerateDependencyCache' (-G) and '‑‑analyzeMoifiedFiles' (-F) flags;
  • ‑‑dependencyCacheSourcesRoot (-R): an optional path to specify the root directory of relative source file paths in dependency caches generated with '‑‑sourceFiles' (-f), '‑‑regenerateDependencyCache' (-G) flags and '‑‑analyzeMoifiedFiles' (-F);
  • ‑‑settings (-s): path to the PVS-Studio configuration file. If this parameter is omitted, the Settings.xml file will be used, located in the directory "%AppData%\PVS-Studio\" on Windows or "~/.config/PVS-Studio/" on Linux / macOS. These same settings files are used by plugins (Visual Studio, Rider), which makes it possible to edit them using the PVS-Studio plugin interface in these IDEs. Please note that for the analyzer to work under Windows, the settings file must contain registration information. Various ways to enter a license are described here. Depending on the settings file used, the following rules apply:
    • when using the default settings file, it must contain registration information;
    • if you explicitly specify the path to the settings file, the registration information must be written either in the specified settings file or in the default settings file;
  • ‑‑licFile (-l): path to the PVS-Studio license file. This flag is available only in pvs-studio-dotnet. If the parameter is omitted, the PVS-Studio.lic license file will be used, which is located in the "~/.config/PVS-Studio/ " directory.
  • ‑‑suppressAll (-a): add unsuppressed warnings in suppress files of corresponding projects (disabled by default). If this flag is present, all messages will be added to the suppress warnings base after saving the check result. The flag supports 2 operating modes.
    • SuppressOnly adds messages from the passed analyzer report to the suppress files without running the analysis;
    • AnalyzeAndSuppress runs the analysis, saves the analyzer report, and only after this suppresses the messages found in it. This mode allows you to get a report from the analyzer on regular runs, which contains only new messages for the changed \ written code, i.e. new messages get into the new log and get immediately suppressed - they won't be issued during the subsequent check. However, if you still need to view the old messages (without rechecking), a file with the full check report (only for .plog analyzer reports) will be saved next to the analyzer report containing new messages. Read more about message suppression mode in this section of the documentation;
  • ‑‑sourceTreeRoot (-e): the root part of the path that PVS-Studio will use when generating relative paths in diagnostic messages. Setting this parameter overrides the 'SourceTreeRoot' value in the PVS-Studio settings;
  • ‑‑incremental (-i): incremental analysis mode. For more information about incremental analysis in PVS-Studio, see the section "PVS-Studio incremental analysis mode". Note, that this mode is available only under PVS-Studio Enterprise license. There are following modes of incremental analysis available:
    • Scan - analyze all dependencies to determine, which files will be analyzed incrementally. The analysis itself won't be performed. Changes made since the last build will be taken into account, and the previous history of changes will be deleted.
    • AppendScan - analyze all dependencies to determine, which files will be analyzed incrementally. The analysis itself won't be performed. Changes made since the last build, as well as all previous changes, will be taken into account.
    • Analyze - perform incremental analysis. This step should be done after Scan or AppendScan and can be performed both before and after the build of a solution or project. Static analysis will only be performed for files from the list obtained by executing the Scan or AppendScan commands.
    • ScanAndAnalyze - analyze all the dependencies to determine which files should be analyzed incrementally and perform incremental analysis of the edited files with the source code. Changes made since the last build will be taken into account.
  • ‑‑msBuildProperties (-m): allows you to set or redefine project level properties. To set or redefine multiple project level properties, use the "|" symbol, for example: ‑‑msBuildProperties "WarningLevel=2|OutDir=bin\OUT32\"
  • ‑‑excludeDefines (-x): a list of symbols that will be excluded from the current set when analyzing the project. If you need to list several symbols, use ';' as the delimiter. Example: ‑‑excludeDefines "DEF1;DEF2". This option is only taken into account when analyzing C# projects.
  • ‑‑appendDefines (-d): a list of symbols that will be added to the current set when analyzing the project. If you need to list several symbols, use ';' as the delimiter. Example: ‑‑appendDefines "DEF1;DEF2". This option is only taken into account when analyzing C# projects.
  • ‑‑selectProjects (-S): a list of analyzed solution's projects (sln) to be analyzed. Other projects will be excluded from the analysis. It supports listing projects using the name of the project file (with or without an extension), using an absolute or relative path. If you need to list multiple projects, use ';' as the delimiter. Example: ‑‑selectProjects Project1;"Project 2.vcxproj";".\Project3\Project3.csproj".
  • ‑‑excludeProjects (-E): a list of projects in the analyzed solution (sln) that will be excluded from the analysis. It supports listing projects using the name of the project file (with or without an extension), using an absolute or relative path. If you need to list multiple projects, use ';' as the delimiter. Example: ‑‑excludeProjects Project1;"Project 2.vcxproj";".\Project3\Project3.csproj".
  • ‑‑rulesConfig (-C): path to the .pvsconfig diagnostics configuration file. It can be used together with configuration files from projects / solutions and configuration files from directories:
    • Windows: "%AppData%\PVS-Studio\";
    • Linux / macOS: "~/.config/PVS-Studio/".
  • ‑‑useSuppressFile (-u): path to the suppress files. You can specify several suppress files via this parameter. Use the ';' character to separate paths to suppress files. For example: -u "path\to\test.suppress;path\to\test.suppress.json". Read more about message suppression mode in this section of the documentation.
  • ‑‑disableLicenseExpirationCheck (-h): resets return code and disables license expiration warning when the license is about to expire.
  • ‑‑intermodular (-I): enables the intermodular analysis mode for C and C++ projects. In this mode, the analyzer performs a deeper code analysis by increasing the analysis time. The C# analyzer provides the intermodular analysis by default.
  • ‑‑redirectOutputToLog: redirects error messages from stdOut\strErr streams to analysis report file.

The console utility also has additional modes of operation:

  • credentials – intended to activate the license;
  • suppression – is intended for specific actions on the suppress files (to create empty suppress files, to suppress/unsuppress individual warnings, to get statistics on the suppress files).

The "suppression" mode has additional flags that are not present in the main mode (or the flags have a different name):

  • ‑‑mode (-m): using this flag you can specify the submode for working with suppress files:
    • CreateEmptySuppressFiles creates empty suppress files next to project files (.csproj/.vcxproj) by the specified pattern of the file name (‑‑suppressFilePattern flag). If the flag of the pattern is omitted, empty suppress files are created with the project name. This mode takes into account the flag marking suppress files with the primary tag (‑‑markAsPrimary). Primary suppress files are used to suppress several warnings selected in the PVS-Studio Output Window for Visual Studio;
    • Suppress allows suppression of individual warnings from the analyzer's report file (‑‑analyzerReport). The suppressed warnings from the analyzer report are selected using filters: groups (‑‑groups), diagnostic codes (‑‑errorCodes), paths (‑‑files). This mode takes into account the ‑‑markAsPrimary flag;
    • UnSuppress mode unsuppresses warnings from the passed analyzer report. UnSuppress is similar to Suppress mode in terms of the flags used, except for the ‑‑markAsPrimary flag. It is not used in this mode;
    • FilterFromSuppress filters the messages in the existing report file (.plog, .json, or unparsed output of the C++ core) without running the analysis. This mode filters the messages using suppress files located next to project/solution files. Another option: pass the path to the suppress files using the –useSuppressFile (-u) flag. The file with results is saved next to the report file passed. The file is named with postfix '_filtered';
    • CountSuppressedMessages calculates the number of suppressed warnings in all suppress files. This mode can also calculate the number of relevant warnings in suppress files. If you pass the full report file (via the -o flag), you can see how many warnings in the suppress base are still relevant. You can also learn statistics for each suppress file if you run this mode with the '-r' flag;
    • UpdateSuppressFiles updates suppress files and deletes the warnings that are not included in the report file passed. Please note that this mode requires a full report containing suppressed warnings. A full report is created each time the analysis is started if there are suppressed warnings. The full report file is named "*_WithSuppressedMessages.*" and is located next to the main report file. If you run this mode with a report file that doesn't contain suppressed warnings, all suppress files will be cleared.
  • ‑‑analyzerReport (-R): the path to the analyzer report whose warnings are to be used in processing. Similar to -o flag from the main mode;
  • ‑‑msBuildProperties (-b): key-value pairs. It is similar to -msBuildProperties (-m) flag from the main mode of PVS-Studio-Cmd.exe;
  • ‑‑markAsPrimary (-M): marks suppress files as primary suppress files;
  • ‑‑suppressFilePattern (-P): the name pattern for creating/using suppress files;
  • ‑‑logModifiedFiles (-l): paths to all modified project and suppress files are written to the file passed in this flag. Both absolute and relative file paths can be used. A file will be created or overwritten if at least one project or suppress file has been modified. The same information is output to the console if the ‑‑progress (-r) flag is specified;
  • ‑‑groups (-g): the filter of warnings from the analyzer report (-R) by diagnostic groups with warning certainty level. Example: GA:1,2,3|OWASP|64:2;
  • ‑‑errorCodes (-E): the filter of warnings from the analyzer report (-R) by the analyzer diagnostic codes. Example: V501,V1001,V3001;
  • ‑‑files (-f): the filter of warnings from the analyzer report (-R) by paths. Example: ‑‑files absolute/path/directory*3,8,11|relative/path/file*1|fileName

PVS-Studio command-line version supports all settings on filtering/disabling messages available in the IDE plugin for Visual Studio. You can either set them manually in the xml file, passed through the '‑‑settings' argument, or use the settings specified through the UI plugin, without passing this argument. Note that the PVS-Studio IDE plugin uses an individual set of settings for each user in the system.

Only relevant for PVS-Studio_Cmd. If you have installed multiple instances of PVS-Studio of different versions for the current system user, all instances of the program will use the installation directory specified during the last installation. To avoid conflicts in the analyzer's operation, in the settings passed with the ‑‑settings (-s) argument, the path to the installation directory (the value of the <InstallDir> element) must be specified.

Specification of individual files for analysis

PVS-Studio_Cmd allows you to selectively check individual files (for example, only files that have been modified) specified in the list passed using the '‑‑sourceFiles' (-f) flag. This significantly reduces the time required for analysis and also enables you to get the analyzer report only for specific changes in the source code.

The file list is a simple text file that contains line-by-line paths to the files being checked. Relative file paths will be expanded relative to the current working directory. You can specify both compiled source files (c/cpp for C++ and cs for C#), and header files (h/hpp for C++).

To get a list of changed files for the '‑‑sourceFiles' (-f) flag, you can use version control systems (SVN, Git, etc.). They provide the analyzer with up-to-date information about changes in the code.

When you use the mode to analyze C and C++ files, a compilation dependency cache is generated, which will be used for subsequent analysis runs. By default, dependency caches are saved in a special '.pvs-studio' subdirectory where project files (.vcxproj) are located. If necessary, you can change their storage location using the '‑‑dependencyRoot' (-D) flag. You can also use the ‑‑dependencyCacheSourcesRoot (-R) flag to generate dependency cache files with relative paths, allowing the same dependency cache file to be used on different systems.

You can find more detailed information about dependency caches in the corresponding section of the documentation for C++ projects.

Wildcard filtration of the analyzed

To specify the list of analyzed files with path patterns, you need to pass a specially formatted XML file to the '‑‑sourceFiles' (-f) flag. It accepts the list of absolute and relative paths and/or wildcards to analyzed files.

<SourceFilesFilters>
  <SourceFiles>
    <Path>C:\Projects\Project1\source1.cpp</Path>
    <Path>\Project2\*</Path>
    <Path>source_*.cpp</Path>
  </SourceFiles>
  <SourcesRoot>C:\Projects\</SourcesRoot>
</SourceFilesFilters>

Intermodular analysis mode

Enabling this mode allows the analyzer to consider information not only from the analyzed file, but also from files that relate to the analyzed file. This allows for deeper and more qualitative analysis. However, it takes extra time to collect the necessary information, which will affect the time you analyze your project.

This mode is relevant to C and C++ projects. C# projects provide cross-modular analysis by default.

To start the intermodular analysis, pass the ‑‑intermodular flag to the command-line utility.

Command-line tools exit codes

The PVS-Studio_Cmd / pvs-studio-dotnet utilities have several non-zero exit codes that don't indicate a problem with the utility itself, i.e. even if the utility returned not '0', it doesn't mean that it crashed. The exit code is a bit mask that masks all possible states that occurred during the operation of the utility. For example, the utility will return a non-zero code if the analyzer finds potential errors in the code being checked. This allows you to handle this situation separately, for example, on the build server, when the analyzer usage policy doesn't imply the presence of warnings in the code uploaded in the version control system.

PVS-Studio_Cmd exit codes (Windows)

Let's look at all possible utility state codes that form the bit mask of the return code.

  • '0' - the analysis was completed successfully, no errors were found in the code being checked;
  • '1' - an error (crash) of the analyzer when checking one of the files;
  • '2' - a general (non-specific) error during the analyzer operation, intercepted exception during operation. This usually signals the presence of an error in the analyzer code itself and is followed by this error's stack trace in stderr. If you stumbled upon such an error, please, help us make the analyzer better and send this stack trace to us;
  • '4' - some of the passed command-line arguments are incorrect. Possible causes: an incorrect or empty path to a file, an incorrect output format of the analyzer report;
  • '8' - the specified project, solution, or analyzer settings file wasn't found. Possible causes: an incorrect or empty path to a file, a project within a solution was renamed or deleted;
  • '16' - the specified configuration and / or platform weren't found in the solution file. Possible causes: a project configuration (platform) was renamed/deleted/not configured;
  • '32' - the solution or project file isn't supported or contains errors. Possible causes: a solution or project file structure is corrupted, the analysis of the Unreal Engine project is started not via the Unreal Build Tool integration, incorrect value of the VCTargetsPath macro in MSBuild projects;
  • '64' - invalid extension of the solution or project being checked;
  • '128' - invalid or expired analyzer license. Possible causes: features exclusive to the Enterprise license are used with a Team license enabled or after the license has expired;
  • '256' - potential errors were found in the code being checked. Possible causes: there is at least one message not in the "Fails" group;
  • '512' - an error occurred while performing message suppression (suppression mode or ‑‑suppressAll flag is used). Caused by loading an invalid report file or suppress file;
  • '1024' - indicates that the analyzer license will expire within a month;

Here is an example of a Windows batch script for decrypting the return code of the PVS-Studio_Cmd utility:

@echo off

"C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe"
-t "YourSolution.sln" -o "YourSolution.plog"

set /A FilesFail = "(%errorlevel% & 1) / 1"
set /A GeneralExeption = "(%errorlevel% & 2) / 2"
set /A IncorrectArguments = "(%errorlevel% & 4) / 4"
set /A FileNotFound = "(%errorlevel% & 8) / 8"
set /A IncorrectCfg = "(%errorlevel% & 16) / 16"
set /A InvalidSolution = "(%errorlevel% & 32) / 32"
set /A IncorrectExtension = "(%errorlevel% & 64) / 64"
set /A IncorrectLicense = "(%errorlevel% & 128) / 128"
set /A AnalysisDiff = "(%errorlevel% & 256) / 256"
set /A SuppressFail = "(%errorlevel% & 512) / 512"
set /A LicenseRenewal = "(%errorlevel% & 1024) / 1024"

if %FilesFail% == 1 echo FilesFail
if %GeneralExeption% == 1 echo GeneralExeption
if %IncorrectArguments% == 1 echo IncorrectArguments
if %FileNotFound% == 1 echo FileNotFound
if %IncorrectCfg% == 1 echo IncorrectConfiguration
if %InvalidSolution% == 1 echo IncorrectCfg
if %IncorrectExtension% == 1 echo IncorrectExtension
if %IncorrectLicense% == 1 echo IncorrectLicense
if %AnalysisDiff% == 1 echo AnalysisDiff
if %SuppressFail% == 1 echo SuppressFail
if %LicenseRenewal% == 1 echo LicenseRenewal

pvs-studio-dotnet exit codes (Linux / macOS)

Note. Since the maximum value of the exit code under Unix is limited by 255, exit codes of the PVS-Studio_Cmd (where the exit code may exceed 255) and pvs-studio-dotnet utilities are different.

Let's look at all possible utility state codes that form the bit mask of the return code.

  • '0' - analysis was successfully completed, no issues were found in the source code;
  • '1' - incorrect or out-of-date analyzer license. It is triggered when the features exclusive to the Enterprise license are used with the Team license enabled or after the license has expired;
  • '2' - general error in the analyzer's operation. This includes missed command-line arguments, invalid solution or project specified for analysis, an error inside the analyzer, etc. If an error message is followed by a stack trace, please help us improve the analyzer by sending it to us;
  • '4' - indicates that the analyzer license will expire within a month;
  • '8' - some issues were found in the source code. It is triggered when at least one message is not in the "Fails" group;

Analysis run from the command line for C/C++ projects that don't use the Visual Studio build system

Note. This section is relevant for Windows. Analysis of C++ projects on Linux / macOS is described in the corresponding section of the documentation.

If your C/C++ project doesn't use standard Visual Studio build systems (VCBuild/MSBuild) or even uses your own build system / make files via NMAKE Visual Studio projects, you will not be able to check such a project using PVS-Studio_Cmd.

In this case, you can use the compiler monitoring system, which allows you to analyze projects regardless of their build system, "intercepting" the start of compilation processes. The compilation monitoring system can be used either from the command line or through the user interface of the C and C++ Compiler Monitoring UI application.

You can also directly embed the command line launch of the analyzer core right into your build system. Mind you, this will require writing a call to the PVS-Studio analyzer.exe core for each compiled file, similar to the the way how the C++ compiler is called.

Effect of PVS-Studio settings on command line run; filtering and converting analysis results (plog\json file)

When you run code analysis from the command line, the default settings are the same as when you run analysis from the IDE (Visual Studio / Rider). You can also specify which settings file to use via the ‑‑settings argument, as described above.

For example, as for the filter system (Keyword Message Filtering and Detectable Errors), it is NOT used when analyzing from the command line. Which means that the report file will contain all error messages regardless of the parameters you set. However, when you upload the results file to the IDE, the filters will already be applied. This is because filters are applied dynamically to results. The same occurs when running from the IDE as well. This is very convenient, because when you get a list of messages, you may want to disable some of them (for example, V201). Just disable them in the settings and the corresponding messages will disappear from the list WITHOUT restarting the analysis.

The analyzer report format isn't intended for direct display or human reading. However, if you need to filter the analysis results in some way and convert them to a "readable" view, you can use the PlogConverter utility distributed with PVS-Studio.

To work with reports in different formats, you need to use different utilities:

  • .plog – PlogConverter.exe (available only on Windows);
  • .json – plog-converter (Linux, macOS).

The source code of both utilities is open and available for download: PlogConverter; plog-converter, which allows you to simply add support for new formats based on existing algorithms.

These utilities are described in more detail in the corresponding sections of the documentation: