Skip to content

Commit bb99ade

Browse files
committed
reworked to make command running more generic
1 parent 0b220b1 commit bb99ade

3 files changed

Lines changed: 37 additions & 50 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
- **Diagnostic cleanup**: When you close a file, its diagnostics are automatically cleared.
1111
- **Project file support**: You can feed your project file to cppcheck through the `--project` flag in the `cppcheck-official.arguments` field in the extension settings.
1212
- **Warning notes**: Display notes for warnings when those are available
13-
- **Dynamic config**: The extension supports runing scripts to generate arguments to pass to cppcheck. This can be done by setting the argument to command to run wrapped with \${}, e.g. `--project=${bash path/to/script.sh}`. The script is expected to output the argument wrapped with \${}, so with the argument `--project=${bash path/to/script.sh}` the script will be run and expected to create a compile_commands.json file whose path will be printed out as such: `${path/to/compile_commands.json}`.
13+
- **Dynamic config**: The extension supports runing scripts to generate arguments to pass to cppcheck. This can be done by including the command in the argument field wrapped with \${}, e.g. `--suppress=memleak:src/file1.cpp ${bash path/to/script.sh}`. The script is expected to output the argument wrapped with \${}. If the script e.g. creates a project file it should print out `\${--project=path/to/projectfile.json}`. This output will be spliced in to the argument string as such: `--suppress=memleak:src/file1.cpp --project=path/to/projectfile.json`.
1414
## Requirements
1515

1616
**Cppcheck** must be installed on your system.

src/extension.ts

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as cp from 'child_process';
33
import * as path from "path";
44
import * as xml2js from 'xml2js';
55

6-
import { runScript } from './util/scripts';
6+
import { runCommand } from './util/scripts';
77
import { resolvePath } from './util/path';
88

