diff --git a/.vscodeignore b/.vscodeignore index 32376201b..14673ecc7 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -5,14 +5,9 @@ src/** **/*.map .vscode/** -.omnisharp/** **/.nyc_output/** **/coverage/** -coreclr-debug/debugAdapters/** -coreclr-debug/bin/** -coreclr-debug/obj/** -coreclr-debug/project.lock.json -coreclr-debug/install.log +RuntimeLicenses/dependencies/* +coreclr-debug/install.log \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 9522da46b..ade798240 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -5,21 +5,142 @@ 'use strict'; +const fs = require('fs'); +const path = require('path'); const del = require('del'); const gulp = require('gulp'); +const gulpUtil = require('gulp-util'); const tslint = require('gulp-tslint'); const vsce = require('vsce'); -//const omnisharpDownload = require('./out/omnisharpDownload'); +const debugUtil = require('./out/coreclr-debug/util.js'); +const debugInstall = require('./out/coreclr-debug/install.js'); +const fs_extra = require('fs-extra-promise'); +const omnisharpDownload = require('./out/omnisharpDownload'); +const child_process = require('child_process'); + +const OmniSharpVersion = omnisharpDownload.OmniSharpVersion; + +/// used in offline packaging run so does not clean .vsix +function clean() { + cleanDebugger(); + return cleanOmnisharp(); +} + +gulp.task('clean', ['omnisharp:clean', 'debugger:clean', 'package:clean'], () => { + +}); + +/// Omnisharp Tasks +function installOmnisharp(omnisharpAssetName) { + const logFunction = (message) => { console.log(message); }; + return omnisharpDownload.downloadOmnisharp(logFunction, omnisharpAssetName); +} + +function cleanOmnisharp() { + return del('.omnisharp'); +} gulp.task('omnisharp:clean', () => { - return del('.omnisharp'); + return cleanOmnisharp(); +}); + +gulp.task('omnisharp:install', ['omnisharp:clean'], () => { + var asset = gulpUtil.env.asset || omnisharpDownload.getOmnisharpAssetName(); + return installOmnisharp(asset); +}); + +/// 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 +function doPackageSync(packageName) { + + var vsceArgs = []; + vsceArgs.push(path.join(__dirname, 'node_modules', 'vsce', 'out', 'vsce')) + vsceArgs.push('package'); // package command + + if (packageName !== undefined) { + vsceArgs.push('-o'); + vsceArgs.push(packageName); + } + + var proc = child_process.spawnSync('node', vsceArgs); + if (proc.error) { + console.error(proc.error.toString()); + } +} + +function doOfflinePackage(runtimeId, omnisharpAsset, packageName) { + return clean().then(() => { + return installDebugger(runtimeId); + }).then(() => { + return installOmnisharp(omnisharpAsset); + }).then(() => { + doPackageSync(packageName + '-' + runtimeId + '.vsix'); + }); +} + +gulp.task('package:clean', () => { + return del('*.vsix'); +}); + +gulp.task('package:online', ['clean'], () => { + doPackageSync(); }); -//TODO: decouple omnisharpDownload (specifically proxy.ts) from vscode -// gulp.task('omnisharp:fetch', ['omnisharp:clean'], () => { -// return omnisharpDownload.downloadOmnisharp(); -// }); +gulp.task('package:offline', ['clean'], () => { + var json = JSON.parse(fs.readFileSync('package.json')); + var name = json.name; + var version = json.version; + var packageName = name + '.' + version; + var packages = []; + packages.push({rid: 'win7-x64', omni: `omnisharp-${OmniSharpVersion}-win-x64-net451.zip`}); + packages.push({rid: 'osx.10.11-x64', omni: `omnisharp-${OmniSharpVersion}-osx-x64-netcoreapp1.0.tar.gz`}); + packages.push({rid: 'centos.7-x64', omni: `omnisharp-${OmniSharpVersion}-centos-x64-netcoreapp1.0.tar.gz`}); + packages.push({rid: 'debian.8-x64', omni: `omnisharp-${OmniSharpVersion}-debian-x64-netcoreapp1.0.tar.gz`}); + packages.push({rid: 'rhel.7.2-x64', omni: `omnisharp-${OmniSharpVersion}-rhel-x64-netcoreapp1.0.tar.gz`}); + packages.push({rid: 'ubuntu.14.04-x64', omni: `omnisharp-${OmniSharpVersion}-ubuntu-x64-netcoreapp1.0.tar.gz`}); + + var promise = Promise.resolve(); + + packages.forEach(pair => { + promise = promise.then(() => { + return doOfflinePackage(pair.rid, pair.omni, packageName); + }) + }); + + return promise; +}); + +/// Misc Tasks const allTypeScript = [ 'src/**/*.ts', '!**/*.d.ts', @@ -29,7 +150,7 @@ const allTypeScript = [ const lintReporter = (output, file, options) => { //emits: src/helloWorld.c:5:3: warning: implicit declaration of function ‘prinft’ var relativeBase = file.base.substring(file.cwd.length + 1).replace('\\', '/'); - output.forEach(function(e) { + output.forEach(e => { var message = relativeBase + e.name + ':' + (e.startPosition.line + 1) + ':' + (e.startPosition.character + 1) + ': ' + e.failure; console.log('[tslint] ' + message); }); @@ -44,10 +165,4 @@ gulp.task('tslint', () => { summarizeFailureOutput: false, emitError: false })) -}); - -// gulp.task('omnisharp', ['omnisharp:fetch']); - -gulp.task('package', () => { - vsce(['', '', 'package']); }); \ No newline at end of file diff --git a/package.json b/package.json index 1ab61fe6c..ca834ebe9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "csharp", "publisher": "ms-vscode", - "version": "1.1.5", + "version": "1.1.6", "description": "C# for Visual Studio Code (powered by OmniSharp).", "displayName": "C#", "author": "Microsoft Corporation", @@ -294,14 +294,25 @@ }, "default": [] }, + "requireExactSource": { + "type": "boolean", + "description": "Optional flag to require current source code to match the pdb.", + "default": true + }, "pipeTransport": { "type": "object", "description": "When present, this tells the debugger to connect to a remote computer using another executable as a pipe that will relay standard input/output between VS Code and the .NET Core debugger backend executable (clrdbg).", "default": { + "pipeCwd": "${workspaceRoot}", "pipeProgram": "enter the fully qualified path for the pipe program name, for example 'c:\\tools\\plink.exe'", "pipeArgs": [] }, "properties" : { + "pipeCwd": { + "type": "string", + "description": "The fully qualified path to the working directory for the pipe program.", + "default": "${workspaceRoot}" + }, "pipeProgram": { "type": "string", "description": "The fully qualified pipe command to execute.", @@ -315,14 +326,26 @@ }, "default": [] }, + "pipeEnv": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Environment variables passed to the pipe program.", + "default": { } + }, "windows": { "type": "object", "description": "Windows-specific pipe launch configuration options", "default": { + "pipeCwd": "${workspaceRoot}", "pipeProgram": "enter the fully qualified path for the pipe program name, for example 'c:\\tools\\plink.exe'", "pipeArgs": [] }, "properties": { + "pipeCwd": { + "type": "string", + "description": "The fully qualified path to the working directory for the pipe program.", + "default": "${workspaceRoot}" + }, "pipeProgram": { "type": "string", "description": "The fully qualified pipe command to execute.", @@ -335,6 +358,12 @@ "type": "string" }, "default": [] + }, + "pipeEnv": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Environment variables passed to the pipe program.", + "default": { } } } }, @@ -342,10 +371,16 @@ "type": "object", "description": "OSX-specific pipe launch configuration options", "default": { + "pipeCwd": "${workspaceRoot}", "pipeProgram": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'", "pipeArgs": [] }, "properties": { + "pipeCwd": { + "type": "string", + "description": "The fully qualified path to the working directory for the pipe program.", + "default": "${workspaceRoot}" + }, "pipeProgram": { "type": "string", "description": "The fully qualified pipe command to execute.", @@ -358,17 +393,29 @@ "type": "string" }, "default": [] - } + }, + "pipeEnv": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Environment variables passed to the pipe program.", + "default": { } + } } }, "linux": { "type": "object", "description": "Linux-specific pipe launch configuration options", "default": { + "pipeCwd": "${workspaceRoot}", "pipeProgram": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'", "pipeArgs": [] }, "properties": { + "pipeCwd": { + "type": "string", + "description": "The fully qualified path to the working directory for the pipe program.", + "default": "${workspaceRoot}" + }, "pipeProgram": { "type": "string", "description": "The fully qualified pipe command to execute.", @@ -381,6 +428,12 @@ "type": "string" }, "default": [] + }, + "pipeEnv": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Environment variables passed to the pipe program.", + "default": { } } } } @@ -419,6 +472,11 @@ "items": { "type": "string" }, + "requireExactSource": { + "type": "boolean", + "description": "Optional flag to require current source code to match the pdb.", + "default": true + }, "default": [] } } diff --git a/src/coreclr-debug/activate.ts b/src/coreclr-debug/activate.ts new file mode 100644 index 000000000..3445d1450 --- /dev/null +++ b/src/coreclr-debug/activate.ts @@ -0,0 +1,194 @@ +/*--------------------------------------------------------------------------------------------- + * 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 * 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'; + +let _reporter: TelemetryReporter = null; +let _channel: vscode.OutputChannel = null; +let _util: CoreClrDebugUtil = null; + +export function activate(context: vscode.ExtensionContext, reporter: TelemetryReporter) { + _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; + } + + if (!isOnPath('dotnet')) { + const getDotNetMessage = "Get .NET CLI tools"; + 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.", + getDotNetMessage).then(value => { + if (value === getDotNetMessage) { + let open = require('open'); + open("http://dotnet.github.io/getting-started/"); + } + }); + + return; + } + + let installer = new debugInstall.DebugInstaller(_util); + _util.createInstallLog(); + + let runtimeId = getPlatformRuntimeId(); + + let statusBarMessage = vscode.window.setStatusBarMessage("Downloading and configuring the .NET Core Debugger..."); + + let installStage = "installBegin"; + let installError = ""; + + 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.installError; + }).then(() => { + // log telemetry and delete install begin file + logTelemetry('Acquisition', {installStage: installStage, installError: installError}); + try { + deleteInstallBeginFile(); + } catch (err) { + // if this throws there's really nothing we can do + } + _util.closeInstallLog(); + }); +} + +function logTelemetry(eventName: string, properties?: {[prop: string]: string}): void { + 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()); + } +} + +// Determines if the specified command is in one of the directories in the PATH environment variable. +function isOnPath(command : string) : boolean { + let pathValue = process.env['PATH']; + if (!pathValue) { + return false; + } + let fileName = command; + if (process.platform == 'win32') { + // on Windows, add a '.exe', and the path is semi-colon seperatode + fileName = fileName + ".exe"; + } + + let pathSegments: string[] = pathValue.split(path.delimiter); + for (let segment of pathSegments) { + if (segment.length === 0 || !path.isAbsolute(segment)) { + continue; + } + + const segmentPath = path.join(segment, fileName); + + try { + if (CoreClrDebugUtil.existsSync(segmentPath)) { + return true; + } + } catch (err) { + // any error from existsSync can be treated as the command not being on the path + continue; + } + } + + return false; +} + +function getPlatformRuntimeId() : string { + switch (process.platform) { + case 'win32': + return 'win7-x64'; + case 'darwin': + return getDotnetRuntimeId(); + case 'linux': + return getDotnetRuntimeId(); + default: + _util.log('Error: Unsupported platform ' + process.platform); + throw Error('Unsupported platform ' + process.platform); + } +} + +function getDotnetRuntimeId(): string { + _util.log("Starting 'dotnet --info'"); + + const cliVersionErrorMessage = "Ensure that .NET Core CLI Tools version >= 1.0.0-beta-002173 is installed. Run 'dotnet --version' to see what version is installed."; + + let child = child_process.spawnSync('dotnet', ['--info'], { cwd: _util.coreClrDebugDir() }); + + if (child.stderr.length > 0) { + _util.log('Error: ' + child.stderr.toString()); + } + const out = child.stdout.toString(); + if (out.length > 0) { + _util.log(out); + } + + if (child.status !== 0) { + const message = `Error: 'dotnet --info' failed with error ${child.status}`; + _util.log(message); + _util.log(cliVersionErrorMessage); + throw new Error(message); + } + + if (out.length === 0) { + const message = "Error: 'dotnet --info' provided no output"; + _util.log(message); + _util.log(cliVersionErrorMessage); + throw new Error(message); + } + + let lines = out.split('\n'); + let ridLine = lines.filter(value => { + return value.trim().startsWith('RID:'); + }); + + if (ridLine.length < 1) { + _util.log("Error: Cannot find 'RID' property"); + _util.log(cliVersionErrorMessage); + throw new Error('Cannot obtain Runtime ID from dotnet cli'); + } + + let rid = ridLine[0].split(':')[1].trim(); + + if (!rid) { + _util.log("Error: Unable to parse 'RID' property."); + _util.log(cliVersionErrorMessage); + throw new Error('Unable to determine Runtime ID'); + } + + return rid; +} diff --git a/src/coreclr-debug/install.ts b/src/coreclr-debug/install.ts new file mode 100644 index 000000000..58a9fa271 --- /dev/null +++ b/src/coreclr-debug/install.ts @@ -0,0 +1,231 @@ +/*--------------------------------------------------------------------------------------------- + * 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 child_process from 'child_process'; +import * as fs_extra from 'fs-extra-promise'; + +export class InstallError extends Error { + public installStage: string; + public installError: string; + + constructor(stage: string, error: string) { + super("Error during installation."); + this.installStage = stage; + this.installError = error; + } +} + +export class DebugInstaller +{ + private _util: CoreClrDebugUtil = null; + private _isOffline; + + constructor(util: CoreClrDebugUtil, isOffline?: boolean) { + this._util = util; + this._isOffline = isOffline || false; + } + + public install(runtimeId: string): Promise { + let installStage = 'writeProjectJson'; + return this.writeProjectJson(runtimeId).then(() => { + installStage = 'dotnetRestore'; + return this.spawnChildProcess('dotnet', ['--verbose', 'restore', '--configfile', 'NuGet.config'], this._util.coreClrDebugDir()); + }).then(() => { + installStage = "dotnetPublish"; + return this.spawnChildProcess('dotnet', ['--verbose', 'publish', '-r', runtimeId, '-o', this._util.debugAdapterDir()], this._util.coreClrDebugDir()); + }).then(() => { + installStage = "ensureAd7"; + return this.ensureAd7EngineExists(this._util.debugAdapterDir()); + }).then(() => { + installStage = "renameDummyEntrypoint"; + return this.renameDummyEntrypoint(); + }).then(() => { + installStage = "rewriteManifest"; + this.rewriteManifest(); + installStage = "writeCompletionFile"; + return this.writeCompletionFile(); + }).catch((error) => { + throw new InstallError(installStage, error.toString()); + }); + } + + 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'); + let manifestObject = JSON.parse(manifestString); + delete manifestObject.contributes.debuggers[0].runtime; + delete manifestObject.contributes.debuggers[0].program; + + let programString = './coreclr-debug/debugAdapters/OpenDebugAD7'; + manifestObject.contributes.debuggers[0].windows = { program: programString + '.exe' }; + manifestObject.contributes.debuggers[0].osx = { program: programString }; + manifestObject.contributes.debuggers[0].linux = { program: programString }; + + 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 + // build that I have tested. + 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": "14.0.25406-preview-3044032", + "Microsoft.VisualStudio.clrdbg.MIEngine": "14.0.30610-preview-1", + "Microsoft.VisualStudio.OpenDebugAD7": "1.0.20614-preview-2", + "NETStandard.Library": "1.5.0-rc2-24027", + "Newtonsoft.Json": "7.0.1", + "Microsoft.VisualStudio.Debugger.Interop.Portable": "1.0.1", + "System.Collections.Specialized": "4.0.1-rc2-24027", + "System.Collections.Immutable": "1.2.0-rc2-24027", + "System.Diagnostics.Process" : "4.1.0-rc2-24027", + "System.Diagnostics.StackTrace": "4.0.1-rc2-24027", + "System.Dynamic.Runtime": "4.0.11-rc2-24027", + "Microsoft.CSharp": "4.0.1-rc2-24027", + "System.Threading.Tasks.Dataflow": "4.6.0-rc2-24027", + "System.Threading.Thread": "4.0.0-rc2-24027", + "System.Xml.XDocument": "4.0.11-rc2-24027", + "System.Xml.XmlDocument": "4.0.1-rc2-24027", + "System.Xml.XmlSerializer": "4.0.11-rc2-24027", + "System.ComponentModel": "4.0.1-rc2-24027", + "System.ComponentModel.Annotations": "4.1.0-rc2-24027", + "System.ComponentModel.EventBasedAsync": "4.0.11-rc2-24027", + "System.Runtime.Serialization.Primitives": "4.1.1-rc2-24027", + "System.Net.Http": "4.0.1-rc2-24027" + }, + frameworks: { + "netcoreapp1.0": { + imports: [ "dnxcore50", "portable-net45+win8" ] + } + }, + runtimes: { + } + }; + + projectJson.runtimes[targetRuntime] = {}; + + if (this._isOffline) { + projectJson.dependencies["Microsoft.NetCore.DotNetHostPolicy"] = "1.0.1-rc-002702"; + } + + return projectJson; + } + + private spawnChildProcess(process: string, args: string[], workingDirectory: string) : Promise { + const promise = new Promise((resolve, reject) => { + const child = child_process.spawn(process, args, {cwd: workingDirectory}); + + child.stdout.on('data', (data) => { + this._util.log(`${data}`); + }); + + child.stderr.on('data', (data) => { + this._util.log(`Error: ${data}`); + }); + + child.on('close', (code: number) => { + if (code != 0) { + this._util.log(`${process} exited with error code ${code}`); + reject(new Error(code.toString())); + } + else { + resolve(); + } + }); + }); + + return promise; + } +} \ No newline at end of file diff --git a/src/coreclr-debug/main.ts b/src/coreclr-debug/main.ts deleted file mode 100644 index f8c77524c..000000000 --- a/src/coreclr-debug/main.ts +++ /dev/null @@ -1,400 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 * 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' - -let _channel: vscode.OutputChannel; -let _installLog: NodeJS.WritableStream; -let _reporter: TelemetryReporter; // Telemetry reporter -let _util: CoreClrDebugUtil; - -export function activate(context: vscode.ExtensionContext, reporter: TelemetryReporter) { - _util = new CoreClrDebugUtil(context.extensionPath); - - if (CoreClrDebugUtil.existsSync(_util.installCompleteFilePath())) { - console.log('.NET Core Debugger tools already installed'); - return; - } - - if (!isOnPath('dotnet')) { - const getDotNetMessage = "Get .NET CLI tools"; - 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.", - getDotNetMessage).then(function (value) { - if (value === getDotNetMessage) { - let open = require('open'); - open("http://dotnet.github.io/getting-started/"); - } - }); - - return; - } - - _reporter = reporter; - _channel = vscode.window.createOutputChannel('coreclr-debug'); - - // Create our log file and override _channel.append to also output to the log - _installLog = fs.createWriteStream(_util.installLogPath()); - (function() { - let proxied = _channel.append; - _channel.append = function(val: string) { - _installLog.write(val); - proxied.apply(this, arguments); - }; - })(); - - let statusBarMessage = vscode.window.setStatusBarMessage("Downloading and configuring the .NET Core Debugger..."); - - let installStage = 'installBegin'; - let installError = ''; - - writeInstallBeginFile().then(function() { - installStage = 'writeProjectJson'; - return writeProjectJson(_channel); - }).then(function() { - installStage = 'dotnetRestore' - return spawnChildProcess('dotnet', ['--verbose', 'restore', '--configfile', 'NuGet.config'], _channel, _util.coreClrDebugDir()) - }).then(function() { - installStage = "dotnetPublish"; - return spawnChildProcess('dotnet', ['--verbose', 'publish', '-o', _util.debugAdapterDir()], _channel, _util.coreClrDebugDir()); - }).then(function() { - installStage = "ensureAd7"; - return ensureAd7EngineExists(_channel, _util.debugAdapterDir()); - }).then(function() { - installStage = "additionalTasks"; - let promises: Promise[] = []; - - promises.push(renameDummyEntrypoint()); - promises.push(removeLibCoreClrTraceProvider()); - - return Promise.all(promises); - }).then(function() { - installStage = "rewriteManifest"; - rewriteManifest(); - installStage = "writeCompletionFile"; - return writeCompletionFile(); - }).then(function() { - installStage = "completeSuccess"; - statusBarMessage.dispose(); - vscode.window.setStatusBarMessage('Successfully installed .NET Core Debugger.'); - }) - .catch(function(error) { - const viewLogMessage = "View Log"; - vscode.window.showErrorMessage('Error while installing .NET Core Debugger.', viewLogMessage).then(function (value) { - if (value === viewLogMessage) { - _channel.show(vscode.ViewColumn.Three); - } - }); - statusBarMessage.dispose(); - - installError = error.toString(); - console.log(error); - - - }).then(function() { - // log telemetry and delete install begin file - logTelemetry('Acquisition', {installStage: installStage, installError: installError}); - - try { - deleteInstallBeginFile(); - } catch (err) { - // if this throws there's really nothing we can do - } - }); -} - -function logTelemetry(eventName: string, properties?: {[prop: string]: string}) { - if (_reporter) - { - _reporter.sendTelemetryEvent('coreclr-debug/' + eventName, properties); - } -} - -function rewriteManifest() : void { - const manifestPath = path.join(_util.extensionDir(), 'package.json'); - let manifestString = fs.readFileSync(manifestPath, 'utf8'); - let manifestObject = JSON.parse(manifestString); - manifestObject.contributes.debuggers[0].runtime = ''; - manifestObject.contributes.debuggers[0].program = './coreclr-debug/debugAdapters/OpenDebugAD7' + CoreClrDebugUtil.getPlatformExeExtension(); - manifestString = JSON.stringify(manifestObject, null, 2); - fs.writeFileSync(manifestPath, manifestString); -} - -function writeInstallBeginFile() : Promise { - return writeEmptyFile(_util.installBeginFilePath()); -} - -function deleteInstallBeginFile() { - if (CoreClrDebugUtil.existsSync(_util.installBeginFilePath())) { - fs.unlinkSync(_util.installBeginFilePath()); - } -} - -function writeCompletionFile() : Promise { - return writeEmptyFile(_util.installCompleteFilePath()); -} - -function writeEmptyFile(path: string) : Promise { - return new Promise(function(resolve, reject) { - fs.writeFile(path, '', function(err) { - if (err) { - reject(err.code); - } else { - resolve(); - } - }); - }); -} - -function renameDummyEntrypoint() : Promise { - let src = path.join(_util.debugAdapterDir(), 'dummy'); - let dest = path.join(_util.debugAdapterDir(), 'OpenDebugAD7'); - - src += CoreClrDebugUtil.getPlatformExeExtension(); - dest += CoreClrDebugUtil.getPlatformExeExtension(); - - const promise = new Promise(function(resolve, reject) { - fs.rename(src, dest, function(err) { - if (err) { - reject(err.code); - } else { - resolve(); - } - }); - }); - - return promise; -} - -function removeLibCoreClrTraceProvider() : Promise -{ - const filePath = path.join(_util.debugAdapterDir(), 'libcoreclrtraceptprovider' + CoreClrDebugUtil.getPlatformLibExtension()); - - if (!CoreClrDebugUtil.existsSync(filePath)) { - return Promise.resolve(); - } else { - return new Promise(function(resolve, reject) { - fs.unlink(filePath, function(err) { - if (err) { - reject(err.code); - } else { - _channel.appendLine('Successfully deleted ' + filePath); - resolve(); - } - }); - }); - } -} - -// Determines if the specified command is in one of the directories in the PATH environment variable. -function isOnPath(command : string) : boolean { - let pathValue = process.env['PATH']; - if (!pathValue) { - return false; - } - let fileName = command; - if (process.platform == 'win32') { - // on Windows, add a '.exe', and the path is semi-colon seperatode - fileName = fileName + ".exe"; - } - - let pathSegments: string[] = pathValue.split(path.delimiter); - for (let segment of pathSegments) { - if (segment.length === 0 || !path.isAbsolute(segment)) { - continue; - } - - const segmentPath = path.join(segment, fileName); - - try { - if (CoreClrDebugUtil.existsSync(segmentPath)) { - return true; - } - } catch (err) { - // any error from existsSync can be treated as the command not being on the path - continue; - } - } - - return false; -} - -function ensureAd7EngineExists(channel: vscode.OutputChannel, 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 { - channel.appendLine(`${filePath} does not exist.`); - channel.appendLine(''); - // NOTE: The minimum build number is actually less than 1584, but this is the minimum - // build that I have tested. - channel.appendLine("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."); - } - }); - }); -} - -function writeProjectJson(channel: vscode.OutputChannel): Promise { - return new Promise(function (resolve, reject) { - const projectJsonPath = path.join(_util.coreClrDebugDir(), 'project.json'); - _channel.appendLine('Creating ' + projectJsonPath); - - const projectJson = createProjectJson(getPlatformRuntimeId(channel)); - - fs.writeFile(projectJsonPath, JSON.stringify(projectJson, null, 2), {encoding: 'utf8'}, function(err) { - if (err) { - channel.appendLine('Error: Unable to write to project.json: ' + err.message); - reject(err.code); - } - else { - resolve(); - } - }); - }); -} - -function getPlatformRuntimeId(channel: vscode.OutputChannel) : string { - switch (process.platform) { - case 'win32': - return 'win7-x64'; - case 'darwin': - return getDotnetRuntimeId(channel); - case 'linux': - return getDotnetRuntimeId(channel); - default: - channel.appendLine('Error: Unsupported platform ' + process.platform); - throw Error('Unsupported platform ' + process.platform); - } -} - -function getDotnetRuntimeId(channel: vscode.OutputChannel): string { - channel.appendLine("Starting 'dotnet --info'"); - - const cliVersionErrorMessage = "Ensure that .NET Core CLI Tools version >= 1.0.0-beta-002173 is installed. Run 'dotnet --version' to see what version is installed."; - - let child = child_process.spawnSync('dotnet', ['--info'], { cwd: _util.coreClrDebugDir() }); - - if (child.stderr.length > 0) { - channel.append('Error: ' + child.stderr.toString()); - } - const out = child.stdout.toString(); - if (out.length > 0) { - channel.append(out); - } - - if (child.status !== 0) { - const message = `Error: 'dotnet --info' failed with error ${child.status}`; - channel.appendLine(message); - channel.appendLine(cliVersionErrorMessage); - throw new Error(message); - } - - if (out.length === 0) { - const message = "Error: 'dotnet --info' provided no output"; - channel.appendLine(message); - channel.appendLine(cliVersionErrorMessage); - throw new Error(message); - } - - let lines = out.split('\n'); - let ridLine = lines.filter(function (value) { - return value.trim().startsWith('RID:'); - }); - - if (ridLine.length < 1) { - channel.appendLine("Error: Cannot find 'RID' property"); - channel.appendLine(cliVersionErrorMessage); - throw new Error('Cannot obtain Runtime ID from dotnet cli'); - } - - let rid = ridLine[0].split(':')[1].trim(); - - if (!rid) { - channel.appendLine("Error: Unable to parse 'RID' property."); - channel.appendLine(cliVersionErrorMessage); - throw new Error('Unable to determine Runtime ID'); - } - - return rid; -} - -function createProjectJson(targetRuntime: string): any -{ - let projectJson = { - name: "dummy", - buildOptions: { - emitEntryPoint: true - }, - dependencies: { - "Microsoft.VisualStudio.clrdbg": "14.0.25406-preview-3044032", - "Microsoft.VisualStudio.clrdbg.MIEngine": "14.0.30606-preview-1", - "Microsoft.VisualStudio.OpenDebugAD7": "1.0.20527-preview-1", - "NETStandard.Library": "1.5.0-rc2-24027", - "Newtonsoft.Json": "7.0.1", - "Microsoft.VisualStudio.Debugger.Interop.Portable": "1.0.1", - "System.Collections.Specialized": "4.0.1-rc2-24027", - "System.Collections.Immutable": "1.2.0-rc2-24027", - "System.Diagnostics.Process" : "4.1.0-rc2-24027", - "System.Diagnostics.StackTrace": "4.0.1-rc2-24027", - "System.Dynamic.Runtime": "4.0.11-rc2-24027", - "Microsoft.CSharp": "4.0.1-rc2-24027", - "System.Threading.Tasks.Dataflow": "4.6.0-rc2-24027", - "System.Threading.Thread": "4.0.0-rc2-24027", - "System.Xml.XDocument": "4.0.11-rc2-24027", - "System.Xml.XmlDocument": "4.0.1-rc2-24027", - "System.Xml.XmlSerializer": "4.0.11-rc2-24027", - "System.ComponentModel": "4.0.1-rc2-24027", - "System.ComponentModel.Annotations": "4.1.0-rc2-24027", - "System.ComponentModel.EventBasedAsync": "4.0.11-rc2-24027", - "System.Runtime.Serialization.Primitives": "4.1.1-rc2-24027", - "System.Net.Http": "4.0.1-rc2-24027" - }, - frameworks: { - "netstandardapp1.5": { - imports: [ "dnxcore50", "portable-net45+win8" ] - } - }, - runtimes: { - } - } - - projectJson.runtimes[targetRuntime] = {}; - - return projectJson; -} - -function spawnChildProcess(process: string, args: string[], channel: vscode.OutputChannel, workingDirectory: string) : Promise { - const promise = new Promise(function(resolve, reject) { - const child = child_process.spawn(process, args, {cwd: workingDirectory}); - - child.stdout.on('data', (data) => { - channel.append(`${data}`); - }); - - child.stderr.on('data', (data) => { - channel.appendLine(`Error: ${data}`); - }); - - child.on('close', (code: number) => { - if (code != 0) { - channel.appendLine(`${process} exited with error code ${code}`); - reject(new Error(code.toString())); - } - else { - resolve(); - } - }); - }); - - return promise; -} diff --git a/src/coreclr-debug/proxy.ts b/src/coreclr-debug/proxy.ts index d6a9c3bfd..8e9889563 100644 --- a/src/coreclr-debug/proxy.ts +++ b/src/coreclr-debug/proxy.ts @@ -7,7 +7,7 @@ import * as path from 'path'; import { DebugProtocol } from 'vscode-debugprotocol'; import * as child_process from 'child_process'; -import CoreClrDebugUtil from './util'; +import { CoreClrDebugUtil } from './util'; class ProxyErrorResponse implements DebugProtocol.ErrorResponse { public body: { error?: DebugProtocol.Message }; @@ -50,7 +50,7 @@ function proxy() { } else { - new Promise(function(resolve, reject) { + new Promise((resolve, reject) => { let processPath = path.join(util.debugAdapterDir(), "OpenDebugAD7" + CoreClrDebugUtil.getPlatformExeExtension()); let args = process.argv.slice(2); @@ -59,7 +59,7 @@ function proxy() { const child = child_process.spawn(processPath, args); // If we don't exit cleanly from the child process, log the error. - child.on('close', function(code: number) { + child.on('close', code => { if (code !== 0) { reject(new Error(code.toString())); } else { @@ -69,38 +69,38 @@ function proxy() { process.stdin.setEncoding('utf8'); - child.on('error', function(data) { + child.on('error', data => { util.logToFile(`Child error: ${data}`); }); - process.on('SIGTERM', function() { + process.on('SIGTERM', () => { child.kill(); process.exit(0); }); - process.on('SIGHUP', function() { + process.on('SIGHUP', () => { child.kill(); process.exit(0); }); - process.stdin.on('error', function(error) { + process.stdin.on('error', error => { util.logToFile(`process.stdin error: ${error}`); }); - process.stdout.on('error', function(error) { + process.stdout.on('error', error => { util.logToFile(`process.stdout error: ${error}`); }); - child.stdout.on('data', function(data) { + child.stdout.on('data', data => { process.stdout.write(data); }); - process.stdin.on('data', function(data) { + process.stdin.on('data', data => { child.stdin.write(data); }); process.stdin.resume(); - }).catch(function(err) { + }).catch(err => { util.logToFile(`Promise failed: ${err}`); }); } diff --git a/src/coreclr-debug/util.ts b/src/coreclr-debug/util.ts index e17f35af9..b29575f7d 100644 --- a/src/coreclr-debug/util.ts +++ b/src/coreclr-debug/util.ts @@ -7,16 +7,9 @@ import * as path from 'path'; import * as fs from 'fs'; import * as os from 'os'; -import * as child_process from 'child_process' +import * as vscode from 'vscode'; -let _extensionDir: string = ''; -let _coreClrDebugDir: string = ''; -let _debugAdapterDir: string = ''; -let _installLogPath: string = ''; -let _installBeginFilePath: string = ''; -let _installCompleteFilePath: string = ''; - -export default class CoreClrDebugUtil +export class CoreClrDebugUtil { private _extensionDir: string = ''; private _coreClrDebugDir: string = ''; @@ -24,58 +17,97 @@ export default class CoreClrDebugUtil private _installLogPath: string = ''; private _installBeginFilePath: string = ''; private _installCompleteFilePath: string = ''; + + private _installLog: fs.WriteStream = null; + private _channel: vscode.OutputChannel = null; - constructor(extensionDir) { - _extensionDir = extensionDir; - _coreClrDebugDir = path.join(_extensionDir, 'coreclr-debug'); - _debugAdapterDir = path.join(_coreClrDebugDir, 'debugAdapters'); - _installLogPath = path.join(_coreClrDebugDir, 'install.log'); - _installBeginFilePath = path.join(_coreClrDebugDir, 'install.begin'); - _installCompleteFilePath = path.join(_debugAdapterDir, 'install.complete'); + constructor(extensionDir: string, channel?: vscode.OutputChannel) { + 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._installCompleteFilePath = path.join(this._debugAdapterDir, 'install.complete'); + + this._channel = channel; } extensionDir(): string { - if (_extensionDir === '') + if (this._extensionDir === '') { throw new Error('Failed to set extension directory'); } - return _extensionDir; + return this._extensionDir; } coreClrDebugDir(): string { - if (_coreClrDebugDir === '') { + if (this._coreClrDebugDir === '') { throw new Error('Failed to set coreclrdebug directory'); } - return _coreClrDebugDir; + return this._coreClrDebugDir; } debugAdapterDir(): string { - if (_debugAdapterDir === '') { + if (this._debugAdapterDir === '') { throw new Error('Failed to set debugadpter directory'); } - return _debugAdapterDir; + return this._debugAdapterDir; } installLogPath(): string { - if (_installLogPath === '') { + if (this._installLogPath === '') { throw new Error('Failed to set install log path'); } - return _installLogPath; + return this._installLogPath; } installBeginFilePath(): string { - if (_installBeginFilePath === '') { + if (this._installBeginFilePath === '') { throw new Error('Failed to set install begin file path'); } - return _installBeginFilePath; + return this._installBeginFilePath; } installCompleteFilePath(): string { - if (_installCompleteFilePath === '') + if (this._installCompleteFilePath === '') { throw new Error('Failed to set install complete file path'); } - return _installCompleteFilePath; + return this._installCompleteFilePath; + } + + createInstallLog(): void { + this._installLog = fs.createWriteStream(this.installLogPath()); + } + + closeInstallLog(): void { + if (this._installLog !== null) { + this._installLog.close(); + } + } + + 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 { + return new Promise((resolve, reject) => { + fs.writeFile(path, '', (err) => { + if (err) { + reject(err.code); + } else { + resolve(); + } + }); + }); } static existsSync(path: string) : boolean { diff --git a/src/omnisharpDownload.ts b/src/omnisharpDownload.ts index 5fcae4431..f64ab72b0 100644 --- a/src/omnisharpDownload.ts +++ b/src/omnisharpDownload.ts @@ -13,17 +13,16 @@ import * as tmp from 'tmp'; import {parse} from 'url'; import {SupportedPlatform, getSupportedPlatform} from './utils'; import {getProxyAgent} from './proxy'; -import {OutputChannel} from 'vscode'; const decompress = require('decompress'); const BaseDownloadUrl = 'https://vscodeoscon.blob.core.windows.net/ext'; const DefaultInstallLocation = path.join(__dirname, '../.omnisharp'); -const OmniSharpVersion = '1.9-beta5'; +export const OmniSharpVersion = '1.9-beta5'; tmp.setGracefulCleanup(); -function getOmnisharpAssetName(): string { +export function getOmnisharpAssetName(): string { switch (getSupportedPlatform()) { case SupportedPlatform.Windows: return `omnisharp-${OmniSharpVersion}-win-x64-net451.zip`; @@ -48,10 +47,10 @@ function getOmnisharpAssetName(): string { } } -function download(urlString: string): Promise { +function download(urlString: string, proxy?: string, strictSSL?: boolean): Promise { let url = parse(urlString); - const agent = getProxyAgent(url); + const agent = getProxyAgent(url, proxy, strictSSL); let options: https.RequestOptions = { host: url.host, @@ -74,23 +73,23 @@ function download(urlString: string): Promise { }); } -export function downloadOmnisharp(output: OutputChannel): Promise { +export function downloadOmnisharp(log: (message: string) => void, omnisharpAssetName?: string, proxy?: string, strictSSL?: boolean) { return new Promise((resolve, reject) => { - output.appendLine(`[INFO] Installing to ${DefaultInstallLocation}`); - - const assetName = getOmnisharpAssetName(); + log(`[INFO] Installing to ${DefaultInstallLocation}`); + + const assetName = omnisharpAssetName || getOmnisharpAssetName(); const urlString = `${BaseDownloadUrl}/${assetName}`; - - output.appendLine(`[INFO] Attempting to download ${assetName}...`); - return download(urlString) + log(`[INFO] Attempting to download ${assetName}...`); + + return download(urlString, proxy, strictSSL) .then(inStream => { tmp.file((err, tmpPath, fd, cleanupCallback) => { if (err) { return reject(err); } - output.appendLine(`[INFO] Downloading to ${tmpPath}...`); + log(`[INFO] Downloading to ${tmpPath}...`); const outStream = fs.createWriteStream(null, { fd: fd }); @@ -100,16 +99,16 @@ export function downloadOmnisharp(output: OutputChannel): Promise { outStream.once('finish', () => { // At this point, the asset has finished downloading. - output.appendLine(`[INFO] Download complete!`); - output.appendLine(`[INFO] Decompressing...`); + log(`[INFO] Download complete!`); + log(`[INFO] Decompressing...`); return decompress(tmpPath, DefaultInstallLocation) .then(files => { - output.appendLine(`[INFO] Done! ${files.length} files unpacked.`) + log(`[INFO] Done! ${files.length} files unpacked.`); return resolve(true); }) .catch(err => { - output.appendLine(`[ERROR] ${err}`); + log(`[ERROR] ${err}`); return reject(err); }); }); @@ -119,7 +118,7 @@ export function downloadOmnisharp(output: OutputChannel): Promise { }) .catch(err => { - output.appendLine(`[ERROR] ${err}`); + log(`[ERROR] ${err}`); }); }); } \ No newline at end of file diff --git a/src/omnisharpMain.ts b/src/omnisharpMain.ts index ec5e51e04..ddedf93e8 100644 --- a/src/omnisharpMain.ts +++ b/src/omnisharpMain.ts @@ -22,7 +22,7 @@ import registerCommands from './features/commands'; import {StdioOmnisharpServer} from './omnisharpServer'; import forwardChanges from './features/changeForwarding'; import reportStatus from './features/omnisharpStatus'; -import * as coreclrdebug from './coreclr-debug/main'; +import * as coreclrdebug from './coreclr-debug/activate'; import {addAssetsIfNecessary} from './assets'; import * as vscode from 'vscode'; import TelemetryReporter from 'vscode-extension-telemetry'; @@ -91,7 +91,7 @@ export function activate(context: vscode.ExtensionContext): any { server.stop(); })); - // install coreclr-debug + // activate coreclr-debug coreclrdebug.activate(context, reporter); context.subscriptions.push(...disposables); diff --git a/src/omnisharpPath.ts b/src/omnisharpPath.ts index a1f439277..f1554edf5 100644 --- a/src/omnisharpPath.ts +++ b/src/omnisharpPath.ts @@ -50,7 +50,7 @@ function getLaunchPathFromSettings(): Promise { .catch(err => { vscode.window.showWarningMessage(`Invalid "csharp.omnisharp" use setting specified ('${setting}).`); throw err; - }) + }); } return Promise.reject(new Error('OmniSharp use setting does not exists.')); diff --git a/src/omnisharpServer.ts b/src/omnisharpServer.ts index 33bf07224..582cffac6 100644 --- a/src/omnisharpServer.ts +++ b/src/omnisharpServer.ts @@ -14,7 +14,7 @@ import {Disposable, CancellationToken, OutputChannel, workspace, window} from 'v import {ErrorMessage, UnresolvedDependenciesMessage, MSBuildProjectDiagnostics, ProjectInformationResponse} from './protocol'; import getLaunchTargets, {LaunchTarget} from './launchTargetFinder'; import TelemetryReporter from 'vscode-extension-telemetry'; -import * as vscode from 'vscode' +import * as vscode from 'vscode'; enum ServerState { Starting, @@ -153,7 +153,7 @@ export abstract class OmnisharpServer { } public reportAndClearTelemetry() { - for (var path in this._requestDelays) { + for (const path in this._requestDelays) { const eventName = 'omnisharp' + path; const measures = this._requestDelays[path].toMeasures(); diff --git a/src/omnisharpServerLauncher.ts b/src/omnisharpServerLauncher.ts index b4d0bb87d..63a0d8e3a 100644 --- a/src/omnisharpServerLauncher.ts +++ b/src/omnisharpServerLauncher.ts @@ -5,13 +5,11 @@ 'use strict'; -import {exists as fileExists} from 'fs'; import {spawn, ChildProcess} from 'child_process'; import {workspace, OutputChannel} from 'vscode'; import {satisfies} from 'semver'; -import {join} from 'path'; import {getOmnisharpLaunchFilePath} from './omnisharpPath'; -import {downloadOmnisharp} from './omnisharpDownload'; +import {downloadOmnisharp, getOmnisharpAssetName} from './omnisharpDownload'; import {SupportedPlatform, getSupportedPlatform} from './utils'; const isWindows = process.platform === 'win32'; @@ -37,9 +35,14 @@ export function installOmnisharpIfNeeded(output: OutputChannel): Promise throw err; } - return downloadOmnisharp(output).then(_ => { + const logFunction = (message: string) => { output.appendLine(message); }; + const omnisharpAssetName = getOmnisharpAssetName(); + const proxy = workspace.getConfiguration().get('http.proxy'); + const strictSSL = workspace.getConfiguration().get('http.proxyStrictSSL', true); + + return downloadOmnisharp(logFunction, omnisharpAssetName, proxy, strictSSL).then(_ => { return getOmnisharpLaunchFilePath(); - }) + }); }); } diff --git a/src/proxy.ts b/src/proxy.ts index 1eaa96197..250554fd3 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -8,7 +8,6 @@ import { Url, parse as parseUrl } from 'url'; import HttpProxyAgent = require('http-proxy-agent'); import HttpsProxyAgent = require('https-proxy-agent'); -import {workspace} from 'vscode'; function getSystemProxyURL(requestURL: Url): string { if (requestURL.protocol === 'http:') { @@ -20,9 +19,8 @@ function getSystemProxyURL(requestURL: Url): string { return null; } -export function getProxyAgent(requestURL: Url): any { - const vsConfigProxyUrl = workspace.getConfiguration().get('http.proxy'); - const proxyURL = vsConfigProxyUrl || getSystemProxyURL(requestURL); +export function getProxyAgent(requestURL: Url, proxy?: string, strictSSL?: boolean): any { + const proxyURL = proxy || getSystemProxyURL(requestURL); if (!proxyURL) { return null; @@ -34,7 +32,8 @@ export function getProxyAgent(requestURL: Url): any { return null; } - const strictSSL = workspace.getConfiguration().get('http.proxyStrictSSL', true); + strictSSL = strictSSL || true; + const opts = { host: proxyEndpoint.hostname, port: Number(proxyEndpoint.port), diff --git a/src/utils.ts b/src/utils.ts index 551c0c197..c9a5958e8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -5,7 +5,7 @@ 'use strict'; -import * as child_process from 'child_process' +import * as child_process from 'child_process'; export enum SupportedPlatform { None,