Skip to content

Commit 809817c

Browse files
committed
Merged PR 474022: Use runtime extension for runtime aquisition
Use runtime extension for runtime aquisition fix https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1814738 debugging screenshot below shows the server is using the local runtime acquired from the extension ![image.png](https://devdiv.visualstudio.com/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_apis/git/repositories/ed74911c-9619-4c14-bd9d-20ecfd2250be/pullRequests/474022/attachments/image.png)
1 parent dc21fcd commit 809817c

File tree

4 files changed

+112
-11
lines changed

4 files changed

+112
-11
lines changed

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@
6464
"test:artifacts": "tsc -p ./ && mocha out/test/artifactTests/**/*.test.js",
6565
"unpackage:vsix": "gulp vsix:release:unpackage",
6666
"updatePackageDependencies": "gulp updatePackageDependencies"
67-
},
67+
},
68+
"extensionDependencies": [
69+
"ms-dotnettools.vscode-dotnet-runtime"
70+
],
6871
"dependencies": {
6972
"@microsoft/servicehub-framework": "4.2.99-beta",
7073
"@types/cross-spawn": "6.0.2",

src/lsptoolshost/dotnetRuntime.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as vscode from 'vscode';
7+
import { CSharpExtensionId } from '../constants/CSharpExtensionId';
8+
9+
export const DotNetRuntimeVersion = '7.0';
10+
11+
interface IDotnetAcquireResult {
12+
dotnetPath: string;
13+
}
14+
15+
let runtimePath: string | undefined;
16+
17+
/**
18+
* Acquires the .NET runtime if it is not already present.
19+
* @returns The path to the .NET runtime
20+
*/
21+
export async function acquireRuntime() {
22+
if (runtimePath) {
23+
return runtimePath;
24+
}
25+
26+
let status = await vscode.commands.executeCommand<IDotnetAcquireResult>('dotnet.acquireStatus', {
27+
version: DotNetRuntimeVersion,
28+
requestingExtensionId: CSharpExtensionId,
29+
});
30+
if (status === undefined) {
31+
await vscode.commands.executeCommand('dotnet.showAcquisitionLog');
32+
33+
status = await vscode.commands.executeCommand<IDotnetAcquireResult>('dotnet.acquire', {
34+
version: DotNetRuntimeVersion,
35+
requestingExtensionId: CSharpExtensionId,
36+
});
37+
if (!status?.dotnetPath) {
38+
throw new Error('Could not resolve the dotnet path!');
39+
}
40+
}
41+
42+
return (runtimePath = status.dotnetPath);
43+
}
44+
45+
/**
46+
* Acquires the .NET runtime and any other dependencies required to spawn a particular .NET executable.
47+
* @param path The path to the entrypoint assembly. Typically a .dll.
48+
* @returns The path to the `dotnet` command to use to spawn the process.
49+
*/
50+
export async function acquireDotNetProcessDependencies(path: string) {
51+
const dotnetPath = await acquireRuntime();
52+
53+
const args = [path];
54+
// This will install any missing Linux dependencies.
55+
await vscode.commands.executeCommand('dotnet.ensureDotnetDependencies', { command: dotnetPath, arguments: args });
56+
57+
return dotnetPath;
58+
}

src/lsptoolshost/roslynLanguageServer.ts

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import {
3535
CodeActionResolveRequest,
3636
} from 'vscode-languageclient/node';
3737
import { PlatformInformation } from '../shared/platform';
38-
import { DotnetResolver } from '../shared/DotnetResolver';
38+
import { acquireDotNetProcessDependencies } from './dotnetRuntime';
3939
import { readConfigurations } from './configurationMiddleware';
4040
import OptionProvider from '../shared/observers/OptionProvider';
4141
import { DynamicFileInfoHandler } from '../razor/src/DynamicFile/DynamicFileInfoHandler';
@@ -147,12 +147,7 @@ export class RoslynLanguageServer {
147147
* Resolves server options and starts the dotnet language server process.
148148
*/
149149
public async start(): Promise<void> {
150-
const dotnetResolver = new DotnetResolver(this.platformInfo);
151-
152150
let options = this.optionProvider.GetLatestOptions();
153-
let resolvedDotnet = await dotnetResolver.getHostExecutableInfo(options);
154-
_channel.appendLine("Dotnet version: " + resolvedDotnet.version);
155-
156151
let logLevel = options.languageServerOptions.logLevel;
157152
const languageClientTraceLevel = this.GetTraceLevel(logLevel);
158153

@@ -343,10 +338,9 @@ export class RoslynLanguageServer {
343338
return capabilities;
344339
}
345340

346-
private async startServer(logLevel: string | undefined): Promise<cp.ChildProcess> {
341+
private getServerPath(options: Options) {
347342
let clientRoot = __dirname;
348343

349-
let options = this.optionProvider.GetLatestOptions();
350344
let serverPath = options.commonOptions.serverPath;
351345
if (!serverPath) {
352346
// Option not set, use the path from the extension.
@@ -357,6 +351,37 @@ export class RoslynLanguageServer {
357351
throw new Error(`Cannot find language server in path '${serverPath}'`);
358352
}
359353

354+
return serverPath;
355+
}
356+
357+
private async startServer(logLevel: string | undefined): Promise<cp.ChildProcess> {
358+
359+
let options = this.optionProvider.GetLatestOptions();
360+
let serverPath = this.getServerPath(options);
361+
362+
let dotnetRuntimePath = options.commonOptions.dotnetPath;
363+
if (!dotnetRuntimePath)
364+
{
365+
let dotnetPath = await acquireDotNetProcessDependencies(serverPath);
366+
dotnetRuntimePath = path.dirname(dotnetPath);
367+
}
368+
369+
const dotnetExecutableName = this.platformInfo.isWindows() ? 'dotnet.exe' : 'dotnet';
370+
const dotnetExecutablePath = path.join(dotnetRuntimePath, dotnetExecutableName);
371+
if (!fs.existsSync(dotnetExecutablePath)) {
372+
throw new Error(`Cannot find dotnet path '${dotnetExecutablePath}'`);
373+
}
374+
375+
_channel.appendLine("Dotnet path: " + dotnetExecutablePath);
376+
377+
// Take care to always run .NET processes on the runtime that we intend.
378+
// The dotnet.exe we point to should not go looking for other runtimes.
379+
const env: NodeJS.ProcessEnv = { ...process.env };
380+
env.DOTNET_ROOT = dotnetRuntimePath;
381+
env.DOTNET_MULTILEVEL_LOOKUP = '0';
382+
// Save user's DOTNET_ROOT env-var value so server can recover the user setting when needed
383+
env.DOTNET_ROOT_USER = process.env.DOTNET_ROOT ?? 'EMPTY';
384+
360385
let args: string[] = [ ];
361386

362387
if (options.commonOptions.waitForDebugger) {
@@ -402,7 +427,8 @@ export class RoslynLanguageServer {
402427
let childProcess: cp.ChildProcessWithoutNullStreams;
403428
let cpOptions: cp.SpawnOptionsWithoutStdio = {
404429
detached: true,
405-
windowsHide: true
430+
windowsHide: true,
431+
env: env
406432
};
407433

408434
if (serverPath.endsWith('.dll')) {

test/runIntegrationTests.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import * as cp from "child_process";
67
import * as path from 'path';
7-
import { runTests } from '@vscode/test-electron';
8+
import {
9+
downloadAndUnzipVSCode,
10+
resolveCliArgsFromVSCodeExecutablePath,
11+
runTests
12+
} from '@vscode/test-electron';
813
import { execChildProcess } from '../src/common';
914

1015
function getSln(workspacePath: string): string | undefined {
@@ -16,6 +21,15 @@ function getSln(workspacePath: string): string | undefined {
1621

1722
async function main() {
1823
try {
24+
const vscodeExecutablePath = await downloadAndUnzipVSCode("stable");
25+
const [cli, ...args] = resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath);
26+
27+
cp.spawnSync(
28+
cli,
29+
[...args, "--install-extension", "ms-dotnettools.vscode-dotnet-runtime"],
30+
{ encoding: "utf-8", stdio: "inherit" },
31+
);
32+
1933
// The folder containing the Extension Manifest package.json
2034
// Passed to `--extensionDevelopmentPath`
2135
const extensionDevelopmentPath = path.resolve(__dirname, '../../');

0 commit comments

Comments
 (0)