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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "csharp",
"publisher": "ms-vscode",
"version": "1.4.0-beta7",
"version": "1.4.0-beta8",
"description": "C# for Visual Studio Code (powered by OmniSharp).",
"displayName": "C#",
"author": "Microsoft Corporation",
Expand Down
157 changes: 97 additions & 60 deletions src/coreclr-debug/activate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,26 @@
'use strict';

import * as vscode from 'vscode';
import * as child_process from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import TelemetryReporter from 'vscode-extension-telemetry';
import { CoreClrDebugUtil } from './util';
import * as debugInstall from './install';
import { Platform, getCurrentPlatform } from './../platform';
import * as semver from 'semver';

const MINIMUM_SUPPORTED_DOTNET_CLI: string = '1.0.0-preview2-003121';

let _reporter: TelemetryReporter = null;
let _channel: vscode.OutputChannel = null;
let _util: CoreClrDebugUtil = null;

class DotnetInfo
{
public Version: string;
public OsVersion: string;
public RuntimeId: string;
}

export function activate(context: vscode.ExtensionContext, reporter: TelemetryReporter) {
_reporter = reporter;
_channel = vscode.window.createOutputChannel('coreclr-debug');
Expand All @@ -27,22 +35,7 @@ export function activate(context: vscode.ExtensionContext, reporter: TelemetryRe
return;
}

let dotnetVersion: string = '';
let osVersion: string = '';
let osRID: string = '';
_util.spawnChildProcess('dotnet', ['--info'], _util.coreClrDebugDir(), (data: Buffer) => {
var lines: string[] = data.toString().replace(/\r/mg, '').split('\n');
lines.forEach(line => {
let match: RegExpMatchArray;
if (match = /^\ Version:\s*([^\s].*)$/.exec(line)) {
dotnetVersion = match[1];
} else if (match = /^\ OS Version:\s*([^\s].*)$/.exec(line)) {
osVersion = match[1];
} else if (match = /^\ RID:\s*([\w\-\.]+)$/.exec(line)) {
osRID = match[1];
}
});
}).then(() => {
checkForDotnetTools().then((dotnetInfo: DotnetInfo) => {
let installer = new debugInstall.DebugInstaller(_util);
_util.createInstallLog();

Expand All @@ -61,54 +54,98 @@ export function activate(context: vscode.ExtensionContext, reporter: TelemetryRe
statusBarMessage.dispose();
vscode.window.setStatusBarMessage('Successfully installed .NET Core Debugger.');
}).catch((error: debugInstall.InstallError) => {
const viewLogMessage = "View Log";
vscode.window.showErrorMessage('Error while installing .NET Core Debugger.', viewLogMessage).then(value => {
if (value === viewLogMessage) {
_channel.show(vscode.ViewColumn.Three);
}
});
statusBarMessage.dispose();

installStage = error.installStage;
installError = error.errorMessage;
moreErrors = error.hasMoreErrors ? 'true' : 'false';
}).then(() => {
// log telemetry and delete install begin file
logTelemetry('Acquisition', {
installStage: installStage,
installError: installError,
moreErrors: moreErrors,
dotnetVersion: dotnetVersion,
osVersion: osVersion,
osRID: osRID
});
try {
deleteInstallBeginFile();
} catch (err) {
// if this throws there's really nothing we can do
const viewLogMessage = "View Log";
vscode.window.showErrorMessage('Error while installing .NET Core Debugger.', viewLogMessage).then(value => {
if (value === viewLogMessage) {
_channel.show(vscode.ViewColumn.Three);
}
_util.closeInstallLog();
});
}).catch(() => {
const config = vscode.workspace.getConfiguration('csharp');
if (!config.get('suppressDotnetInstallWarning', false)) {
const getDotNetMessage = 'Get .NET CLI tools';
const goToSettingsMessage = 'Disable this message in user settings';
// Buttons are shown in right-to-left order, with a close button to the right of everything;
// getDotNetMessage will be the first button, then goToSettingsMessage, then the close button.
vscode.window.showErrorMessage('The .NET CLI tools cannot be located. .NET Core debugging will not be enabled. Make sure .NET CLI tools are installed and are on the path.',
goToSettingsMessage, getDotNetMessage).then(value => {
if (value === getDotNetMessage) {
let open = require('open');
open('https://www.microsoft.com/net/core');
} else if (value === goToSettingsMessage) {
vscode.commands.executeCommand('workbench.action.openGlobalSettings');
}
});
statusBarMessage.dispose();

installStage = error.installStage;
installError = error.errorMessage;
moreErrors = error.hasMoreErrors ? 'true' : 'false';
}).then(() => {
// log telemetry and delete install begin file
logTelemetry('Acquisition', {
installStage: installStage,
installError: installError,
moreErrors: moreErrors,
dotnetVersion: dotnetInfo.Version,
osVersion: dotnetInfo.OsVersion,
osRID: dotnetInfo.RuntimeId
});
try {
deleteInstallBeginFile();
} catch (err) {
// if this throws there's really nothing we can do
}
_util.closeInstallLog();
});
}).catch((error) => {
// log errors from checkForDotnetTools
_util.log(error.message);
});
}

// This function checks for the presence of dotnet on the path and ensures the Version
// is new enough for us. Any error UI that needs to be displayed is handled by this function.
// Returns: a promise that returns a DotnetInfo class
// Throws: An Error() from the return promise if either dotnet does not exist or is too old.
function checkForDotnetTools() : Promise<DotnetInfo>
{
let dotnetInfo = new DotnetInfo();

return _util.spawnChildProcess('dotnet', ['--info'], _util.coreClrDebugDir(), (data: Buffer) => {
let lines: string[] = data.toString().replace(/\r/mg, '').split('\n');
lines.forEach(line => {
let match: RegExpMatchArray;
if (match = /^\ Version:\s*([^\s].*)$/.exec(line)) {
dotnetInfo.Version = match[1];
} else if (match = /^\ OS Version:\s*([^\s].*)$/.exec(line)) {
dotnetInfo.OsVersion = match[1];
} else if (match = /^\ RID:\s*([\w\-\.]+)$/.exec(line)) {
dotnetInfo.RuntimeId = match[1];
}
});
}).catch((error) => {
// something went wrong with spawning 'dotnet --info'
let message = 'The .NET CLI tools cannot be located. .NET Core debugging will not be enabled. Make sure .NET CLI tools are installed and are on the path.';
showDotnetToolsWarning(message);
throw new Error("Failed to spawn 'dotnet --info'");
}).then(() => {
// succesfully spawned 'dotnet --info', check the Version
if (semver.lt(dotnetInfo.Version, MINIMUM_SUPPORTED_DOTNET_CLI))
{
let message = 'The .NET CLI tools on the path are too old. .NET Core debugging will not be enabled. The minimum supported version is ' + MINIMUM_SUPPORTED_DOTNET_CLI + '.';
showDotnetToolsWarning(message);
throw new Error("dotnet cli is too old");
}

return dotnetInfo;
});
}

function showDotnetToolsWarning(message: string) : void
{
const config = vscode.workspace.getConfiguration('csharp');
if (!config.get('suppressDotnetInstallWarning', false)) {
const getDotNetMessage = 'Get .NET CLI tools';
const goToSettingsMessage = 'Disable this message in user settings';
// Buttons are shown in right-to-left order, with a close button to the right of everything;
// getDotNetMessage will be the first button, then goToSettingsMessage, then the close button.
vscode.window.showErrorMessage(message,
goToSettingsMessage, getDotNetMessage).then(value => {
if (value === getDotNetMessage) {
let open = require('open');
open('https://www.microsoft.com/net/core');
} else if (value === goToSettingsMessage) {
vscode.commands.executeCommand('workbench.action.openGlobalSettings');
}
});
}
}

function logTelemetry(eventName: string, properties?: {[prop: string]: string}): void {
if (_reporter !== null) {
_reporter.sendTelemetryEvent('coreclr-debug/' + eventName, properties);
Expand Down
37 changes: 18 additions & 19 deletions src/coreclr-debug/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import { CoreClrDebugUtil } from './util';
import * as fs from 'fs';
import * as path from 'path';
import * as child_process from 'child_process';
import * as fs_extra from 'fs-extra-promise';

export class InstallError extends Error {
Expand Down Expand Up @@ -53,22 +52,22 @@ export class DebugInstaller {
}

public install(runtimeId: string): Promise<void> {
var errorBuilder: InstallError = new InstallError();
let errorBuilder: InstallError = new InstallError();
errorBuilder.installStage = 'writeProjectJson';

return this.writeProjectJson(runtimeId).then(() => {
errorBuilder.installStage = 'dotnetRestore';
return this._util.spawnChildProcess('dotnet', ['--verbose', 'restore', '--configfile', 'NuGet.config'], this._util.coreClrDebugDir(),
(data: Buffer) => {
var text: string = data.toString();
let text: string = data.toString();
this._util.logRaw(text);

// Certain errors are only logged to stdout.
// Detect these and make a note of the kind of error.
DebugInstaller.parseRestoreErrors(text, errorBuilder);
},
(data: Buffer) => {
var text: string = data.toString();
let text: string = data.toString();
this._util.logRaw(text);

// Reference errors are sent to stderr at the end of restore.
Expand All @@ -78,7 +77,7 @@ export class DebugInstaller {
errorBuilder.installStage = 'dotnetPublish';
return this._util.spawnChildProcess('dotnet', ['--verbose', 'publish', '-r', runtimeId, '-o', this._util.debugAdapterDir()], this._util.coreClrDebugDir(),
(data: Buffer) => {
var text: string = data.toString();
let text: string = data.toString();
this._util.logRaw(text);

DebugInstaller.parsePublishErrors(text, errorBuilder);
Expand Down Expand Up @@ -253,7 +252,7 @@ export class DebugInstaller {
}

private static parseRestoreErrors(output: string, errorBuilder: InstallError): void {
var lines: string[] = output.replace(/\r/mg, '').split('\n');
let lines: string[] = output.replace(/\r/mg, '').split('\n');
lines.forEach(line => {
if (line.startsWith('error')) {
const connectionError: string = 'The server name or address could not be resolved';
Expand All @@ -271,16 +270,16 @@ export class DebugInstaller {

private static parseReferenceErrors(output: string, errorBuilder: InstallError): void {
// Reference errors are restated at the end of the output. Find this section first.
var errorRegionRegExp: RegExp = /^Errors in .*project\.json$/gm
var beginIndex: number = output.search(errorRegionRegExp);
var errorBlock: string = output.slice(beginIndex);
let errorRegionRegExp: RegExp = /^Errors in .*project\.json$/gm;
let beginIndex: number = output.search(errorRegionRegExp);
let errorBlock: string = output.slice(beginIndex);

var lines: string[] = errorBlock.replace(/\r/mg, '').split('\n');
let lines: string[] = errorBlock.replace(/\r/mg, '').split('\n');
lines.forEach(line => {
var referenceRegExp: RegExp = /^(?:\t|\ \ \ \ )Unable to resolve '([^']+)'/g
var match: RegExpMatchArray;
let referenceRegExp: RegExp = /^(?:\t|\ \ \ \ )Unable to resolve '([^']+)'/g;
let match: RegExpMatchArray;
while (match = referenceRegExp.exec(line)) {
var reference: string = match[1];
let reference: string = match[1];
if (reference.startsWith('Microsoft') ||
reference.startsWith('System') ||
reference.startsWith('NETStandard') ||
Expand All @@ -294,15 +293,15 @@ export class DebugInstaller {
}

private static parsePublishErrors(output: string, errorBuilder: InstallError): void {
var lines: string[] = output.replace(/\r/mg, '').split('\n');
let lines: string[] = output.replace(/\r/mg, '').split('\n');
lines.forEach(line => {
var errorTypeRegExp: RegExp = /^([\w\.]+Exception)/g
var typeMatch: RegExpMatchArray;
const errorTypeRegExp: RegExp = /^([\w\.]+Exception)/g;
let typeMatch: RegExpMatchArray;
while (typeMatch = errorTypeRegExp.exec(line)) {
var type: string = typeMatch[1];
let type: string = typeMatch[1];
if (type === 'System.IO.IOException') {
var ioExceptionRegExp: RegExp = /System\.IO\.IOException: The process cannot access the file '(.*)' because it is being used by another process./g
var ioMatch: RegExpMatchArray;
const ioExceptionRegExp: RegExp = /System\.IO\.IOException: The process cannot access the file '(.*)' because it is being used by another process./g;
let ioMatch: RegExpMatchArray;
if (ioMatch = ioExceptionRegExp.exec(line)) {
// Remove path as it may contain user information.
errorBuilder.errorMessage = `System.IO.IOException: unable to access '${path.basename(ioMatch[1])}'`;
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr-debug/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export class CoreClrDebugUtil

child.on('error', (error: Error) => {
reject(error);
})
});
});

return promise;
Expand Down