From c11d208e1e814f0e73dc889ee69257e25c6f324b Mon Sep 17 00:00:00 2001 From: Carlos Gomes Date: Wed, 11 May 2022 17:06:26 +0200 Subject: [PATCH] feat: Adding extra arguments to execution (#24) * feat: Adding extra arguments to execution * Allow extra args from tasks.json * Update README to include how to set Extra Arguments --- .vscode/launch.json | 7 +++- README.md | 53 ++++++++++++++++++++++++++-- package.json | 10 ++++++ src/Tasks/MakefileTask.ts | 5 +++ src/Tasks/createMakefileTask.test.ts | 47 +++++++++++++++++++++++- src/Tasks/createMakefileTask.ts | 8 +++-- src/extension.ts | 1 + src/shared/config.ts | 5 +++ src/shared/exec.ts | 38 -------------------- src/shared/exists.ts | 30 ---------------- 10 files changed, 129 insertions(+), 75 deletions(-) delete mode 100644 src/shared/exec.ts delete mode 100644 src/shared/exists.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 29c0ed5..b1e5b61 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -32,7 +32,12 @@ "preLaunchTask": "Compile Test", "env": { "NODE_ENV": "test" - } + }, + "skipFiles": [ + "/**", + "internal/**", + "**/Visual Studio Code.app/**" + ] } ] } diff --git a/README.md b/README.md index fc24374..690c541 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Run `make` tasks and targets from VS Code command picker or via tasks menu. - Tasks-View listing all available targets - Multiple workspaces ready! - **NEW**: Set the executable path to your `make` command +- **NEW**: Add extra arguments to the `make` execution command ### Running from the command picker: @@ -22,10 +23,58 @@ Run `make` tasks and targets from VS Code command picker or via tasks menu. ![command picker](images/task-tree.gif) +### Adding extra arguments/flags to the `make` execution command + +The setting `make-task-provider.extraArguments` can be set with an array of strings with extra arguments which will be added to the `make` command. + +```json +"make-task-provider.extraArguments": [ + "--always-make", + "--ignore-errors" +] +``` + +Extra arguments will be appended to the `make` command as follows: + +```text +[make bin path] -f [makefile path] [...extra arguments] [target name] +``` + +For example, If the target `build` is triggered, it will be executed as: + +```shell +$ make -f ./Makefile --always-make --ignore-errors build +``` + +It is also possible to customize flags to a single target by using VSCode task customization: + +On your project's `tasks.json`, set the following: + +```json +{ + "version": "2.0.0", + "tasks": [ + { + "type": "make", + "targetName": "build", + "makeFileRelativePath": "Makefile", + "problemMatcher": [], + "label": "make: build with extra args", + "args": [ + "--always-make", + "--ignore-errors" + ] + } + ] +} +``` + +If `make-task-provider.extraArguments` is set along with `args` in the tasks.json, +**ALL** the extra arguments will be added to the `make` command. + --- -**PS:** This extension is **NOT** a re-implementation of `make`, -so you need to have `make` executables available on your system. +**PS:** This extension is **NOT** a re-implementation of `make`, so you must have `make` executables available on your system. If `make` is not on your `PATH`, you can customize the executable path individually based on your operating system: diff --git a/package.json b/package.json index 5b9d2bf..d73cdc5 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,10 @@ "makeFileRelativePath": { "type": "string", "description": "Relative path to the Makefile containing your targets to be executed" + }, + "args": { + "type": "array", + "description": "Arguments to pass to the make target as: make {...args} {target}" } } } @@ -164,6 +168,12 @@ "open" ], "markdownDescription": "The action to trigger when clicking on a target on the targets list: `open` or `run`, the default is `run`." + }, + "make-task-provider.extraArguments": { + "scope": "resource", + "type": "array", + "default": [], + "markdownDescription": "Additional flags to be added to the run command like: `make {...extraFlags} {target}`" } } } diff --git a/src/Tasks/MakefileTask.ts b/src/Tasks/MakefileTask.ts index 983c734..6d33245 100644 --- a/src/Tasks/MakefileTask.ts +++ b/src/Tasks/MakefileTask.ts @@ -11,6 +11,11 @@ export interface MakefileTaskDefinition extends vscode.TaskDefinition { * The relative path to the Makefile containing the Target task */ makeFileRelativePath: string; + + /** + * Extra arguments to be passed to the make command + */ + args?: string[]; } export interface MakefileTask extends vscode.Task { diff --git a/src/Tasks/createMakefileTask.test.ts b/src/Tasks/createMakefileTask.test.ts index c6fe0f7..249bd06 100644 --- a/src/Tasks/createMakefileTask.test.ts +++ b/src/Tasks/createMakefileTask.test.ts @@ -1,4 +1,5 @@ import path from 'path'; +import sinon from 'sinon'; import vscode from 'vscode'; import { TYPE } from '../shared/constants'; @@ -35,7 +36,7 @@ describe('Create Makefile tasks', () => { }, 'task.definition', ); - expect(task.scope).to.equal(workspace, 'task.source'); + expect(task.scope).to.equal(workspace, 'task.scope'); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const { execution }: { execution: vscode.ShellExecution } = task; @@ -54,4 +55,48 @@ describe('Create Makefile tasks', () => { const task = createMakefileTask(fakeDefinition, workspace, makefileUri); expect(task.definition).to.equal(fakeDefinition); }); + + it('should pass extra arguments when defined on settings', () => { + const extraArgsMock = ['--extra-arg', '-B']; + const getConfigurationMocked = sinon.stub(vscode.workspace, 'getConfiguration'); + + getConfigurationMocked.onFirstCall().callThrough(); + getConfigurationMocked.onSecondCall().returns({ + get: () => extraArgsMock, + } as any); + getConfigurationMocked.callThrough(); + + const name = 'test_with_args'; + const task = createMakefileTask(name, workspace, makefileUri); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const { execution }: { execution: vscode.ShellExecution } = task; + expect(execution.command).to.equal('make'); + expect(execution.args).to.deep.equal(['-f', 'Makefile', ...extraArgsMock, name]); + }); + + it('should merge extra arguments from settings and tasks.json', () => { + const extraArgsMock = ['--extra-unique', '-B']; + const getConfigurationMocked = sinon.stub(vscode.workspace, 'getConfiguration'); + + getConfigurationMocked.onFirstCall().callThrough(); + getConfigurationMocked.onSecondCall().returns({ + get: () => extraArgsMock, + } as any); + getConfigurationMocked.callThrough(); + + const name = 'test_with_args_merged'; + const fakeDefinition = { + type: TYPE, + targetName: name, + makeFileRelativePath: 'Makefile', + args: ['--extra-arg', '-B'], + }; + + const task = createMakefileTask(fakeDefinition, workspace, makefileUri); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const { execution }: { execution: vscode.ShellExecution } = task; + + expect(execution.args).to.deep.equal(['-f', 'Makefile', ...extraArgsMock, '--extra-arg', name]); + }); }); diff --git a/src/Tasks/createMakefileTask.ts b/src/Tasks/createMakefileTask.ts index 91c36b0..089ad46 100644 --- a/src/Tasks/createMakefileTask.ts +++ b/src/Tasks/createMakefileTask.ts @@ -1,7 +1,7 @@ import path from 'path'; import vscode from 'vscode'; -import { getMakeExecutablePath } from '../shared/config'; +import { getExtraArguments, getMakeExecutablePath } from '../shared/config'; import { TYPE } from '../shared/constants'; import { getFileRelativePath } from '../shared/workspaceFiles'; @@ -40,7 +40,7 @@ export function createMakefileTask( makefileUri: vscode.Uri, ): MakefileTask { const definition = getDefinition(nameOrDefinition, makefileUri.fsPath, folder.uri.fsPath); - const { targetName, makeFileRelativePath } = definition; + const { targetName, makeFileRelativePath, args = [] } = definition; const cwd = path.dirname(makefileUri.fsPath); const makefile = path.basename(makeFileRelativePath); @@ -48,6 +48,8 @@ export function createMakefileTask( const options: vscode.ShellExecutionOptions = { cwd }; // TODO: check performance degradation here const makeBin = getMakeExecutablePath(folder); + const extraArguments = getExtraArguments(folder); + const uniqueArgs = Array.from(new Set([...extraArguments, ...args])); const task = ( new vscode.Task( @@ -55,7 +57,7 @@ export function createMakefileTask( folder, targetName, TYPE, - new vscode.ShellExecution(makeBin, ['-f', makefile, targetName], options), + new vscode.ShellExecution(makeBin, ['-f', makefile, ...uniqueArgs, targetName], options), [], ) ); diff --git a/src/extension.ts b/src/extension.ts index bd3d14c..8a70521 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -31,6 +31,7 @@ export function activate(context: vscode.ExtensionContext): void { vscode.workspace.onDidChangeConfiguration((e) => { const shouldRefresh = e.affectsConfiguration(CONFIG_KEYS.autoDetect) || + e.affectsConfiguration(CONFIG_KEYS.extraArguments) || e.affectsConfiguration(CONFIG_KEYS.makeExecutable); if (shouldRefresh) { diff --git a/src/shared/config.ts b/src/shared/config.ts index 854c4f2..16b1ce8 100644 --- a/src/shared/config.ts +++ b/src/shared/config.ts @@ -10,6 +10,7 @@ export const CONFIG_KEYS = { makefileNames: `${APP_NAME}.makefileNames`, makeExecutable: `${APP_NAME}.${getUserPlatformKey() ?? 'unix.makeExecutable'}`, telemetry: `${APP_NAME}.telemetry`, + extraArguments: `${APP_NAME}.extraArguments`, }; export const COMMANDS = { @@ -99,3 +100,7 @@ export function getUserPlatformKey(): string | null { export function getTargetsExplorerClickAction(scope?: vscode.ConfigurationScope): 'run' | 'open' { return getFolderConfig(scope).get<'run' | 'open'>('targetsExplorerClickAction', 'run'); } + +export function getExtraArguments(scope?: vscode.ConfigurationScope): string[] { + return getFolderConfig(scope).get('extraArguments', []); +} diff --git a/src/shared/exec.ts b/src/shared/exec.ts deleted file mode 100644 index 76c8416..0000000 --- a/src/shared/exec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import cp, { ExecException } from 'child_process'; - -import { trackException } from '../telemetry/tracking'; - -import { showGenericErrorNotification } from './errorNotifications'; -import getOutputChannel from './getOutputChannel'; - -export type ExecStdIoResult = { - stdout: string; - stderr: string; - error?: ExecException; -}; - -/** - * Executes a command string in the users shell - * Never throws an Error, check the `result.error` - */ -export default function exec(command: string, options: cp.ExecOptions): Promise { - return new Promise((resolve) => { - cp.exec(command, options, (error, stdout, stderr) => { - if (error) { - resolve({ error, stdout, stderr }); - - trackException(error, { - action: 'exec', - cmd: error.cmd, - stdout, - }); - - getOutputChannel().appendLine(error.message); - showGenericErrorNotification(); - return; - } - - resolve({ stdout, stderr }); - }); - }); -} diff --git a/src/shared/exists.ts b/src/shared/exists.ts deleted file mode 100644 index c29545a..0000000 --- a/src/shared/exists.ts +++ /dev/null @@ -1,30 +0,0 @@ -import fs from 'fs'; - -import { trackException } from '../telemetry/tracking'; - -import getOutputChannel from './getOutputChannel'; - -/** - * Checks if a file exists in the given path - */ -export default function exists(file: string): Promise { - return new Promise((resolve) => { - fs.access(file, fs.constants.F_OK, (error) => { - if (error) { - getOutputChannel().appendLine(error.message); - - trackException(error, { - category: 'Files', - action: 'exists', - filePath: error.path, - errno: error.errno, - code: error.code, - }); - - resolve(false); - } else { - resolve(true); - } - }); - }); -}