Skip to content

Commit

Permalink
"F5" using the correct build task (#6420)
Browse files Browse the repository at this point in the history
* shows details correctly

* change default name

* generating unique names

* lint errors

* lint errors

* modify resolvetask

* modify details
  • Loading branch information
elahehrashedi authored Nov 3, 2020
1 parent 7d7b898 commit df8523e
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 31 deletions.
42 changes: 33 additions & 9 deletions Extension/src/Debugger/configurationProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as debugUtils from './utils';
import * as os from 'os';
import * as path from 'path';
import * as vscode from 'vscode';
import { CppBuildTaskDefinition } from '../LanguageServer/cppBuildTaskProvider';
import { CppBuildTask, CppBuildTaskDefinition } from '../LanguageServer/cppBuildTaskProvider';
import * as util from '../common';
import * as fs from 'fs';
import * as Telemetry from '../telemetry';
Expand Down Expand Up @@ -113,25 +113,49 @@ class CppConfigurationProvider implements vscode.DebugConfigurationProvider {
* Returns a list of initial debug configurations based on contextual information, e.g. package.json or folder.
*/
async provideDebugConfigurations(folder?: vscode.WorkspaceFolder, token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration[]> {
let buildTasks: vscode.Task[] = await cppBuildTaskProvider.getTasks(true);
if (buildTasks.length === 0) {
return Promise.resolve(this.provider.getInitialConfigurations(this.type));
}
const defaultConfig: vscode.DebugConfiguration = this.provider.getInitialConfigurations(this.type).find((config: any) =>
isDebugLaunchStr(config.name) && config.request === "launch");
console.assert(defaultConfig, "Could not find default debug configuration.");

const platformInfo: PlatformInformation = await PlatformInformation.GetPlatformInformation();
const platform: string = platformInfo.platform;

// Import the tasks from tasks.json file.
const buildTasksJson: CppBuildTask[] = await cppBuildTaskProvider.getJsonTasks();

// Provide detected tasks by cppBuildTaskProvider.
const buildTasksDetected: CppBuildTask[] = await cppBuildTaskProvider.getTasks(true);

// Rename the provided tasks that has same name as tasks in tasks.json.
const buildTasksDetectedRename: CppBuildTask[] = buildTasksDetected.map(taskDetected => {
for (const taskJson of buildTasksJson) {
if ((taskDetected.definition.label as string) === (taskJson.definition.label as string)) {
taskDetected.name = cppBuildTaskProvider.provideUniqueTaskLabel(taskJson.definition.label, buildTasksJson);
taskDetected.definition.label = taskDetected.name;
break;
}
}
return taskDetected;
});

let buildTasks: CppBuildTask[] = [];
buildTasks = buildTasks.concat(buildTasksJson, buildTasksDetectedRename);

if (buildTasks.length === 0) {
return Promise.resolve(this.provider.getInitialConfigurations(this.type));
}

if (buildTasks.length === 0) {
return Promise.resolve(this.provider.getInitialConfigurations(this.type));
}
// Filter out build tasks that don't match the currently selected debug configuration type.
buildTasks = buildTasks.filter((task: vscode.Task) => {
buildTasks = buildTasks.filter((task: CppBuildTask) => {
if (defaultConfig.name.startsWith("(Windows) ")) {
if (task.name.startsWith("C/C++: cl.exe")) {
if ((task.definition.command as string).includes("cl.exe")) {
return true;
}
} else {
if (!task.name.startsWith("C/C++: cl.exe")) {
if (!(task.definition.command as string).includes("cl.exe")) {
return true;
}
}
Expand All @@ -153,7 +177,7 @@ class CppConfigurationProvider implements vscode.DebugConfigurationProvider {
newConfig.program = platform === "win32" ? exeName + ".exe" : exeName;
// Add the "detail" property to show the compiler path in QuickPickItem.
// This property will be removed before writing the DebugConfiguration in launch.json.
newConfig.detail = definition.command;
newConfig.detail = task.detail ? task.detail : definition.command;

return new Promise<vscode.DebugConfiguration>(resolve => {
if (platform === "darwin") {
Expand Down
73 changes: 51 additions & 22 deletions Extension/src/LanguageServer/cppBuildTaskProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,10 @@ export class CppBuildTask extends Task {
export class CppBuildTaskProvider implements TaskProvider {
static CppBuildScriptType: string = 'cppbuild';
static CppBuildSourceStr: string = "C/C++";
private tasks: CppBuildTask[] | undefined;

constructor() { }

public async provideTasks(): Promise<CppBuildTask[]> {
if (this.tasks) {
return this.tasks;
}
return this.getTasks(false);
}

Expand All @@ -47,17 +43,14 @@ export class CppBuildTaskProvider implements TaskProvider {
const execution: ProcessExecution | ShellExecution | CustomExecution | undefined = _task.execution;
if (!execution) {
const definition: CppBuildTaskDefinition = <any>_task.definition;
_task = this.getTask(definition.command, false, definition.args ? definition.args : [], definition);
_task = this.getTask(definition.command, false, definition.args ? definition.args : [], definition, _task.detail);
return _task;
}
return undefined;
}

// Generate tasks to build the current file based on the user's detected compilers, the user's compilerPath setting, and the current file's extension.
public async getTasks(appendSourceToName: boolean): Promise<CppBuildTask[]> {
if (this.tasks !== undefined) {
return this.tasks;
}
const editor: TextEditor | undefined = window.activeTextEditor;
const emptyTasks: CppBuildTask[] = [];
if (!editor) {
Expand Down Expand Up @@ -149,14 +142,11 @@ export class CppBuildTaskProvider implements TaskProvider {
if (userCompilerPath) {
result.push(this.getTask(userCompilerPath, appendSourceToName, userCompilerPathAndArgs?.additionalArgs));
}

return result;
}

private getTask: (compilerPath: string, appendSourceToName: boolean, compilerArgs?: string[], definition?: CppBuildTaskDefinition) => Task = (compilerPath: string, appendSourceToName: boolean, compilerArgs?: string[], definition?: CppBuildTaskDefinition) => {
private getTask: (compilerPath: string, appendSourceToName: boolean, compilerArgs?: string[], definition?: CppBuildTaskDefinition, detail?: string) => Task = (compilerPath: string, appendSourceToName: boolean, compilerArgs?: string[], definition?: CppBuildTaskDefinition, detail?: string) => {
const compilerPathBase: string = path.basename(compilerPath);
const taskLabel: string = ((appendSourceToName && !compilerPathBase.startsWith(CppBuildTaskProvider.CppBuildSourceStr)) ?
CppBuildTaskProvider.CppBuildSourceStr + ": " : "") + compilerPathBase + " build active file";
const isCl: boolean = compilerPathBase === "cl.exe";
// Double-quote the command if it is not already double-quoted.
let resolvedcompilerPath: string = isCl ? compilerPathBase : compilerPath;
Expand All @@ -165,6 +155,8 @@ export class CppBuildTaskProvider implements TaskProvider {
}

if (!definition) {
const taskLabel: string = ((appendSourceToName && !compilerPathBase.startsWith(CppBuildTaskProvider.CppBuildSourceStr)) ?
CppBuildTaskProvider.CppBuildSourceStr + ": " : "") + compilerPathBase + " build active file";
const filePath: string = path.join('${fileDirname}', '${fileBasenameNoExtension}');
const isWindows: boolean = os.platform() === 'win32';
let args: string[] = isCl ? ['/Zi', '/EHsc', '/Fe:', filePath + '.exe', '${file}'] : ['-g', '${file}', '-o', filePath + (isWindows ? '.exe' : '')];
Expand Down Expand Up @@ -192,38 +184,58 @@ export class CppBuildTaskProvider implements TaskProvider {
}

const scope: TaskScope = TaskScope.Workspace;
const task: CppBuildTask = new Task(definition, scope, taskLabel, CppBuildTaskProvider.CppBuildSourceStr,
const task: CppBuildTask = new Task(definition, scope, definition.label, CppBuildTaskProvider.CppBuildSourceStr,
new CustomExecution(async (): Promise<Pseudoterminal> =>
// When the task is executed, this callback will run. Here, we setup for running the task.
new CustomBuildTaskTerminal(resolvedcompilerPath, definition ? definition.args : [], definition ? definition.options : undefined)
), isCl ? '$msCompile' : '$gcc');

task.group = TaskGroup.Build;
task.detail = "compiler: " + resolvedcompilerPath;
task.detail = detail ? detail : "compiler: " + resolvedcompilerPath;

return task;
};

public async getJsonTasks(): Promise<CppBuildTask[]> {
const rawJson: any = await this.getRawTasksJson();
const rawTasksJson: any = (!rawJson.tasks) ? new Array() : rawJson.tasks;
const buildTasksJson: CppBuildTask[] = rawTasksJson.map((task: any) => {
const definition: CppBuildTaskDefinition = {
type: task.type,
label: task.label,
command: task.command,
args: task.args,
options: task.options
};
const cppBuildTask: CppBuildTask = new Task(definition, TaskScope.Workspace, task.label, "C/C++");
cppBuildTask.detail = task.detail;
return cppBuildTask;
});
return buildTasksJson;
}

public async ensureBuildTaskExists(taskLabel: string): Promise<void> {
const rawTasksJson: any = await this.getRawTasksJson();

// Ensure that the task exists in the user's task.json. Task will not be found otherwise.
if (!rawTasksJson.tasks) {
rawTasksJson.tasks = new Array();
}
// Find or create the task which should be created based on the selected "debug configuration".
let selectedTask: CppBuildTask | undefined = rawTasksJson.tasks.find((task: any) => task.label && task.label === taskLabel);
// Ensure that the task exists in the user's task.json. Task will not be found otherwise.
let selectedTask: any = rawTasksJson.tasks.find((task: any) => task.label && task.label === taskLabel);
if (selectedTask) {
return;
}

// Create the task which should be created based on the selected "debug configuration".
const buildTasks: CppBuildTask[] = await this.getTasks(true);
selectedTask = buildTasks.find(task => task.name === taskLabel);
const normalizedLabel: string = (taskLabel.indexOf("ver(") !== -1) ? taskLabel.slice(0, taskLabel.indexOf("ver(")).trim() : taskLabel;
selectedTask = buildTasks.find(task => task.name === normalizedLabel);
console.assert(selectedTask);
if (!selectedTask) {
throw new Error("Failed to get selectedTask in ensureBuildTaskExists()");
} else {
selectedTask.definition.label = taskLabel;
selectedTask.name = taskLabel;
}

rawTasksJson.version = "2.0.0";

// Modify the current default task
Expand Down Expand Up @@ -272,6 +284,23 @@ export class CppBuildTaskProvider implements TaskProvider {
return;
}

// Provide a unique name for a newly defined tasks, which is different from tasks' names in tasks.json.
public provideUniqueTaskLabel(label: string, buildTasksJson: CppBuildTask[]): string {
const taskNameDictionary: {[key: string]: any} = {};
buildTasksJson.forEach(task => {
taskNameDictionary[task.definition.label] = {};
});
let newLabel: string = label;
let version: number = 0;
do {
version = version + 1;
newLabel = label + ` ver(${version})`;

} while (taskNameDictionary[newLabel]);

return newLabel;
}

private getLaunchJsonPath(): string | undefined {
return util.getJsonPath("launch.json");
}
Expand All @@ -280,12 +309,12 @@ export class CppBuildTaskProvider implements TaskProvider {
return util.getJsonPath("tasks.json");
}

private getRawLaunchJson(): Promise<any> {
public getRawLaunchJson(): Promise<any> {
const path: string | undefined = this.getLaunchJsonPath();
return util.getRawJson(path);
}

private getRawTasksJson(): Promise<any> {
public getRawTasksJson(): Promise<any> {
const path: string | undefined = this.getTasksJsonPath();
return util.getRawJson(path);
}
Expand Down

0 comments on commit df8523e

Please sign in to comment.