blob: 0cc1d4add25c0e36dce7aaeb019b0b137c1e4bd6 [file] [log] [blame] [raw]
GeorgeGribkoveeb6bc52020-05-19 10:48:29 +03001// Copyright (c) 2020, Compiler Explorer Authors
GeorgeGribkovae3f22c2020-05-18 16:57:22 +03002// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6//
7// * Redistributions of source code must retain the above copyright notice,
8// this list of conditions and the following disclaimer.
9// * Redistributions in binary form must reproduce the above copyright
10// notice, this list of conditions and the following disclaimer in the
11// documentation and/or other materials provided with the distribution.
12//
13// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
17// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
23// POSSIBILITY OF SUCH DAMAGE.
GeorgeGribkovae3f22c2020-05-18 16:57:22 +030024
Austin Morton044dcfb2020-09-26 16:59:26 -040025import path from 'path';
GeorgeGribkovae3f22c2020-05-18 16:57:22 +030026
Austin Morton044dcfb2020-09-26 16:59:26 -040027import fs from 'fs-extra';
28
29import * as exec from '../exec';
30import { logger } from '../logger';
31import * as utils from '../utils';
32
33import { BaseTool } from './base-tool';
34
35export class PvsStudioTool extends BaseTool {
Matt Godboltaceaae22021-04-03 18:09:58 -050036 static get key() {
37 return 'pvs-studio-tool';
38 }
Austin Mortonbac07fe2020-09-25 11:21:30 -040039
GeorgeGribkovae3f22c2020-05-18 16:57:22 +030040 constructor(toolInfo, env) {
41 super(toolInfo, env);
Rubén Rincón Blancoccff4b92020-08-04 22:39:02 +020042 this.plogConverterPath = this.env.compilerProps('plogConverter', '/usr/bin/plog-converter');
Rubén Rincón Blanco4e683c52021-02-08 07:15:13 +010043
44 this.addOptionsToToolArgs = false;
GeorgeGribkovae3f22c2020-05-18 16:57:22 +030045 }
46
47 async runTool(compilationInfo, inputFilepath, args) {
Matt Godbolt29125df2020-06-03 07:14:54 -050048 if (compilationInfo.code !== 0) {
Rubén Rincón Blancoccff4b92020-08-04 22:39:02 +020049 return this.createErrorResponse('Unable to start analysis due to compilation error.');
GeorgeGribkovd6fd4e72020-06-03 11:52:47 +030050 }
51
GeorgeGribkovae3f22c2020-05-18 16:57:22 +030052 const sourceDir = path.dirname(inputFilepath);
53
54 // Collecting the flags of compilation
Partouf56e64c12021-03-04 22:08:42 +010055 let compileFlags = utils.splitArguments(compilationInfo.compiler.options);
GeorgeGribkovae3f22c2020-05-18 16:57:22 +030056
57 const includeflags = super.getIncludeArguments(compilationInfo.libraries, compilationInfo.compiler);
58 compileFlags = compileFlags.concat(includeflags);
59
Patrick Quist697c8e92020-09-25 02:09:47 +020060 const libOptions = super.getLibraryOptions(compilationInfo.libraries, compilationInfo.compiler);
61 compileFlags = compileFlags.concat(libOptions);
62
GeorgeGribkovae3f22c2020-05-18 16:57:22 +030063 const manualCompileFlags = compilationInfo.options.filter(option => (option !== inputFilepath));
64 compileFlags = compileFlags.concat(manualCompileFlags);
Partouf3a458c12020-05-22 14:14:49 +020065
GeorgeGribkovcf9494d2020-06-04 16:16:04 +030066 compileFlags = compileFlags.filter(function (flag) {
Rubén Rincón Blancoccff4b92020-08-04 22:39:02 +020067 return flag !== '';
GeorgeGribkovcf9494d2020-06-04 16:16:04 +030068 });
69
GeorgeGribkovae3f22c2020-05-18 16:57:22 +030070 // Deal with args
71 args = [];
72
73 // Necessary arguments
Matt Godbolt5c5bc4d2021-05-27 23:25:37 -050074 args.push('analyze', '--source-file', inputFilepath);
Rubén Rincón Blancoccff4b92020-08-04 22:39:02 +020075 const outputFilePath = path.join(sourceDir, 'pvs-studio-log.log');
Matt Godbolt5c5bc4d2021-05-27 23:25:37 -050076 args.push(
77 '--output-file', outputFilePath,
78 // Exclude directories from analysis
79 '-e', '/opt',
80 '-e', '/usr',
81 '--pvs-studio-path', path.dirname(this.tool.exe) + '/pvs-studio',
82 // TODO: expand this to switch() for all supported compilers:
83 // visualcpp, clang, gcc, bcc, bcc_clang64, iar, keil5, keil5_gnu
84 '--preprocessor',
85 );
Rubén Rincón Blancoccff4b92020-08-04 22:39:02 +020086 if (compilationInfo.compiler.group.includes('clang'))
87 args.push('clang');
GeorgeGribkovae3f22c2020-05-18 16:57:22 +030088 else
Rubén Rincón Blancoccff4b92020-08-04 22:39:02 +020089 args.push('gcc');
GeorgeGribkovae3f22c2020-05-18 16:57:22 +030090
GeorgeGribkov91972db2020-05-29 12:45:41 +030091 // Now let's push the path to the compiler
Rubén Rincón Blancoccff4b92020-08-04 22:39:02 +020092 if (this.tool.compilerLanguage === 'c') {
93 args.push('--cc');
Matt Godbolt29125df2020-06-03 07:14:54 -050094 } else {
Rubén Rincón Blancoccff4b92020-08-04 22:39:02 +020095 args.push('--cxx');
GeorgeGribkov91972db2020-05-29 12:45:41 +030096 }
Matt Godbolt5c5bc4d2021-05-27 23:25:37 -050097 args.push(compilationInfo.compiler.exe, '--cl-params');
GeorgeGribkovae3f22c2020-05-18 16:57:22 +030098 args = args.concat(compileFlags);
Partouf3a458c12020-05-22 14:14:49 +020099
GeorgeGribkovae3f22c2020-05-18 16:57:22 +0300100 // If you are to modify this code,
101 // don't forget that super.runTool() does args.push(inputFilepath) inside.
Partoufb2d10272020-05-28 12:18:25 +0200102 const result = await super.runTool(compilationInfo, inputFilepath, args);
Matt Godbolt142c8242020-05-21 22:55:24 -0500103 if (result.code !== 0) {
104 return result;
105 }
GeorgeGribkovae3f22c2020-05-18 16:57:22 +0300106
107 // Convert log to readable format
Rubén Rincón Blancoccff4b92020-08-04 22:39:02 +0200108 const plogConverterOutputFilePath = path.join(sourceDir, 'pvs-studio-log.err');
GeorgeGribkovae3f22c2020-05-18 16:57:22 +0300109
Matt Godbolt142c8242020-05-21 22:55:24 -0500110 const plogConverterArgs = [];
Rubén Rincón Blancoccff4b92020-08-04 22:39:02 +0200111 plogConverterArgs.push('-t', 'errorfile', '-a', 'FAIL:1,2,3;GA:1,2,3', '-o',
GeorgeGribkovae3f22c2020-05-18 16:57:22 +0300112 plogConverterOutputFilePath,
Rubén Rincón Blancoccff4b92020-08-04 22:39:02 +0200113 'pvs-studio-log.log');
Partouf3a458c12020-05-22 14:14:49 +0200114
115 const plogExecOptions = this.getDefaultExecOptions();
Partoufb2d10272020-05-28 12:18:25 +0200116 plogExecOptions.customCwd = sourceDir;
GeorgeGribkovae3f22c2020-05-18 16:57:22 +0300117
Matt Godbolt142c8242020-05-21 22:55:24 -0500118 const plogConverterResult = await exec.execute(
Partouf3a458c12020-05-22 14:14:49 +0200119 this.plogConverterPath, plogConverterArgs, plogExecOptions);
Matt Godbolt142c8242020-05-21 22:55:24 -0500120 if (plogConverterResult.code !== 0) {
Rubén Rincón Blancoccff4b92020-08-04 22:39:02 +0200121 logger.warn('plog-converter failed', plogConverterResult);
Matt Godbolt142c8242020-05-21 22:55:24 -0500122 return this.convertResult(plogConverterResult, inputFilepath);
123 }
GeorgeGribkovae3f22c2020-05-18 16:57:22 +0300124 let plogConverterOutput = (await fs.readFile(plogConverterOutputFilePath, 'utf8')).toString();
125
Rubén Rincón Blancoccff4b92020-08-04 22:39:02 +0200126 // Sometimes if a code fragment can't be analyzed, PVS-Studio makes a warning for a preprocessed file
GeorgeGribkovae3f22c2020-05-18 16:57:22 +0300127 // (*.PVS-Studio.i)
128 // This name can't be parsed by utils.parseOutput
129 // so let's just replace it with a source file name.
Rubén Rincón Blancoccff4b92020-08-04 22:39:02 +0200130 plogConverterOutput = plogConverterOutput.replace(sourceDir + '/example.PVS-Studio.i', inputFilepath);
GeorgeGribkovae3f22c2020-05-18 16:57:22 +0300131
132 result.stdout = utils.parseOutput(plogConverterOutput, plogConverterResult.filenameTransform(inputFilepath));
133
134 // Now let's trim the documentation link
Matt Godboltd9ebcd12020-05-21 22:26:07 -0500135 if (result.stdout.length > 0) {
Maxim Stefanovc909aee2021-06-22 04:48:54 +0300136 const idx = result.stdout[0].text.indexOf('The documentation for all analyzer warnings is available here:');
Maxim Stefanov3fe2a212021-06-25 16:38:14 +0300137 result.stdout[0].text = result.stdout[0].text.substring(idx).concat('\n\n');
GeorgeGribkovae3f22c2020-05-18 16:57:22 +0300138 }
139
Rubén Rincón Blancoccff4b92020-08-04 22:39:02 +0200140 // The error output is unnecessary for the user
Partouf101d6f12020-05-26 01:02:08 +0200141 // If you need to debug PVS-Studio-tool, you can comment out this line
142 result.stderr = [];
143
GeorgeGribkovae3f22c2020-05-18 16:57:22 +0300144 return result;
145 }
Partouf0438c8f2020-05-26 03:17:55 +0200146
147 getDefaultExecOptions() {
148 const execOptions = super.getDefaultExecOptions();
Matt Godboltaceaae22021-04-03 18:09:58 -0500149 execOptions.env = {
150 ...execOptions.env,
151 PATH: process.env.PATH + ':/opt/compiler-explorer/pvs-studio-latest/bin',
152 };
Partouf0438c8f2020-05-26 03:17:55 +0200153
154 return execOptions;
155 }
GeorgeGribkovae3f22c2020-05-18 16:57:22 +0300156}