Grdowns/launch configs (#3215)

* Implement debug configurations that will build the active C/C++ file and launch a debugger (disabled)
* Handle compilerPath in build and squiggles.
This commit is contained in:
Griffin Downs
2019-02-28 16:46:26 -08:00
committed by Sean McManus
parent 98260f96ea
commit 3d7937feeb
11 changed files with 403 additions and 111 deletions
+41 -65
View File
@@ -16,9 +16,9 @@
"dev": true
},
"@types/node": {
"version": "8.5.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.5.4.tgz",
"integrity": "sha512-iSJdm5S6Vhk+Ep38gwxie/5T7+MSip6bu30LsE5VylRKTMxXrvVJGqdxJLs8hsviLqjlCx9o3uRXDkJ+/K0DsQ==",
"version": "8.10.40",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.40.tgz",
"integrity": "sha512-RRSjdwz63kS4u7edIwJUn8NqKLLQ6LyqF/X4+4jp38MBT3Vwetewi2N4dgJEshLbDwNgOJXNYoOwzVZUSSLhkQ==",
"dev": true
},
"agent-base": {
@@ -30,9 +30,9 @@
}
},
"ajv": {
"version": "6.9.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz",
"integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==",
"version": "6.9.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.2.tgz",
"integrity": "sha512-4UFy0/LgDo7Oa/+wOAlj44tp9K78u38E5/359eSrqEp1Z5PdVfimCcs7SluXMP755RUQu6d2b4AvF0R1C9RZjg==",
"dev": true,
"requires": {
"fast-deep-equal": "^2.0.1",
@@ -114,6 +114,16 @@
"buffer-equal": "^1.0.0"
}
},
"applicationinsights": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.0.8.tgz",
"integrity": "sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg==",
"requires": {
"diagnostic-channel": "0.2.0",
"diagnostic-channel-publishers": "0.2.1",
"zone.js": "0.7.6"
}
},
"archy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
@@ -1626,8 +1636,7 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"aproba": {
"version": "1.2.0",
@@ -1648,14 +1657,12 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -1670,20 +1677,17 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"core-util-is": {
"version": "1.0.2",
@@ -1800,8 +1804,7 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"ini": {
"version": "1.3.5",
@@ -1813,7 +1816,6 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -1828,7 +1830,6 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -1836,14 +1837,12 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"minipass": {
"version": "2.2.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"yallist": "^3.0.0"
@@ -1862,7 +1861,6 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -1943,8 +1941,7 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"object-assign": {
"version": "4.1.1",
@@ -1956,7 +1953,6 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@@ -2042,8 +2038,7 @@
"safe-buffer": {
"version": "5.1.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -2079,7 +2074,6 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -2099,7 +2093,6 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -2143,14 +2136,12 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"yallist": {
"version": "3.0.2",
"bundled": true,
"dev": true,
"optional": true
"dev": true
}
}
},
@@ -5109,9 +5100,9 @@
"dev": true
},
"typescript": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.2.tgz",
"integrity": "sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=",
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz",
"integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==",
"dev": true
},
"unc-path-regex": {
@@ -5426,18 +5417,15 @@
}
},
"vrsource-tslint-rules": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/vrsource-tslint-rules/-/vrsource-tslint-rules-5.8.2.tgz",
"integrity": "sha512-wr69FB/S6P8GT+rO/OocCr3biFaX4lT/JH1IZGqd7Jy4A3WaPUs0Jn4ELmTEpbFtYMWTawFACMQh1AyT7YewRQ==",
"dev": true,
"requires": {
"tslint": "~5.8.0"
}
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/vrsource-tslint-rules/-/vrsource-tslint-rules-5.8.3.tgz",
"integrity": "sha512-3tYS1zn+d9QtgmQIYmQ9e1i0kipmUjp6cvvYWLC+lZ3z8CQUle7rPKsTFMexOTIVXDpBt9JSJlhDWbwljl3DMQ==",
"dev": true
},
"vscode": {
"version": "1.1.29",
"resolved": "https://registry.npmjs.org/vscode/-/vscode-1.1.29.tgz",
"integrity": "sha512-E6hzqGtCD65BnBxdZzSIi8FPCM4seEEK/bbTeYdJntg+4D5R6GLbdYFySE9DNTtMJF4iB9UGoucKU/p8Guts1g==",
"version": "1.1.30",
"resolved": "https://registry.npmjs.org/vscode/-/vscode-1.1.30.tgz",
"integrity": "sha512-YDj5w0TGOcS8XLIdekT4q6LlLV6hv1ZvuT2aGT3KJll4gMz6dUPDgo2VVAf0i0E8igbbZthwvmaUGRwW9yPIaw==",
"dev": true,
"requires": {
"glob": "^7.1.2",
@@ -5505,23 +5493,11 @@
"integrity": "sha512-d+l4lrEz6OP2kmGpweqe37x9H7icAMV8S4m8azTWGAIlNJxBP4rlSTnZa7NMLcbgqWkWG9lTGY7fJ+rSPaW7yg=="
},
"vscode-extension-telemetry": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.0.tgz",
"integrity": "sha512-WVCnP+uLxlqB6UD98yQNV47mR5Rf79LFxpuZhSPhEf0Sb4tPZed3a63n003/dchhOwyCTCBuNN4n8XKJkLEI1Q==",
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz",
"integrity": "sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA==",
"requires": {
"applicationinsights": "1.0.6"
},
"dependencies": {
"applicationinsights": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.0.6.tgz",
"integrity": "sha512-VQT3kBpJVPw5fCO5n+WUeSx0VHjxFtD7znYbILBlVgOS9/cMDuGFmV2Br3ObzFyZUDGNbEfW36fD1y2/vAiCKw==",
"requires": {
"diagnostic-channel": "0.2.0",
"diagnostic-channel-publishers": "0.2.1",
"zone.js": "0.7.6"
}
}
"applicationinsights": "1.0.8"
}
},
"vscode-jsonrpc": {
+7 -7
View File
@@ -1420,10 +1420,11 @@
},
"devDependencies": {
"@types/mocha": "^5.2.6",
"@types/node": "^8.0.46",
"await-notify": "^1.0.1",
"@types/node": "^8.10.40",
"async-child-process": "^1.1.1",
"await-notify": "^1.0.1",
"child-process": "^1.0.2",
"extend": "3.0.2",
"gulp": "^4.0.0",
"gulp-env": "0.4.0",
"gulp-mocha": "5.0.0",
@@ -1432,10 +1433,9 @@
"tslint": "5.8.0",
"tslint-microsoft-contrib": "5.0.1",
"tslint-no-unused-expression-chai": "0.0.3",
"typescript": "^2.5.3",
"vrsource-tslint-rules": "^5.8.2",
"vscode": "^1.1.29",
"extend": "3.0.2"
"typescript": "^2.9.2",
"vrsource-tslint-rules": "^5.8.3",
"vscode": "^1.1.30"
},
"dependencies": {
"@types/minimatch": "^3.0.3",
@@ -1448,7 +1448,7 @@
"vscode-cpptools": "2.1.1",
"vscode-debugadapter": "~1.33.0",
"vscode-debugprotocol": "~1.33.0",
"vscode-extension-telemetry": "~0.1.0",
"vscode-extension-telemetry": "^0.1.1",
"vscode-languageclient": "5.1.1",
"yauzl": "~2.10.0"
},
+2 -1
View File
@@ -14,5 +14,6 @@
"c_cpp.command.pauseParsing.title": "Metti in pausa l'analisi del codice",
"c_cpp.command.resumeParsing.title": "Riprendi l'analisi del codice",
"c_cpp.command.showParsingCommands.title": "Mostra comandi per l'analisi del codice",
"c_cpp.command.takeSurvey.title": "Partecipa al Sondaggio"
"c_cpp.command.takeSurvey.title": "Partecipa al Sondaggio",
"c_cpp.command.buildAndDebugActiveFile.title": "Compila ed Esegui il debug del file attivo"
}
+4 -3
View File
@@ -2,8 +2,8 @@
"c_cpp.command.configurationSelect.title": "Select a configuration...",
"c_cpp.command.configurationProviderSelect.title": "Change configuration provider...",
"c_cpp.command.configurationEdit.title": "Edit configurations...",
"c_cpp.command.goToDeclaration.title": "Go to declaration",
"c_cpp.command.peekDeclaration.title": "Peek declaration",
"c_cpp.command.goToDeclaration.title": "Go to Declaration",
"c_cpp.command.peekDeclaration.title": "Peek Declaration",
"c_cpp.command.switchHeaderSource.title": "Switch Header/Source",
"c_cpp.command.navigate.title": "Navigate...",
"c_cpp.command.toggleErrorSquiggles.title": "Toggle error squiggles",
@@ -14,5 +14,6 @@
"c_cpp.command.pauseParsing.title": "Pause parsing",
"c_cpp.command.resumeParsing.title": "Resume parsing",
"c_cpp.command.showParsingCommands.title": "Show parsing commands",
"c_cpp.command.takeSurvey.title": "Take survey"
"c_cpp.command.takeSurvey.title": "Take survey",
"c_cpp.command.buildAndDebugActiveFile.title": "Build and Debug Active File"
}
+2 -1
View File
@@ -14,5 +14,6 @@
"c_cpp.command.pauseParsing.title": "暂停解析",
"c_cpp.command.resumeParsing.title": "继续解析",
"c_cpp.command.showParsingCommands.title": "查看解析命令",
"c_cpp.command.takeSurvey.title": "调查问卷"
"c_cpp.command.takeSurvey.title": "调查问卷",
"c_cpp.command.buildAndDebugActiveFile.title": "生成和调试当前文件"
}
+124 -3
View File
@@ -7,11 +7,80 @@ import * as debugUtils from './utils';
import * as os from 'os';
import * as path from 'path';
import * as vscode from 'vscode';
import { getBuildTasks, BuildTaskDefinition } from '../LanguageServer/extension';
import * as util from '../common';
import * as fs from 'fs';
import * as assert from 'assert';
import * as Telemetry from '../telemetry';
import { buildAndDebugActiveFileStr } from './extension';
import { IConfiguration, IConfigurationSnippet, DebuggerType, MIConfigurations, WindowsConfigurations, WSLConfigurations, PipeTransportConfigurations } from './configurations';
import { parse } from 'jsonc-parser';
import { PlatformInformation } from '../platform';
abstract class CppConfigurationProvider implements vscode.DebugConfigurationProvider {
function isDebugLaunchStr(str: string): boolean {
return str === "(gdb) Launch" || str === "(lldb) Launch" || str === "(Windows) Launch";
}
/*
* Retrieves configurations from a provider and displays them in a quickpick menu to be selected.
* Ensures that the selected configuration's preLaunchTask (if existent) is populated in the user's task.json.
* Automatically starts debugging for "Build and Debug" configurations.
*/
export class QuickPickConfigurationProvider implements vscode.DebugConfigurationProvider {
private underlyingProvider: vscode.DebugConfigurationProvider;
public constructor(provider: CppConfigurationProvider) {
this.underlyingProvider = provider;
}
async provideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration[]> {
const configs: vscode.DebugConfiguration[] = await this.underlyingProvider.provideDebugConfigurations(folder, token);
const defaultConfig: vscode.DebugConfiguration = configs.find(config => { return isDebugLaunchStr(config.name); });
console.assert(defaultConfig);
const editor: vscode.TextEditor = vscode.window.activeTextEditor;
if (!editor || !util.fileIsCOrCppSource(editor.document.fileName) || configs.length <= 1) {
return [defaultConfig];
}
interface MenuItem extends vscode.QuickPickItem {
configuration: vscode.DebugConfiguration;
}
const items: MenuItem[] = configs.map<MenuItem>(config => {
let menuItem: MenuItem = {label: config.name, configuration: config};
// Rename the menu item for the default configuration as its name is non-descriptive.
if (isDebugLaunchStr(menuItem.label)) {
menuItem.label = "Default Configuration";
}
return menuItem;
});
return vscode.window.showQuickPick(items, {placeHolder: "Select a configuration"}).then(async selection => {
// Wrap in new Promise to make sure task kicks off before VS Code switches the active document to launch.json
return new Promise<vscode.DebugConfiguration[]>(async resolve => {
if (!selection) {
return resolve([defaultConfig]); // User canceled it. Choose the default config?
}
if (selection.label.indexOf(buildAndDebugActiveFileStr()) !== -1 && selection.configuration.preLaunchTask) {
try {
await util.ensureBuildTaskExists(selection.configuration.preLaunchTask);
await vscode.debug.startDebugging(folder, selection.configuration);
Telemetry.logDebuggerEvent("buildAndDebug", { "success": "true" });
} catch (e) {
Telemetry.logDebuggerEvent("buildAndDebug", { "success": "false" });
}
}
return resolve([selection.configuration]);
});
});
}
resolveDebugConfiguration(folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration> {
return this.underlyingProvider.resolveDebugConfiguration(folder, config, token);
}
}
class CppConfigurationProvider implements vscode.DebugConfigurationProvider {
private type: DebuggerType;
private provider: IConfigurationAssetProvider;
@@ -23,8 +92,60 @@ abstract class CppConfigurationProvider implements vscode.DebugConfigurationProv
/**
* Returns a list of initial debug configurations based on contextual information, e.g. package.json or folder.
*/
provideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration[]> {
return this.provider.getInitialConfigurations(this.type);
async provideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration[]> {
let buildTasks: vscode.Task[]; // = await getBuildTasks(); // TODO
if (!buildTasks || buildTasks.length === 0) {
return Promise.resolve(this.provider.getInitialConfigurations(this.type));
}
const defaultConfig: vscode.DebugConfiguration = this.provider.getInitialConfigurations(this.type).find(config => {
return isDebugLaunchStr(config.name);
});
console.assert(defaultConfig, "Could not find default debug configuration.");
const platformInfo: PlatformInformation = await PlatformInformation.GetPlatformInformation();
const platform: string = platformInfo.platform;
// Generate new configurations for each build task.
// Generating a task is async, therefore we must *await* *all* map(task => config) Promises to resolve.
let configs: vscode.DebugConfiguration[] = await Promise.all(buildTasks.map<Promise<vscode.DebugConfiguration>>(async task => {
const definition: BuildTaskDefinition = task.definition as BuildTaskDefinition;
const compilerName: string = path.basename(definition.compilerPath);
const compilerDirname: string = path.dirname(definition.compilerPath);
let newConfig: vscode.DebugConfiguration = Object.assign({}, defaultConfig); // Copy enumerables and properties
newConfig.name = compilerName + buildAndDebugActiveFileStr();
newConfig.preLaunchTask = task.name;
newConfig.externalConsole = false;
const exeName: string = path.join("${fileDirname}", "${fileBasenameNoExtension}");
newConfig.program = platform === "win32" ? exeName + ".exe" : exeName;
let debuggerName: string;
if (compilerName.startsWith("clang")) {
newConfig.MIMode = "lldb";
const suffixIndex: number = compilerName.indexOf("-");
const suffix: string = suffixIndex === -1 ? "" : compilerName.substr(suffixIndex);
debuggerName = (platform === "darwin" ? "lldb" : "lldb-mi") + suffix;
} else {
debuggerName = "gdb";
}
const debuggerPath: string = path.join(compilerDirname, debuggerName);
return new Promise<vscode.DebugConfiguration>(resolve => {
fs.stat(debuggerPath, (err, stats: fs.Stats) => {
if (!err && stats && stats.isFile) {
newConfig.miDebuggerPath = debuggerPath;
} else {
// TODO should probably resolve a missing debugger in a more graceful fashion for win32.
newConfig.miDebuggerPath = path.join("/usr", "bin", debuggerName);
}
return resolve(newConfig);
});
});
}));
configs.push(defaultConfig);
return configs;
}
/**
+71 -2
View File
@@ -7,12 +7,18 @@ import * as vscode from 'vscode';
import * as os from 'os';
import { AttachPicker, RemoteAttachPicker, AttachItemsProvider } from './attachToProcess';
import { NativeAttachItemsProviderFactory } from './nativeAttach';
import { ConfigurationAssetProviderFactory, CppVsDbgConfigurationProvider, CppDbgConfigurationProvider, ConfigurationSnippetProvider, IConfigurationAssetProvider } from './configurationProvider';
import { QuickPickConfigurationProvider, ConfigurationAssetProviderFactory, CppVsDbgConfigurationProvider, CppDbgConfigurationProvider, ConfigurationSnippetProvider, IConfigurationAssetProvider } from './configurationProvider';
import { CppdbgDebugAdapterDescriptorFactory, CppvsdbgDebugAdapterDescriptorFactory } from './debugAdapterDescriptorFactory';
import * as util from '../common';
import * as Telemetry from '../telemetry';
// The extension deactivate method is asynchronous, so we handle the disposables ourselves instead of using extensonContext.subscriptions.
let disposables: vscode.Disposable[] = [];
export function buildAndDebugActiveFileStr(): string {
return " build and debug active file";
}
export function initialize(context: vscode.ExtensionContext): void {
// Activate Process Picker Commands
let attachItemsProvider: AttachItemsProvider = NativeAttachItemsProviderFactory.Get();
@@ -28,7 +34,70 @@ export function initialize(context: vscode.ExtensionContext): void {
if (os.platform() === 'win32') {
disposables.push(vscode.debug.registerDebugConfigurationProvider('cppvsdbg', new CppVsDbgConfigurationProvider(configurationProvider)));
}
disposables.push(vscode.debug.registerDebugConfigurationProvider('cppdbg', new CppDbgConfigurationProvider(configurationProvider)));
const provider: CppDbgConfigurationProvider = new CppDbgConfigurationProvider(configurationProvider);
disposables.push(vscode.debug.registerDebugConfigurationProvider('cppdbg', provider)); //new QuickPickConfigurationProvider(provider))); // TODO
disposables.push(vscode.commands.registerTextEditorCommand("C_Cpp.BuildAndDebugActiveFile", async (textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit, ...args: any[]) => {
const folder: vscode.WorkspaceFolder = vscode.workspace.getWorkspaceFolder(textEditor.document.uri);
if (!folder) {
// Not enabled because we do not react to single-file mode correctly yet.
// We get an ENOENT when the user's c_cpp_properties.json is attempted to be parsed.
// The DefaultClient will also have its configuration accessed, but since it doesn't exist it errors out.
vscode.window.showErrorMessage('This command is not yet available for single-file mode.');
return Promise.resolve();
}
if (!util.fileIsCOrCppSource(textEditor.document.uri.fsPath)) {
vscode.window.showErrorMessage('Cannot build and debug because the active file is not a C or C++ source file.');
return Promise.resolve();
}
let configs: vscode.DebugConfiguration[] = (await provider.provideDebugConfigurations(folder)).filter(config => {
return config.name.indexOf(buildAndDebugActiveFileStr()) !== -1;
});
interface MenuItem extends vscode.QuickPickItem {
configuration: vscode.DebugConfiguration;
}
const items: MenuItem[] = configs.map<MenuItem>(config => {
return {label: config.name, configuration: config};
});
vscode.window.showQuickPick(items, {placeHolder: (items.length === 0 ? "No compiler found" : "Select a compiler" )}).then(async selection => {
if (!selection) {
return; // User canceled it.
}
if (selection.configuration.preLaunchTask) {
if (folder) {
try {
await util.ensureBuildTaskExists(selection.configuration.preLaunchTask);
Telemetry.logDebuggerEvent("buildAndDebug", { "success": "false" });
} catch (e) {
return Promise.resolve();
}
} else {
return Promise.resolve();
// TODO uncomment this when single file mode works correctly.
// const buildTasks: vscode.Task[] = await getBuildTasks();
// const task: vscode.Task = buildTasks.find(task => task.name === selection.configuration.preLaunchTask);
// await vscode.tasks.executeTask(task);
// delete selection.configuration.preLaunchTask;
}
}
// Attempt to use the user's (possibly) modified configuration before using the generated one.
try {
await vscode.debug.startDebugging(folder, selection.configuration.name);
Telemetry.logDebuggerEvent("buildAndDebug", { "success": "true" });
} catch (e) {
try {
vscode.debug.startDebugging(folder, selection.configuration);
} catch (e) {
Telemetry.logDebuggerEvent("buildAndDebug", { "success": "false" });
}
}
});
}));
configurationProvider.getConfigurationSnippets();
+13 -3
View File
@@ -630,6 +630,7 @@ export class CppProperties {
// Replace all \<escape character> with \\<character>.
// Otherwise, the JSON.parse result will have the \<escape character> missing.
readResults = readResults.replace(/\\/g, '\\\\');
readResults = readResults.replace(/\\\\"/g, '\\"'); // Need to revert the change to \".
// Try to use the same configuration as before the change.
let newJson: ConfigurationJson = JSON.parse(readResults);
@@ -752,11 +753,19 @@ export class CppProperties {
paths.add(`"${this.CurrentConfiguration.compileCommands}"`);
}
let compilerPathAndArgs: util.CompilerPathAndArgs;
if (this.CurrentConfiguration.compilerPath) {
compilerPathAndArgs = util.extractCompilerPathAndArgs(this.CurrentConfiguration.compilerPath);
paths.add(`"${compilerPathAndArgs.compilerPath}`); // It may not end with ".
}
// Get the start/end for properties that are file-only.
let forcedIncludeStart: number = curText.search(/\s*\"forcedInclude\"\s*:\s*\[/);
let forcedeIncludeEnd: number = forcedIncludeStart === -1 ? -1 : curText.indexOf("]", forcedIncludeStart);
let compileCommandsStart: number = curText.search(/\s*\"compileCommands\"\s*:\s*\"/);
let compileCommandsEnd: number = compileCommandsStart === -1 ? -1 : curText.indexOf('"', curText.indexOf('"', curText.indexOf(":", compileCommandsStart)) + 1);
let compilerPathStart: number = curText.search(/\s*\"compilerPath\"\s*:\s*\"/);
let compilerPathEnd: number = compilerPathStart === -1 ? -1 : curText.indexOf('"', curText.indexOf('"', curText.indexOf(":", compilerPathStart)) + 1);
if (this.prevSquiggleMetrics[this.CurrentConfiguration.name] === undefined) {
this.prevSquiggleMetrics[this.CurrentConfiguration.name] = { PathNonExistent: 0, PathNotAFile: 0, PathNotADirectory: 0 };
@@ -764,7 +773,7 @@ export class CppProperties {
let newSquiggleMetrics: { [key: string]: number } = { PathNonExistent: 0, PathNotAFile: 0, PathNotADirectory: 0 };
for (let curPath of paths) {
let resolvedPath: string = curPath.substr(1, curPath.length - 2);
let resolvedPath: string = curPath.substr(1, (curPath.endsWith('"') ? curPath.length - 2 : curPath.length - 1));
// Resolve special path cases.
if (resolvedPath === "${default}") {
// TODO: Add squiggles for when the C_Cpp.default.* paths are invalid.
@@ -817,7 +826,8 @@ export class CppProperties {
} else {
// Check for file versus path mismatches.
if ((curOffset >= forcedIncludeStart && curOffset <= forcedeIncludeEnd) ||
(curOffset >= compileCommandsStart && curOffset <= compileCommandsEnd)) {
(curOffset >= compileCommandsStart && curOffset <= compileCommandsEnd) ||
(curOffset >= compilerPathStart && curOffset <= compilerPathEnd)) {
if (util.checkFileExistsSync(resolvedPath)) {
continue;
}
@@ -832,7 +842,7 @@ export class CppProperties {
}
}
let diagnostic: vscode.Diagnostic = new vscode.Diagnostic(
new vscode.Range(document.positionAt(curTextStartOffset + curOffset + 1), document.positionAt(curTextStartOffset + curOffset + curPath.length - 1)),
new vscode.Range(document.positionAt(curTextStartOffset + curOffset + 1), document.positionAt(curTextStartOffset + curOffset + (curPath.endsWith('"') ? curPath.length - 1 : curPath.length))),
message, vscode.DiagnosticSeverity.Warning);
diagnostics.push(diagnostic);
}
+41 -26
View File
@@ -38,6 +38,7 @@ let realActivationOccurred: boolean = false;
let tempCommands: vscode.Disposable[] = [];
let activatedPreviously: PersistentWorkspaceState<boolean>;
const insiderUpdateTimerInterval: number = 1000 * 60 * 60;
const taskSourceStr: string = "C/C++";
let taskProvider: vscode.Disposable;
@@ -67,7 +68,7 @@ export function activate(activationEventOccurred: boolean): void {
return;
}
taskProvider = vscode.tasks.registerTaskProvider('C/Cpp', {
taskProvider = vscode.tasks.registerTaskProvider(taskSourceStr, {
provideTasks: () => {
return getBuildTasks();
},
@@ -77,7 +78,7 @@ export function activate(activationEventOccurred: boolean): void {
}
});
vscode.tasks.onDidStartTask(event => {
if (event.execution.task.source === 'C/Cpp') {
if (event.execution.task.source === taskSourceStr) {
telemetry.logLanguageServerEvent('buildTaskStarted');
}
});
@@ -105,10 +106,14 @@ export function activate(activationEventOccurred: boolean): void {
}
}
export interface BuildTaskDefinition extends vscode.TaskDefinition {
compilerPath: string;
}
/**
* 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.
*/
async function getBuildTasks(): Promise<vscode.Task[]> {
export async function getBuildTasks(): Promise<vscode.Task[]> {
const editor: vscode.TextEditor = vscode.window.activeTextEditor;
if (!editor) {
return [];
@@ -121,7 +126,7 @@ async function getBuildTasks(): Promise<vscode.Task[]> {
// Don't offer tasks for header files.
const fileExtLower: string = fileExt.toLowerCase();
const isHeader: boolean = !fileExt || [".hpp", ".hh", ".hxx", ".h", ""].some(ext => fileExtLower === ext);
const isHeader: boolean = !fileExt || [".hpp", ".hh", ".hxx", ".h", ".inl", ""].some(ext => fileExtLower === ext);
if (isHeader) {
return [];
}
@@ -133,7 +138,7 @@ async function getBuildTasks(): Promise<vscode.Task[]> {
fileIsCpp = true;
fileIsC = true;
} else {
fileIsCpp = [".cpp", ".cc", ".cxx", ".mm", ".ino", ".inl"].some(ext => fileExtLower === ext);
fileIsCpp = [".cpp", ".cc", ".cxx", ".mm", ".ino"].some(ext => fileExtLower === ext);
fileIsC = fileExtLower === ".c";
}
if (!(fileIsCpp || fileIsC)) {
@@ -143,17 +148,31 @@ async function getBuildTasks(): Promise<vscode.Task[]> {
// Get a list of compilers found from the C++ side, then filter them based on the file type to get a reduced list appropriate
// for the active file, remove duplicate compiler names, then finally add the user's compilerPath setting.
let compilerPaths: string[];
const isWindows: boolean = os.platform() === 'win32';
const activeClient: Client = getActiveClient();
const userCompilerPath: string = await activeClient.getCompilerPath();
let userCompilerPath: string = await activeClient.getCompilerPath();
if (userCompilerPath) {
userCompilerPath = userCompilerPath.trim();
if (isWindows && (userCompilerPath.startsWith("/") || userCompilerPath.endsWith("cl.exe"))) { // TODO: Add WSL/cl.exe compiler support.
userCompilerPath = null;
} else {
userCompilerPath = userCompilerPath.replace(/\\\\/g, "\\");
}
}
let knownCompilers: configs.KnownCompiler[] = await activeClient.getKnownCompilers();
if (knownCompilers) {
knownCompilers = knownCompilers.filter(info => { return (fileIsCpp && !info.isC) || (fileIsC && info.isC); });
knownCompilers = knownCompilers.filter(info => {
return ((fileIsCpp && !info.isC) || (fileIsC && info.isC)) &&
(!isWindows || !info.path.startsWith("/")); // TODO: Add WSL compiler support.
});
compilerPaths = knownCompilers.map<string>(info => { return info.path; });
let map: Map<string, string> = new Map<string, string>();
const insertOrAssignEntry: (compilerPath: string) => void = (compilerPath: string): void => {
const basename: string = path.basename(compilerPath);
map.has(basename) ? map[basename] = compilerPath : map.set(basename, compilerPath);
//map.has(basename) ? map.basename] = compilerPath :
map.set(basename, compilerPath);
};
compilerPaths.forEach(insertOrAssignEntry);
@@ -173,7 +192,7 @@ async function getBuildTasks(): Promise<vscode.Task[]> {
// Display a message prompting the user to install compilers if none were found.
// const dontShowAgain: string = "Don't Show Again";
// const learnMore: string = "Learn More";
// const message: string = "No C/C++ compiler found on the system. Please install a C/C++ compiler to use the C/Cpp: build active file tasks.";
// const message: string = "No C/C++ compiler found on the system. Please install a C/C++ compiler to use the C/C++: build active file tasks.";
// let showNoCompilerFoundMessage: PersistentState<boolean> = new PersistentState<boolean>("CPP.showNoCompilerFoundMessage", true);
// if (showNoCompilerFoundMessage) {
@@ -194,38 +213,34 @@ async function getBuildTasks(): Promise<vscode.Task[]> {
return [];
}
// The build task output file should include a '.exe' extension on Windows.
let platformInfo: PlatformInformation = await PlatformInformation.GetPlatformInformation();
let exeName: string;
if (platformInfo.platform === 'win32') {
exeName = '${fileBasenameNoExtension}.exe';
} else {
exeName = '${fileBasenameNoExtension}';
}
// Generate tasks.
let result: vscode.Task[] = [];
compilerPaths.forEach(compilerPath => {
return compilerPaths.map<vscode.Task>(compilerPath => {
// Handle compiler args in compilerPath.
let compilerPathAndArgs: util.CompilerPathAndArgs = util.extractCompilerPathAndArgs(compilerPath);
compilerPath = compilerPathAndArgs.compilerPath;
const taskName: string = path.basename(compilerPath) + " build active file";
const args: string[] = ['-g', '${file}', '-o', '${fileDirname}/' + exeName];
let args: string[] = ['-g', '${file}', '-o', path.join('${fileDirname}', '${fileBasenameNoExtension}' + (isWindows ? '.exe' : ''))];
if (compilerPathAndArgs.additionalArgs) {
args = args.concat(compilerPathAndArgs.additionalArgs);
}
const cwd: string = path.dirname(compilerPath);
const kind: vscode.TaskDefinition = {
const kind: BuildTaskDefinition = {
type: 'shell',
label: taskName,
command: compilerPath,
args: args,
options: {"cwd": cwd}
options: {"cwd": cwd},
compilerPath: compilerPath
};
const command: vscode.ShellExecution = new vscode.ShellExecution(compilerPath, [...args], { cwd: cwd });
const target: vscode.WorkspaceFolder = vscode.workspace.getWorkspaceFolder(clients.ActiveClient.RootUri);
let task: vscode.Task = new vscode.Task(kind, target, taskName, 'C/Cpp', command, '$gcc');
let task: vscode.Task = new vscode.Task(kind, target, taskName, taskSourceStr, command, '$gcc');
task.definition = kind; // The constructor for vscode.Task will eat the definition. Reset it by reassigning.
task.group = vscode.TaskGroup.Build;
result.push(task);
return task;
});
return result;
}
function onDidOpenTextDocument(document: vscode.TextDocument): void {
+97
View File
@@ -16,6 +16,7 @@ import { getOutputChannelLogger, showOutputChannel } from './logger';
import * as assert from 'assert';
import * as https from 'https';
import { ClientRequest, OutgoingHttpHeaders } from 'http';
import { getBuildTasks } from './LanguageServer/extension';
export let extensionContext: vscode.ExtensionContext;
export function setExtensionContext(context: vscode.ExtensionContext): void {
@@ -36,6 +37,65 @@ export function getRawPackageJson(): any {
return rawPackageJson;
}
export function getRawTasksJson(): Promise<any> {
const path: string = getTasksJsonPath();
if (!path) {
return undefined;
}
return new Promise<any>((resolve, reject) => {
fs.exists(path, exists => {
if (!exists) {
return resolve({});
}
const fileContents: Buffer = fs.readFileSync(path);
let rawTasks: any = {};
try {
rawTasks = JSON.parse(fileContents.toString());
} catch (error) {
}
resolve(rawTasks);
});
});
}
export async function ensureBuildTaskExists(taskName: string): Promise<void> {
let rawTasksJson: any = await 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: vscode.Task = rawTasksJson.tasks.find(task => {
return task.label && task.label === task;
});
if (selectedTask) {
return;
}
const buildTasks: vscode.Task[] = await getBuildTasks();
selectedTask = buildTasks.find(task => task.name === taskName);
console.assert(selectedTask);
let definition: vscode.TaskDefinition = selectedTask.definition as vscode.TaskDefinition;
if (definition && definition.compilerPath) {
// TODO: add desired properties to empty object, don't delete.
delete definition.compilerPath;
}
if (!rawTasksJson.tasks.find(task => { return task.label === selectedTask.definition.label; })) {
rawTasksJson.tasks.push(selectedTask.definition);
}
// TODO: It's dangerous to overwrite this file. We could be wiping out comments.
await writeFileText(getTasksJsonPath(), JSON.stringify(rawTasksJson, null, 2));
}
export function fileIsCOrCppSource(file: string): boolean {
const fileExtLower: string = path.extname(file).toLowerCase();
return [".C", ".c", ".cpp", ".cc", ".cxx", ".mm", ".ino", ".inl"].some(ext => fileExtLower === ext);
}
// This function is used to stringify the rawPackageJson.
// Do not use with util.packageJson or else the expanded
// package.json will be written back.
@@ -51,6 +111,15 @@ export function getPackageJsonPath(): string {
return getExtensionFilePath("package.json");
}
export function getTasksJsonPath(): string {
const editor: vscode.TextEditor = vscode.window.activeTextEditor;
const folder: vscode.WorkspaceFolder = vscode.workspace.getWorkspaceFolder(editor.document.uri);
if (!folder) {
return undefined;
}
return path.join(folder.uri.fsPath, ".vscode", "tasks.json");
}
export function getVcpkgPathDescriptorFile(): string {
if (process.platform === 'win32') {
return path.join(process.env.LOCALAPPDATA, "vcpkg/vcpkg.path.txt");
@@ -686,4 +755,32 @@ export function downloadFileToStr(urlStr: string, headers?: OutgoingHttpHeaders)
request.on('error', (error) => { reject(error); });
request.end();
});
}
export interface CompilerPathAndArgs {
compilerPath: string;
additionalArgs: string[];
}
export function extractCompilerPathAndArgs(inputCompilerPath: string): CompilerPathAndArgs {
let compilerPath: string = inputCompilerPath;
let additionalArgs: string[];
if (compilerPath) {
if (compilerPath.startsWith("\"")) {
let endQuote: number = compilerPath.substr(1).search("\"") + 1;
if (endQuote !== -1) {
additionalArgs = compilerPath.substr(endQuote + 1).split(" ");
additionalArgs = additionalArgs.filter((arg: string) => { return arg.trim().length !== 0; });
compilerPath = compilerPath.substr(1, endQuote - 1);
}
} else {
if (compilerPath.includes(" ") && !fs.existsSync(compilerPath)) {
let argStart: number = compilerPath.search(" ");
additionalArgs = compilerPath.substr(argStart + 1).split(" ");
additionalArgs = additionalArgs.filter((arg: string) => { return arg.trim().length !== 0; });
compilerPath = compilerPath.substr(0, argStart);
}
}
}
return { compilerPath, additionalArgs };
}
+1
View File
@@ -329,6 +329,7 @@ function rewriteManifest(): Promise<void> {
"onLanguage:c",
"onCommand:extension.pickNativeProcess",
"onCommand:extension.pickRemoteNativeProcess",
//"onCommand:C_Cpp.BuildAndDebugActiveFile", // TODO
"onCommand:C_Cpp.ConfigurationEdit",
"onCommand:C_Cpp.ConfigurationSelect",
"onCommand:C_Cpp.ConfigurationProviderSelect",