99
enum SeverityNumber {
@@ -12,8 +12,8 @@ enum SeverityNumber {
1212
Error = 2
1313
}
1414

15-
// If a script generates arguments at extension activation they are saved in dynamicArgs
16-
const dynamicArgs : Array<string> = [];
15+
// The arguments field may contain a script to evaluate on startup. The result of this evaluation is stored in this variable
16+
var processedArgs = '';
1717

1818
const criticalWarningTypes = [
1919
'cppcheckError',
@@ -70,19 +70,16 @@ export async function activate(context: vscode.ExtensionContext) {
7070
// If an argument requires us to run any scripts we do it here
7171
const config = vscode.workspace.getConfiguration();
7272
const args = config.get<string>("cppcheck-official.arguments", "");
73-
const argsWithScripts = args.split("--").filter((arg) => arg.includes('${'));
74-
for (const arg of argsWithScripts) {
75-
// argType will look like e.g. --project
76-
const argType = arg.split("=")[0];
77-
const argValue = arg.split("=")[1];
78-
// Remove ${ from the beginning and slice } away from the end of argValue
79-
const scriptCommand = argValue.split("{")[1].split("}")[0];
80-
const scriptOutput = await runScript(scriptCommand);
81-
// We expect the script output that we are to set the argument to will be wrapped with ${}
82-
const scriptOutputPath = scriptOutput.split("${")[1].split("}")[0];
83-
dynamicArgs.push(`${argType}=${scriptOutputPath}`);
84-
};
85-
73+
if (args.includes('${')) {
74+
const scriptCommand = args.split("${")[1].split("}")[0];
75+
const scriptOutput = await runCommand(scriptCommand);
76+
// We expect the script output that is to be used as arguments will be wrapped with ${}
77+
const scriptOutputTrimmed = scriptOutput.split("${")[1].split("}")[0];
78+
processedArgs = args.split("${")[0] + scriptOutputTrimmed + args.split("}")?.[1];
79+
} else {
80+
processedArgs = args;
81+
}
82+
8683
// set up a map of timers per document URI for debounce for continuous analysis triggers
8784
// I.e. document has been changed -> DEBOUNCE_MS time passed since last change -> run cppcheck
8885
const debounceTimers: Map<string, NodeJS.Timeout> = new Map();
@@ -105,7 +102,6 @@ export async function activate(context: vscode.ExtensionContext) {
105102

106103
const config = vscode.workspace.getConfiguration();
107104
const isEnabled = config.get<boolean>("cppcheck-official.enable", true);
108-
const extraArgs = config.get<string>("cppcheck-official.arguments", "");
109105
const minSevString = config.get<string>("cppcheck-official.minSeverity", "info");
110106
const userPath = config.get<string>("cppcheck-official.path")?.trim() || "";
111107
const commandPath = userPath ? resolvePath(userPath) : "cppcheck";
@@ -130,7 +126,6 @@ export async function activate(context: vscode.ExtensionContext) {
130126
await runCppcheckOnFileXML(
131127
document,
132128
commandPath,
133-
extraArgs,
134129
minSevString,
135130
diagnosticCollection
136131
);
@@ -180,7 +175,6 @@ export async function activate(context: vscode.ExtensionContext) {
180175
async function runCppcheckOnFileXML(
181176
document: vscode.TextDocument,
182177
commandPath: string,
183-
extraArgs: string,
184178
minSevString: string,
185179
diagnosticCollection: vscode.DiagnosticCollection
186180
): Promise<void> {
@@ -191,20 +185,17 @@ async function runCppcheckOnFileXML(
191185
const filePath = document.fileName.replaceAll('\\', '/');
192186
const minSevNum = parseMinSeverity(minSevString);
193187

194-
// Arguments specified with scripts are replaced with script output (dynamicArgs)
195-
const staticArgs = extraArgs.split("--").filter((arg) => !arg.includes("${"));
196-
const allArgs = staticArgs.concat(dynamicArgs);
197188
// Resolve paths for arguments where applicable
198-
const extraArgsParsed = allArgs.map((arg) => {
199-
if (arg.startsWith('project')) {
189+
const argsParsed = processedArgs.split(" ").map((arg) => {
190+
if (arg.startsWith('--project')) {
200191
const splitArg = arg.split('=');
201-
return `--${splitArg[0]}=${resolvePath(splitArg[1])}`;
192+
return `${splitArg[0]}=${resolvePath(splitArg[1])}`;
202193
}
203194
return arg;
204195
});
205196

206197
let proc;
207-
if (extraArgs.includes("--project")) {
198+
if (processedArgs.includes("--project")) {
208199
const args = [
209200
'--enable=all',
210201
'--inline-suppr',
@@ -213,7 +204,7 @@ async function runCppcheckOnFileXML(
213204
'--suppress=missingInclude',
214205
'--suppress=missingIncludeSystem',
215206
`--file-filter=${filePath}`,
216-
...extraArgsParsed,
207+
...argsParsed,
217208
].filter(Boolean);
218209
proc = cp.spawn(commandPath, args, {
219210
cwd: path.dirname(document.fileName),
@@ -226,7 +217,7 @@ async function runCppcheckOnFileXML(
226217
'--suppress=unusedFunction',
227218
'--suppress=missingInclude',
228219
'--suppress=missingIncludeSystem',
229-
...extraArgsParsed,
220+
...argsParsed,
230221
filePath,
231222
].filter(Boolean);
232223
proc = cp.spawn(commandPath, args, {

src/util/scripts.ts

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,22 @@
1-
import { execFile } from "child_process";
1+
import { exec } from "child_process";
22
import { resolvePath } from './path';
3+
import util from 'util';
34

4-
function runScript(scriptCommand: string): Promise<string> {
5-
const scriptParts : string[] = scriptCommand.split(" ");
6-
// ASSUMPTION: script path will be the last part of the command
7-
const scriptPath = scriptParts[scriptParts.length -1];
8-
const absoluteScriptPath = resolvePath(scriptPath);
9-
const joinedCommand = scriptParts.slice(0, scriptParts.length -1).join(" ") + " " + absoluteScriptPath;
10-
return new Promise((resolve, reject) => {
11-
execFile(joinedCommand, [], { cwd: resolvePath('${workspaceFolder}') }, (error, stdout, stderr) => {
12-
if (error) {
13-
reject(error);
14-
return;
15-
}
16-
if (stderr) {
17-
console.warn("Script stderr:", stderr);
18-
}
19-
const result = stdout.trim();
20-
resolve(result);
5+
const execAsync = util.promisify(exec);
6+
7+
async function runCommand(command : string) {
8+
try {
9+
const { stdout, stderr } = await execAsync(command, {
10+
cwd: resolvePath('${workspaceFolder}'),
2111
});
22-
});
23-
}
2412

13+
if (stderr) {
14+
throw new Error(stderr);
15+
}
16+
return stdout;
17+
} catch (error) {
18+
throw error;
19+
}
20+
}
2521

26-
export { runScript };
22+
export { runScript, runCommand };

0 commit comments

Comments
 (0)