diff --git a/.gitignore b/.gitignore
index ea0c8f290..062afeb3b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,8 @@ bin
node_modules
out
.omnisharp-*/
+.debugger
-install.lock
+install.*
*.vsix
diff --git a/coreclr-debug/.gitignore b/coreclr-debug/.gitignore
deleted file mode 100644
index 60944094f..000000000
--- a/coreclr-debug/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-bin
-obj
-project.lock.json
-debugAdapters
-install.log
-extension.log
-project.json
diff --git a/coreclr-debug/NuGet.config b/coreclr-debug/NuGet.config
deleted file mode 100644
index ef957669a..000000000
--- a/coreclr-debug/NuGet.config
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/coreclr-debug/dummy.cs b/coreclr-debug/dummy.cs
deleted file mode 100644
index 15513fc2c..000000000
--- a/coreclr-debug/dummy.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-
-namespace Dummy
-{
- class Dummy
- {
- static void Main(string[] args) {
- // empty boilerplate required by dotnet build/publish to emit an entry point
- // The entrypoint created is dummy[.exe], which we rename to OpenDebugAD7[.exe]
- // The generated entry point will then run OpenDebugAD7.dll for us
- }
- }
-}
\ No newline at end of file
diff --git a/gulpfile.js b/gulpfile.js
index 207d4bc0a..a700a1779 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -14,7 +14,6 @@ const tslint = require('gulp-tslint');
const vsce = require('vsce');
const debugUtil = require('./out/src/coreclr-debug/util');
const debugInstall = require('./out/src/coreclr-debug/install');
-const fs_extra = require('fs-extra-promise');
const packages = require('./out/src/packages');
const logger = require('./out/src/logger');
const platform = require('./out/src/platform');
@@ -26,75 +25,49 @@ const PackageManager = packages.PackageManager;
const LinuxDistribution = platform.LinuxDistribution;
const PlatformInformation = platform.PlatformInformation;
-/// used in offline packaging run so does not clean .vsix
-function clean() {
- cleanDebugger();
- return cleanOmnisharp();
-}
+function cleanSync(deleteVsix) {
+ del.sync('install.*');
+ del.sync('.omnisharp-*');
+ del.sync('.debugger');
-gulp.task('clean', ['omnisharp:clean', 'debugger:clean', 'package:clean'], () => {
+ if (deleteVsix) {
+ del.sync('*.vsix');
+ }
+}
+gulp.task('clean', () => {
+ cleanSync(true);
});
-/// Omnisharp Tasks
-function installOmnisharp(platformInfo, packageJSON) {
+// Install Tasks
+function install(platformInfo, packageJSON) {
const packageManager = new PackageManager(platformInfo, packageJSON);
const logger = new Logger(message => process.stdout.write(message));
+ const debuggerUtil = new debugUtil.CoreClrDebugUtil(path.resolve('.'), logger);
+ const debugInstaller = new debugInstall.DebugInstaller(debuggerUtil);
return packageManager.DownloadPackages(logger)
.then(() => {
return packageManager.InstallPackages(logger);
+ })
+ .then(() => {
+ return util.touchInstallFile(util.InstallFileType.Lock)
+ })
+ .then(() => {
+ return debugInstaller.finishInstall();
});
}
-function cleanOmnisharp() {
- return del('.omnisharp-*');
-}
-
-gulp.task('omnisharp:clean', () => {
- return cleanOmnisharp();
-});
+gulp.task('install', ['clean'], () => {
+ util.setExtensionPath(__dirname);
-gulp.task('omnisharp:install', ['omnisharp:clean'], () => {
return PlatformInformation.GetCurrent()
.then(platformInfo => {
- return installOmnisharp(platformInfo, getPackageJSON());
+ return install(platformInfo, getPackageJSON());
});
});
-/// Debugger Tasks
-function getDebugInstaller() {
- return new debugInstall.DebugInstaller(new debugUtil.CoreClrDebugUtil(path.resolve('.')), true);
-}
-
-function installDebugger(runtimeId) {
- return getDebugInstaller().install(runtimeId);
-}
-
-function cleanDebugger() {
- try {
- getDebugInstaller().clean();
- console.log('Cleaned Succesfully');
- } catch (error) {
- console.error(error);
- }
-}
-
-gulp.task('debugger:install', ['debugger:clean'], () => {
- installDebugger(gulp.env.runtimeId)
- .then(() => {
- console.log('Installed Succesfully');
- })
- .catch((error) => {
- console.error(error);
- });
-});
-
-gulp.task('debugger:clean', () => {
- cleanDebugger();
-});
-
-/// Packaging Tasks
+/// Packaging (VSIX) Tasks
function doPackageSync(packageName) {
var vsceArgs = [];
@@ -113,13 +86,12 @@ function doPackageSync(packageName) {
}
function doOfflinePackage(platformInfo, packageName, packageJSON) {
- return clean()
- .then(() => {
- return installDebugger(platformInfo.runtimeId);
- })
- .then(() => {
- return installOmnisharp(platformInfo, packageJSON);
- })
+ if (process.platform === 'win32') {
+ throw new Error('Do not build offline packages on windows. Runtime executables will not be marked executable in *nix packages.');
+ }
+
+ cleanSync(false);
+ return install(platformInfo, packageJSON)
.then(() => {
doPackageSync(packageName + '-' + platformInfo.runtimeId + '.vsix');
});
@@ -130,7 +102,7 @@ function getPackageJSON() {
}
gulp.task('package:clean', () => {
- return del('*.vsix');
+ del.sync('*.vsix');
});
gulp.task('package:online', ['clean'], () => {
diff --git a/package.json b/package.json
index 684d8cf13..b28f8d1e3 100644
--- a/package.json
+++ b/package.json
@@ -91,7 +91,7 @@
]
},
{
- "description": "OmniSharp (.NET Core - OSX / x64)",
+ "description": "OmniSharp (.NET Core - macOS / x64)",
"url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-1.9-beta19-osx-x64-netcoreapp1.0.zip",
"installPath": ".omnisharp-coreclr",
"runtimeIds": [
@@ -190,6 +190,110 @@
"darwin",
"linux"
]
+ },
+ {
+ "description": ".NET Core Debugger (Windows / x64)",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-5-0/coreclr-debug-win7-x64.zip",
+ "installPath": ".debugger",
+ "runtimeIds": [
+ "win7-x64"
+ ]
+ },
+ {
+ "description": ".NET Core Debugger (macOS / x64)",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-5-0/coreclr-debug-osx.10.11-x64.zip",
+ "installPath": ".debugger",
+ "runtimeIds": [
+ "osx.10.11-x64"
+ ],
+ "binaries": [
+ "./OpenDebugAD7",
+ "./clrdbg"
+ ]
+ },
+ {
+ "description": ".NET Core Debugger (CentOS / x64)",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-5-0/coreclr-debug-centos.7-x64.zip",
+ "installPath": ".debugger",
+ "runtimeIds": [
+ "centos.7-x64"
+ ],
+ "binaries": [
+ "./OpenDebugAD7",
+ "./clrdbg"
+ ]
+ },
+ {
+ "description": ".NET Core Debugger (Debian / x64)",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-5-0/coreclr-debug-debian.8-x64.zip",
+ "installPath": ".debugger",
+ "runtimeIds": [
+ "debian.8-x64"
+ ],
+ "binaries": [
+ "./OpenDebugAD7",
+ "./clrdbg"
+ ]
+ },
+ {
+ "description": ".NET Core Debugger (Fedora / x64)",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-5-0/coreclr-debug-fedora.23-x64.zip",
+ "installPath": ".debugger",
+ "runtimeIds": [
+ "fedora.23-x64"
+ ],
+ "binaries": [
+ "./OpenDebugAD7",
+ "./clrdbg"
+ ]
+ },
+ {
+ "description": ".NET Core Debugger (OpenSUSE / x64)",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-5-0/coreclr-debug-opensuse.13.2-x64.zip",
+ "installPath": ".debugger",
+ "runtimeIds": [
+ "opensuse.13.2-x64"
+ ],
+ "binaries": [
+ "./OpenDebugAD7",
+ "./clrdbg"
+ ]
+ },
+ {
+ "description": ".NET Core Debugger (RHEL / x64)",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-5-0/coreclr-debug-rhel.7.2-x64.zip",
+ "installPath": ".debugger",
+ "runtimeIds": [
+ "rhel.7-x64"
+ ],
+ "binaries": [
+ "./OpenDebugAD7",
+ "./clrdbg"
+ ]
+ },
+ {
+ "description": ".NET Core Debugger (Ubuntu 14 / x64)",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-5-0/coreclr-debug-ubuntu.14.04-x64.zip",
+ "installPath": ".debugger",
+ "runtimeIds": [
+ "ubuntu.14.04-x64"
+ ],
+ "binaries": [
+ "./OpenDebugAD7",
+ "./clrdbg"
+ ]
+ },
+ {
+ "description": ".NET Core Debugger (Ubuntu 16 / x64)",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-5-0/coreclr-debug-ubuntu.16.04-x64.zip",
+ "installPath": ".debugger",
+ "runtimeIds": [
+ "ubuntu.16.04-x64"
+ ],
+ "binaries": [
+ "./OpenDebugAD7",
+ "./clrdbg"
+ ]
}
],
"engines": {
diff --git a/src/common.ts b/src/common.ts
index 552f63ff7..0ca265554 100644
--- a/src/common.ts
+++ b/src/common.ts
@@ -60,19 +60,39 @@ export function fileExists(filePath: string): Promise {
});
}
-function getInstallLockFilePath(): string {
- return path.resolve(getExtensionPath(), 'install.lock');
+export enum InstallFileType {
+ Begin,
+ Lock
}
-export function lockFileExists(): Promise {
- return fileExists(getInstallLockFilePath());
+function getInstallFilePath(type: InstallFileType): string {
+ let installFile = 'install.' + InstallFileType[type];
+ return path.resolve(getExtensionPath(), installFile);
}
-export function touchLockFile(): Promise {
+export function installFileExists(type: InstallFileType): Promise {
+ return fileExists(getInstallFilePath(type));
+}
+
+export function touchInstallFile(type: InstallFileType): Promise {
+ return new Promise((resolve, reject) => {
+ fs.writeFile(getInstallFilePath(type), '', err => {
+ if (err) {
+ reject(err);
+ return;
+ }
+
+ resolve();
+ });
+ });
+}
+
+export function deleteInstallFile(type: InstallFileType): Promise {
return new Promise((resolve, reject) => {
- fs.writeFile(getInstallLockFilePath(), '', err => {
+ fs.unlink(getInstallFilePath(type), err => {
if (err) {
- return reject(err);
+ reject(err);
+ return;
}
resolve();
diff --git a/src/coreclr-debug/activate.ts b/src/coreclr-debug/activate.ts
index bde6d7227..fc8a828dc 100644
--- a/src/coreclr-debug/activate.ts
+++ b/src/coreclr-debug/activate.ts
@@ -7,126 +7,50 @@
import * as vscode from 'vscode';
import * as fs from 'fs';
import TelemetryReporter from 'vscode-extension-telemetry';
-import { CoreClrDebugUtil } from './util';
+import { CoreClrDebugUtil, DotnetInfo, DotNetCliError } from './util';
import * as debugInstall from './install';
-import { PlatformInformation } from './../platform';
-import * as semver from 'semver';
-
-const MINIMUM_SUPPORTED_DOTNET_CLI: string = '1.0.0-preview2-003121';
+import * as path from 'path';
+import { Logger } from './../logger'
+let _debugUtil: CoreClrDebugUtil = null;
let _reporter: TelemetryReporter = null;
-let _channel: vscode.OutputChannel = null;
-let _util: CoreClrDebugUtil = null;
-
-class DotnetInfo
-{
- public Version: string;
- public OsVersion: string;
- public RuntimeId: string;
-}
+let _logger: Logger = null;
-export function activate(context: vscode.ExtensionContext, reporter: TelemetryReporter) {
+export function activate(context: vscode.ExtensionContext, reporter: TelemetryReporter, logger: Logger) {
+ _debugUtil = new CoreClrDebugUtil(context.extensionPath, logger);
_reporter = reporter;
- _channel = vscode.window.createOutputChannel('coreclr-debug');
- _util = new CoreClrDebugUtil(context.extensionPath, _channel);
-
- if (CoreClrDebugUtil.existsSync(_util.installCompleteFilePath())) {
- console.log('.NET Core Debugger tools already installed');
- return;
- }
-
- checkForDotnetTools().then((dotnetInfo: DotnetInfo) => {
- let installer = new debugInstall.DebugInstaller(_util);
- _util.createInstallLog();
-
- let statusBarMessage = vscode.window.setStatusBarMessage("Downloading and configuring the .NET Core Debugger...");
-
- let runtimeId: string = '';
- let installStage: string = "installBegin";
- let installError: string = '';
- let moreErrors: string = '';
-
- getPlatformRuntimeId().then(rid => {
- runtimeId = rid;
- }).then(() => {
- return writeInstallBeginFile();
- }).then(() => {
- return installer.install(runtimeId);
- }).then(() => {
- installStage = "completeSuccess";
- 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: dotnetInfo.Version,
- osVersion: dotnetInfo.OsVersion,
- osRID: dotnetInfo.RuntimeId
+ _logger = logger;
+
+ if (!CoreClrDebugUtil.existsSync(_debugUtil.debugAdapterDir())) {
+ // We have been activated but it looks like our package was not installed. This is bad.
+ logger.appendLine("[ERROR]: C# Extension failed to install the debugger package");
+ showInstallErrorMessage();
+ } else if (!CoreClrDebugUtil.existsSync(_debugUtil.installCompleteFilePath())) {
+ _debugUtil.checkDotNetCli()
+ .then((dotnetInfo: DotnetInfo) => {
+ let installer = new debugInstall.DebugInstaller(_debugUtil);
+ installer.finishInstall()
+ .then(() => {
+ vscode.window.setStatusBarMessage('Successfully installed .NET Core Debugger.');
+ })
+ .catch((err) => {
+ logger.appendLine("[ERROR]: An error occured while installing the .NET Core Debugger:")
+ logger.appendLine(err);
+ showInstallErrorMessage();
+ // TODO: log telemetry?
+ });
+ }, (err) => {
+ // Check for dotnet tools failed. pop the UI
+ // err is a DotNetCliError but use defaults in the unexpected case that it's not
+ showDotnetToolsWarning(err.ErrorMessage || _debugUtil.defaultDotNetCliErrorMessage());
+ _logger.appendLine(err.ErrorString || err);
+ // TODO: log telemetry?
});
- 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
-{
- 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 showInstallErrorMessage() {
+ vscode.window.showErrorMessage("An error occured during installation of the .NET Core Debugger. The C# extension may need to be reinstalled.");
}
function showDotnetToolsWarning(message: string) : void
@@ -153,27 +77,4 @@ function logTelemetry(eventName: string, properties?: {[prop: string]: string}):
if (_reporter !== null) {
_reporter.sendTelemetryEvent('coreclr-debug/' + eventName, properties);
}
-}
-
-function writeInstallBeginFile() : Promise {
- return CoreClrDebugUtil.writeEmptyFile(_util.installBeginFilePath());
-}
-
-function deleteInstallBeginFile() {
- if (CoreClrDebugUtil.existsSync(_util.installBeginFilePath())) {
- fs.unlinkSync(_util.installBeginFilePath());
- }
-}
-
-function getPlatformRuntimeId(): Promise {
- return PlatformInformation.GetCurrent().then(info => {
- if (info.runtimeId) {
- return info.runtimeId;
- }
-
- // If we got here, this isn't a support runtime ID.
- const message = `Unsupported platform: ${info.toString()}`
- _util.log(`Error: ${message}`);
- throw new Error(message);
- });
-}
+}
\ No newline at end of file
diff --git a/src/coreclr-debug/install.ts b/src/coreclr-debug/install.ts
index 17eb998dd..c4116f2d2 100644
--- a/src/coreclr-debug/install.ts
+++ b/src/coreclr-debug/install.ts
@@ -2,12 +2,10 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-'use strict';
import { CoreClrDebugUtil } from './util';
import * as fs from 'fs';
import * as path from 'path';
-import * as fs_extra from 'fs-extra-promise';
export class InstallError extends Error {
public installStage: string;
@@ -44,84 +42,30 @@ export class InstallError extends Error {
export class DebugInstaller {
private _util: CoreClrDebugUtil = null;
- private _isOffline;
- constructor(util: CoreClrDebugUtil, isOffline?: boolean) {
+ constructor(util: CoreClrDebugUtil) {
this._util = util;
- this._isOffline = isOffline || false;
}
- public install(runtimeId: string): Promise {
- let errorBuilder: InstallError = new InstallError();
- errorBuilder.installStage = 'writeProjectJson';
+ public finishInstall(): Promise {
+ let errorBuilder = new InstallError();
- return this.writeProjectJson(runtimeId).then(() => {
- errorBuilder.installStage = 'dotnetRestore';
- return this._util.spawnChildProcess('dotnet', ['--verbose', 'restore', '--configfile', 'NuGet.config'], this._util.coreClrDebugDir(),
- (data: Buffer) => {
- 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) => {
- let text: string = data.toString();
- this._util.logRaw(text);
-
- // Reference errors are sent to stderr at the end of restore.
- DebugInstaller.parseReferenceErrors(text, errorBuilder);
- });
- }).then(() => {
- errorBuilder.installStage = 'dotnetPublish';
- return this._util.spawnChildProcess('dotnet', ['--verbose', 'publish', '-r', runtimeId, '-o', this._util.debugAdapterDir()], this._util.coreClrDebugDir(),
- (data: Buffer) => {
- let text: string = data.toString();
- this._util.logRaw(text);
-
- DebugInstaller.parsePublishErrors(text, errorBuilder);
- });
- }).then(() => {
- errorBuilder.installStage = 'ensureAd7';
- return this.ensureAd7EngineExists(this._util.debugAdapterDir());
- }).then(() => {
- errorBuilder.installStage = 'renameDummyEntrypoint';
- return this.renameDummyEntrypoint();
- }).then(() => {
+ return Promise.resolve().then(() => {
errorBuilder.installStage = 'rewriteManifest';
this.rewriteManifest();
- errorBuilder.installStage = 'writeCompletionFile';
- return this.writeCompletionFile();
- }).catch((e) => {
+ errorBuilder.installStage = 'writeCompletionFile'
+ return CoreClrDebugUtil.writeEmptyFile(this._util.installCompleteFilePath());
+ }).catch((err) => {
if (errorBuilder.errorMessage === null) {
// Only give the error message if we don't have any better info,
// as this is usually something similar to "Error: 1".
- errorBuilder.errorMessage = e;
+ errorBuilder.errorMessage = err;
}
throw errorBuilder;
});
}
- public clean(): void {
- let cleanItems: string[] = [];
-
- cleanItems.push(this._util.debugAdapterDir());
- cleanItems.push(this._util.installLogPath());
- cleanItems.push(path.join(this._util.coreClrDebugDir(), 'bin'));
- cleanItems.push(path.join(this._util.coreClrDebugDir(), 'obj'));
- cleanItems.push(path.join(this._util.coreClrDebugDir(), 'project.json'));
- cleanItems.push(path.join(this._util.coreClrDebugDir(), 'project.lock.json'));
-
- cleanItems.forEach((item) => {
- if (CoreClrDebugUtil.existsSync(item)) {
- this._util.log(`Cleaning ${item}`);
- fs_extra.removeSync(item);
- }
- });
- }
-
private rewriteManifest(): void {
const manifestPath = path.join(this._util.extensionDir(), 'package.json');
let manifestString = fs.readFileSync(manifestPath, 'utf8');
@@ -129,7 +73,7 @@ export class DebugInstaller {
delete manifestObject.contributes.debuggers[0].runtime;
delete manifestObject.contributes.debuggers[0].program;
- let programString = './coreclr-debug/debugAdapters/OpenDebugAD7';
+ let programString = './.debugger/OpenDebugAD7';
manifestObject.contributes.debuggers[0].windows = { program: programString + '.exe' };
manifestObject.contributes.debuggers[0].osx = { program: programString };
manifestObject.contributes.debuggers[0].linux = { program: programString };
@@ -137,179 +81,4 @@ export class DebugInstaller {
manifestString = JSON.stringify(manifestObject, null, 2);
fs.writeFileSync(manifestPath, manifestString);
}
-
- private writeCompletionFile(): Promise {
- return CoreClrDebugUtil.writeEmptyFile(this._util.installCompleteFilePath());
- }
-
- private renameDummyEntrypoint(): Promise {
- let src = path.join(this._util.debugAdapterDir(), 'dummy');
- let dest = path.join(this._util.debugAdapterDir(), 'OpenDebugAD7');
-
- if (!CoreClrDebugUtil.existsSync(src)) {
- if (CoreClrDebugUtil.existsSync(src + '.exe')) {
- src += '.exe';
- dest += '.exe';
- }
- }
-
- const promise = new Promise((resolve, reject) => {
- fs.rename(src, dest, (err) => {
- if (err) {
- reject(err.code);
- } else {
- resolve();
- }
- });
- });
-
- return promise;
- }
-
- private ensureAd7EngineExists(outputDirectory: string): Promise {
- let filePath = path.join(outputDirectory, 'coreclr.ad7Engine.json');
- return new Promise((resolve, reject) => {
- fs.exists(filePath, (exists) => {
- if (exists) {
- return resolve();
- } else {
- this._util.log(`${filePath} does not exist.`);
- this._util.log('');
- // NOTE: The minimum build number is actually less than 1584, but this is the minimum tested build.
- this._util.log('Error: The .NET CLI did not correctly restore debugger files. ' +
- 'Ensure that you have .NET CLI version 1.0.0 build #001584 or newer. ' +
- "You can check your .NET CLI version using 'dotnet --version'.");
- return reject('The .NET CLI did not correctly restore debugger files.');
- }
- });
- });
- }
-
- private writeProjectJson(runtimeId: string): Promise {
- return new Promise((resolve, reject) => {
- const projectJsonPath = path.join(this._util.coreClrDebugDir(), 'project.json');
- this._util.log('Creating ' + projectJsonPath);
-
- const projectJson = this.createProjectJson(runtimeId);
-
- fs.writeFile(projectJsonPath, JSON.stringify(projectJson, null, 2), { encoding: 'utf8' }, (err) => {
- if (err) {
- this._util.log('Error: Unable to write to project.json: ' + err.message);
- reject(err.code);
- }
- else {
- resolve();
- }
- });
- });
- }
-
- private createProjectJson(targetRuntime: string): any {
- let projectJson = {
- name: "dummy",
- buildOptions: {
- emitEntryPoint: true
- },
- dependencies: {
- "Microsoft.VisualStudio.clrdbg": "15.0.25626-preview-3219185",
- "Microsoft.VisualStudio.clrdbg.MIEngine": "14.0.31028-preview-1",
- "Microsoft.VisualStudio.OpenDebugAD7": "1.0.21028-preview-2",
- "NETStandard.Library": "1.6.0",
- "Newtonsoft.Json": "7.0.1",
- "Microsoft.VisualStudio.Debugger.Interop.Portable": "1.0.1",
- "System.Collections.Specialized": "4.0.1",
- "System.Collections.Immutable": "1.2.0",
- "System.Diagnostics.Process": "4.1.0",
- "System.Dynamic.Runtime": "4.0.11",
- "Microsoft.CSharp": "4.0.1",
- "System.Threading.Tasks.Dataflow": "4.6.0",
- "System.Threading.Thread": "4.0.0",
- "System.Xml.XDocument": "4.0.11",
- "System.Xml.XmlDocument": "4.0.1",
- "System.Xml.XmlSerializer": "4.0.11",
- "System.ComponentModel": "4.0.1",
- "System.ComponentModel.Annotations": "4.1.0",
- "System.ComponentModel.EventBasedAsync": "4.0.11",
- "System.Runtime.Serialization.Primitives": "4.1.1",
- "System.Net.Http": "4.1.0"
- },
- frameworks: {
- "netcoreapp1.0": {
- imports: ["dnxcore50", "portable-net45+win8"]
- }
- },
- runtimes: {
- }
- };
-
- projectJson.runtimes[targetRuntime] = {};
-
- if (this._isOffline) {
- projectJson.dependencies["Microsoft.NetCore.DotNetHostPolicy"] = "1.0.1";
- }
-
- return projectJson;
- }
-
- private static parseRestoreErrors(output: string, errorBuilder: InstallError): void {
- 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';
- if (line.indexOf(connectionError) !== -1) {
- errorBuilder.errorMessage = connectionError;
- }
-
- const parseVersionError: RegExp = /Error reading '.*' at line [0-9]+ column [0-9]+: '.*' is not a valid version string/;
- if (parseVersionError.test(line)) {
- errorBuilder.errorMessage = 'Invalid version string';
- }
- }
- });
- }
-
- private static parseReferenceErrors(output: string, errorBuilder: InstallError): void {
- // Reference errors are restated at the end of the output. Find this section first.
- let errorRegionRegExp: RegExp = /^Errors in .*project\.json$/gm;
- let beginIndex: number = output.search(errorRegionRegExp);
- let errorBlock: string = output.slice(beginIndex);
-
- let lines: string[] = errorBlock.replace(/\r/mg, '').split('\n');
- lines.forEach(line => {
- let referenceRegExp: RegExp = /^(?:\t|\ \ \ \ )Unable to resolve '([^']+)'/g;
- let match: RegExpMatchArray;
- while (match = referenceRegExp.exec(line)) {
- let reference: string = match[1];
- if (reference.startsWith('Microsoft') ||
- reference.startsWith('System') ||
- reference.startsWith('NETStandard') ||
- reference.startsWith('Newtonsoft')) {
- errorBuilder.errorMessage = `Unable to restore reference '${reference}'`;
- } else {
- errorBuilder.errorMessage = 'Error(s) encountered restoring private references';
- }
- }
- });
- }
-
- private static parsePublishErrors(output: string, errorBuilder: InstallError): void {
- let lines: string[] = output.replace(/\r/mg, '').split('\n');
- lines.forEach(line => {
- const errorTypeRegExp: RegExp = /^([\w\.]+Exception)/g;
- let typeMatch: RegExpMatchArray;
- while (typeMatch = errorTypeRegExp.exec(line)) {
- let type: string = typeMatch[1];
- if (type === 'System.IO.IOException') {
- 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])}'`;
- }
- } else {
- errorBuilder.errorMessage = type;
- }
- }
- });
- }
}
\ No newline at end of file
diff --git a/src/coreclr-debug/proxy.ts b/src/coreclr-debug/proxy.ts
index 80bccdeba..2a72701cb 100644
--- a/src/coreclr-debug/proxy.ts
+++ b/src/coreclr-debug/proxy.ts
@@ -8,6 +8,8 @@ import * as path from 'path';
import { DebugProtocol } from 'vscode-debugprotocol';
import * as child_process from 'child_process';
import { CoreClrDebugUtil } from './util';
+import * as common from './../common';
+import { Logger } from './../logger';
class ProxyErrorResponse implements DebugProtocol.ErrorResponse {
public body: { error?: DebugProtocol.Message };
@@ -32,6 +34,18 @@ function serializeProtocolEvent(message: DebugProtocol.ProtocolMessage): string
return finalPayload;
}
+function sendErrorMessage(message: string) {
+ process.stdout.write(serializeProtocolEvent(new ProxyErrorResponse(message)));
+}
+
+function sendStillDownloadingMessage() {
+ sendErrorMessage('The .NET Core Debugger is still being downloaded. See the C# Output Window for more information.');
+}
+
+function sendDownloadingNotStartedMessage() {
+ sendErrorMessage('Run \'Debug: Download .NET Core Debugger\' in the Command Palette or open a .NET project directory to download the .NET Core Debugger');
+}
+
// The default extension manifest calls this proxy as the debugger program
// When installation of the debugger components finishes, the extension manifest is rewritten so that this proxy is no longer called
// If the debugger components have not finished downloading, the proxy displays an error message to the user
@@ -39,17 +53,53 @@ function serializeProtocolEvent(message: DebugProtocol.ProtocolMessage): string
// This proxy will still be called and launch OpenDebugAD7 as a child process.
// During subsequent code sessions, the rewritten manifest will be loaded and this proxy will no longer be called.
function proxy() {
- let util = new CoreClrDebugUtil(path.resolve(__dirname, '../../../'));
+ let extensionPath = path.resolve(__dirname, '../../../');
+ common.setExtensionPath(extensionPath);
+
+ let logger = new Logger((text) => { console.log(text) });
+ let util = new CoreClrDebugUtil(extensionPath, logger);
if (!CoreClrDebugUtil.existsSync(util.installCompleteFilePath())) {
- if (CoreClrDebugUtil.existsSync(util.installBeginFilePath())) {
- process.stdout.write(serializeProtocolEvent(new ProxyErrorResponse('The .NET Core Debugger is still being downloaded. See the Status Bar for more information.')));
- } else {
- process.stdout.write(serializeProtocolEvent(new ProxyErrorResponse('Run \'Debug: Download .NET Core Debugger\' in the Command Palette or open a .NET project directory to download the .NET Core Debugger')));
- }
+ // our install.complete file does not exist yet, meaning we have not rewritten our manifest yet. Try to figure out what if anything the package manager is doing
+ // the order in which files are dealt with is this:
+ // 1. install.Begin is created
+ // 2. install.Lock is created
+ // 3. install.Begin is deleted
+ // 4. install.complete is created
+
+ //first check if dotnet is on the path and new enough
+ util.checkDotNetCli()
+ .then((dotnetInfo) => {
+ // next check if we have begun installing packages
+ common.installFileExists(common.InstallFileType.Begin)
+ .then((beginExists: boolean) => {
+ if (beginExists) {
+ // packages manager has begun
+ sendStillDownloadingMessage();
+ } else {
+ // begin doesn't exist. There is a chance we finished downloading and begin had been deleted. Check if lock exists
+ common.installFileExists(common.InstallFileType.Lock)
+ .then((lockExists) => {
+ if (lockExists) {
+ // packages have finished installing but we had not finished rewriting our manifest when F5 came in
+ sendStillDownloadingMessage();
+ }
+ else {
+ // no install files existed when we checked. we have likely not been activated
+ sendDownloadingNotStartedMessage();
+ }
+ });
+ }
+ });
+ }, (err) => {
+ // error from checkDotNetCli
+ sendErrorMessage(err.ErrorMessage || util.defaultDotNetCliErrorMessage());
+ });
}
else
{
+ // debugger has finished install and manifest has been rewritten, kick off our debugger process
+
new Promise((resolve, reject) => {
let processPath = path.join(util.debugAdapterDir(), "OpenDebugAD7" + CoreClrDebugUtil.getPlatformExeExtension());
let args = process.argv.slice(2);
@@ -70,7 +120,7 @@ function proxy() {
process.stdin.setEncoding('utf8');
child.on('error', data => {
- util.logToFile(`Child error: ${data}`);
+ logger.appendLine(`Child error: ${data}`);
});
process.on('SIGTERM', () => {
@@ -84,11 +134,11 @@ function proxy() {
});
process.stdin.on('error', error => {
- util.logToFile(`process.stdin error: ${error}`);
+ logger.appendLine(`process.stdin error: ${error}`);
});
process.stdout.on('error', error => {
- util.logToFile(`process.stdout error: ${error}`);
+ logger.appendLine(`process.stdout error: ${error}`);
});
child.stdout.on('data', data => {
@@ -101,7 +151,7 @@ function proxy() {
process.stdin.resume();
}).catch(err => {
- util.logToFile(`Promise failed: ${err}`);
+ logger.appendLine(err);
});
}
}
diff --git a/src/coreclr-debug/util.ts b/src/coreclr-debug/util.ts
index d15328ae5..c19b66842 100644
--- a/src/coreclr-debug/util.ts
+++ b/src/coreclr-debug/util.ts
@@ -8,32 +8,37 @@ import * as child_process from 'child_process';
import * as path from 'path';
import * as fs from 'fs';
import * as os from 'os';
-import * as vscode from 'vscode';
+import * as semver from 'semver';
+import { execChildProcess } from './../common'
+import { Logger } from './../logger'
+
+const MINIMUM_SUPPORTED_DOTNET_CLI: string = '1.0.0-preview2-003121';
+
+export class DotnetInfo
+{
+ public Version: string;
+ public OsVersion: string;
+ public RuntimeId: string;
+}
+
+export class DotNetCliError extends Error {
+ public ErrorMessage: string; // the message to display to the user
+ public ErrorString: string; // the string to log for this error
+}
export class CoreClrDebugUtil
{
private _extensionDir: string = '';
- private _coreClrDebugDir: string = '';
private _debugAdapterDir: string = '';
- private _installLogPath: string = '';
- private _installBeginFilePath: string = '';
private _installCompleteFilePath: string = '';
- private _installLog: fs.WriteStream = null;
- private _channel: vscode.OutputChannel = null;
-
- constructor(extensionDir: string, channel?: vscode.OutputChannel) {
+ constructor(extensionDir: string, logger: Logger) {
this._extensionDir = extensionDir;
- this._coreClrDebugDir = path.join(this._extensionDir, 'coreclr-debug');
- this._debugAdapterDir = path.join(this._coreClrDebugDir, 'debugAdapters');
- this._installLogPath = path.join(this._coreClrDebugDir, 'install.log');
- this._installBeginFilePath = path.join(this._coreClrDebugDir, 'install.begin');
+ this._debugAdapterDir = path.join(this._extensionDir, '.debugger');
this._installCompleteFilePath = path.join(this._debugAdapterDir, 'install.complete');
-
- this._channel = channel;
}
- extensionDir(): string {
+ public extensionDir(): string {
if (this._extensionDir === '')
{
throw new Error('Failed to set extension directory');
@@ -41,35 +46,14 @@ export class CoreClrDebugUtil
return this._extensionDir;
}
- coreClrDebugDir(): string {
- if (this._coreClrDebugDir === '') {
- throw new Error('Failed to set coreclrdebug directory');
- }
- return this._coreClrDebugDir;
- }
-
- debugAdapterDir(): string {
+ public debugAdapterDir(): string {
if (this._debugAdapterDir === '') {
throw new Error('Failed to set debugadpter directory');
}
return this._debugAdapterDir;
}
- installLogPath(): string {
- if (this._installLogPath === '') {
- throw new Error('Failed to set install log path');
- }
- return this._installLogPath;
- }
-
- installBeginFilePath(): string {
- if (this._installBeginFilePath === '') {
- throw new Error('Failed to set install begin file path');
- }
- return this._installBeginFilePath;
- }
-
- installCompleteFilePath(): string {
+ public installCompleteFilePath(): string {
if (this._installCompleteFilePath === '')
{
throw new Error('Failed to set install complete file path');
@@ -77,41 +61,7 @@ export class CoreClrDebugUtil
return this._installCompleteFilePath;
}
- createInstallLog(): void {
- this._installLog = fs.createWriteStream(this.installLogPath());
- }
-
- closeInstallLog(): void {
- if (this._installLog !== null) {
- this._installLog.close();
- }
- }
-
- logRaw(message: string): void {
- console.log(message);
-
- if (this._installLog != null) {
- this._installLog.write(message);
- }
-
- if (this._channel != null) {
- this._channel.append(message);
- }
- }
-
- log(message: string): void {
- console.log(message);
-
- if (this._installLog != null) {
- this._installLog.write(message);
- }
-
- if (this._channel != null) {
- this._channel.appendLine(message);
- }
- }
-
- static writeEmptyFile(path: string) : Promise {
+ public static writeEmptyFile(path: string) : Promise {
return new Promise((resolve, reject) => {
fs.writeFile(path, '', (err) => {
if (err) {
@@ -123,39 +73,52 @@ export class CoreClrDebugUtil
});
}
- public spawnChildProcess(process: string, args: string[], workingDirectory: string, onStdout?: (data: Buffer) => void, onStderr?: (data: Buffer) => void): Promise {
- const promise = new Promise((resolve, reject) => {
- const child = child_process.spawn(process, args, { cwd: workingDirectory });
-
- if (!onStdout) {
- onStdout = (data) => { this.logRaw(`${data}`); };
- }
- child.stdout.on('data', onStdout);
-
- if (!onStderr) {
- onStderr = (data) => { this.logRaw(`${data}`); };
- }
- child.stderr.on('data', onStderr);
-
- child.on('close', (code: number) => {
- if (code != 0) {
- this.log(`${process} exited with error code ${code}`);;
- reject(new Error(code.toString()));
- }
- else {
- resolve();
+ public defaultDotNetCliErrorMessage(): string {
+ return 'Failed to find up to date dotnet cli on the path.';
+ }
+
+ // This function checks for the presence of dotnet on the path and ensures the Version
+ // is new enough for us.
+ // Returns: a promise that returns a DotnetInfo class
+ // Throws: An DotNetCliError() from the return promise if either dotnet does not exist or is too old.
+ public checkDotNetCli(): Promise
+ {
+ let dotnetInfo = new DotnetInfo();
+
+ return execChildProcess('dotnet --info', process.cwd())
+ .then((data: string) => {
+ let lines: string[] = data.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 dotnetError = new DotNetCliError();
+ dotnetError.ErrorMessage = '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.';
+ dotnetError.ErrorString = "Failed to spawn 'dotnet --info'";
+ throw dotnetError;
+ }).then(() => {
+ // succesfully spawned 'dotnet --info', check the Version
+ if (semver.lt(dotnetInfo.Version, MINIMUM_SUPPORTED_DOTNET_CLI))
+ {
+ let dotnetError = new DotNetCliError();
+ dotnetError.ErrorMessage = '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 + '.';
+ dotnetError.ErrorString = "dotnet cli is too old";
+ throw dotnetError;
+ }
- child.on('error', (error: Error) => {
- reject(error);
- });
+ return dotnetInfo;
});
-
- return promise;
}
- static existsSync(path: string) : boolean {
+ public static existsSync(path: string) : boolean {
try {
fs.accessSync(path, fs.F_OK);
return true;
@@ -168,30 +131,11 @@ export class CoreClrDebugUtil
}
}
- static getPlatformExeExtension() : string {
+ public static getPlatformExeExtension() : string {
if (process.platform === 'win32') {
return '.exe';
}
return '';
}
-
- static getPlatformLibExtension() : string {
- switch (process.platform) {
- case 'win32':
- return '.dll';
- case 'darwin':
- return '.dylib';
- case 'linux':
- return '.so';
- default:
- throw Error('Unsupported platform ' + process.platform);
- }
- }
-
- /** Used for diagnostics only */
- logToFile(message: string): void {
- let logFolder = path.resolve(this.coreClrDebugDir(), "extension.log");
- fs.writeFileSync(logFolder, `${message}${os.EOL}`, { flag: 'a' });
- }
}
diff --git a/src/main.ts b/src/main.ts
index 32159f172..e1b14a277 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -15,6 +15,8 @@ import { Logger } from './logger';
import { PackageManager, Status } from './packages';
import { PlatformInformation } from './platform';
+let _channel: vscode.OutputChannel = null;
+
export function activate(context: vscode.ExtensionContext): any {
const extensionId = 'ms-vscode.csharp';
@@ -25,31 +27,34 @@ export function activate(context: vscode.ExtensionContext): any {
util.setExtensionPath(extension.extensionPath);
- ensureRuntimeDependencies(extension)
+ _channel = vscode.window.createOutputChannel('C#');
+
+ let logger = new Logger(text => _channel.append(text));
+
+ ensureRuntimeDependencies(extension, logger)
.then(() => {
// activate language services
OmniSharp.activate(context, reporter);
// activate coreclr-debug
- coreclrdebug.activate(context, reporter);
+ coreclrdebug.activate(context, reporter, logger);
});
}
-function ensureRuntimeDependencies(extension: vscode.Extension): Promise {
- return util.lockFileExists()
+function ensureRuntimeDependencies(extension: vscode.Extension, logger: Logger): Promise {
+ return util.installFileExists(util.InstallFileType.Lock)
.then(exists => {
if (!exists) {
- return installRuntimeDependencies(extension);
+ return util.touchInstallFile(util.InstallFileType.Begin).then(() => {
+ return installRuntimeDependencies(extension, logger);
+ });
}
});
}
-function installRuntimeDependencies(extension: vscode.Extension): Promise {
- let channel = vscode.window.createOutputChannel('C#');
- channel.show();
-
- let logger = new Logger(text => channel.append(text));
- logger.appendLine('Updating C# dependencies...');
+function installRuntimeDependencies(extension: vscode.Extension, logger: Logger): Promise {
+ logger.append('Updating C# dependencies...');
+ _channel.show();
let statusItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right);
let status: Status = {
@@ -65,10 +70,14 @@ function installRuntimeDependencies(extension: vscode.Extension): Promise {
+ installationStage = 'getPlatformInfo';
+ return PlatformInformation.GetCurrent()
+ })
.then(info => {
platformInfo = info;
packageManager = new PackageManager(info, extension.packageJSON);
@@ -90,7 +99,7 @@ function installRuntimeDependencies(extension: vscode.Extension): Promise {
installationStage = 'touchLockFile';
- return util.touchLockFile();
+ return util.touchInstallFile(util.InstallFileType.Lock);
})
.catch(error => {
errorMessage = error.toString();
@@ -105,6 +114,11 @@ function installRuntimeDependencies(extension: vscode.Extension): Promise {
+ // We do this step at the end so that we clean up the begin file in the case that we hit above catch block
+ // Attach a an empty catch to this so that errors here do not propogate
+ return util.deleteInstallFile(util.InstallFileType.Begin).catch((error) => { });
});
}