Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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