diff --git a/vscode-dotnet-runtime-extension/package.json b/vscode-dotnet-runtime-extension/package.json index d427dd907e..cd32a4666c 100644 --- a/vscode-dotnet-runtime-extension/package.json +++ b/vscode-dotnet-runtime-extension/package.json @@ -83,8 +83,8 @@ }, "dotnetAcquisitionExtension.existingDotnetPath": { "type": "array", - "markdownDescription": "The path to an existing .NET host executable for an extension's code to run under, not for your project to run under.\nRestart VS Code to apply changes.\n\n⚠️ This is NOT the .NET Runtime that your project will use to run. Extensions such as `C#`, `C# DevKit`, and more have components written in .NET. This .NET PATH is the `dotnet.exe` that these extensions will use to run their code, not your code.\n\nUsing a path value in which .NET does not meet the requirements of a specific extension will cause that extension to fail.\n\n🚀 The version of .NET that is used for your project is determined by the .NET host, or dotnet.exe. The .NET host picks a runtime based on your project. To use a specific version of .NET for your project, install the .NET SDK using the `.NET Install Tool - Install SDK System-Wide` command, install .NET manually using [our instructions](https://dotnet.microsoft.com/download), or edit your PATH environment variable to point to a `dotnet.exe` that has an `/sdk/` folder with only one SDK.", - "description": "The path to an existing .NET host executable for an extension's code to run under, not for your project to run under.\nRestart VS Code to apply changes.\n\n⚠️ This is NOT the .NET Runtime that your project will use to run. Extensions such as 'C#', 'C# DevKit', and more have components written in .NET. This .NET PATH is the 'dotnet.exe' that these extensions will use to run their code, not your code.\n\nUsing a path value in which .NET does not meet the requirements of a specific extension will cause that extension to fail.\n\n🚀 The version of .NET that is used for your project is determined by the .NET host, or dotnet.exe. The .NET host picks a runtime based on your project. To use a specific version of .NET for your project, install the .NET SDK using the '.NET Install Tool - Install SDK System-Wide' command, use the instructions at https://dotnet.microsoft.com/download to manually install the .NET SDK, or edit your PATH environment variable to point to a 'dotnet.exe' that has an '/sdk/' folder with only one SDK.", + "markdownDescription": "The path to an existing .NET executable for an extension's code to run under, not for your project to run under.\nRestart VS Code to apply changes.\n\n⚠️ This is NOT the .NET Runtime that your project will use to run. Extensions such as `C#`, `C# DevKit`, and more have components written in .NET. This .NET PATH is the `dotnet.exe` that these extensions will use to run their code, not your code.\n\nUsing a path value in which .NET does not meet the requirements of a specific extension will cause that extension to fail.\n\n🚀 The version of .NET that is used for your project is determined by the .NET host, or dotnet.exe. The .NET host picks a runtime based on your project. To use a specific version of .NET for your project, install the .NET SDK using the `.NET Install Tool - Install SDK System-Wide` command, install .NET manually using [our instructions](https://dotnet.microsoft.com/download), or edit your PATH environment variable to point to a `dotnet.exe` that has an `/sdk/` folder with only one SDK.", + "description": "The path to an existing .NET executable for an extension's code to run under, not for your project to run under.\nRestart VS Code to apply changes.\n\n⚠️ This is NOT the .NET Runtime that your project will use to run. Extensions such as 'C#', 'C# DevKit', and more have components written in .NET. This .NET PATH is the 'dotnet.exe' that these extensions will use to run their code, not your code.\n\nUsing a path value in which .NET does not meet the requirements of a specific extension will cause that extension to fail.\n\n🚀 The version of .NET that is used for your project is determined by the .NET host, or dotnet.exe. The .NET host picks a runtime based on your project. To use a specific version of .NET for your project, install the .NET SDK using the '.NET Install Tool - Install SDK System-Wide' command, use the instructions at https://dotnet.microsoft.com/download to manually install the .NET SDK, or edit your PATH environment variable to point to a 'dotnet.exe' that has an '/sdk/' folder with only one SDK.", "examples": [ "C:\\Program Files\\dotnet\\dotnet.exe", "/usr/local/share/dotnet/dotnet", @@ -100,6 +100,16 @@ "/usr/lib/dotnet/dotnet" ] }, + "dotnetAcquisitionExtension.dotnetSDKPath": { + "type": "string", + "markdownDescription": "The path to a dotnet executable your project will use.\nRestart VS Code to apply changes.\n\n⚠️ By default, `dotnet.exe` picks the latest applicable SDK in the `/sdk/` folder next to itself. To use a specific SDK, make sure only that version is in the `/sdk/` folder. The SDK also includes a runtime.", + "description": "The path to a dotnet executable your project will use.\nRestart VS Code to apply changes.\n\n⚠️ By default, `dotnet.exe` picks the latest applicable SDK in the `/sdk/` folder next to itself. To use a specific SDK, make sure only that version is in the `/sdk/` folder. The SDK also includes a runtime.", + "examples": [ + "C:\\Program Files\\dotnet\\dotnet.exe", + "/usr/local/share/dotnet/dotnet", + "/usr/lib/dotnet/dotnet" + ] + }, "dotnetAcquisitionExtension.proxyUrl": { "type": "string", "description": "URL to a proxy if you use one, such as: https://proxy:port" diff --git a/vscode-dotnet-runtime-extension/src/extension.ts b/vscode-dotnet-runtime-extension/src/extension.ts index 0b0c7b4cc5..89a96e748f 100644 --- a/vscode-dotnet-runtime-extension/src/extension.ts +++ b/vscode-dotnet-runtime-extension/src/extension.ts @@ -97,6 +97,7 @@ namespace configKeys export const enableTelemetry = 'enableTelemetry'; export const existingPath = 'existingDotnetPath'; export const existingSharedPath = 'sharedExistingDotnetPath' + export const dotnetSDKPath = 'dotnetSDKPath'; export const proxyUrl = 'proxyUrl'; export const allowInvalidPaths = 'allowInvalidPaths'; export const cacheTimeToLiveMultiplier = 'cacheTimeToLiveMultiplier'; @@ -571,18 +572,37 @@ export function activate(vsCodeContext: vscode.ExtensionContext, extensionContex globalEventStream.post(new DotnetFindPathLookupSetting(`Looking up vscode setting.`)); const workerContext = getAcquisitionWorkerContext(commandContext.acquireContext.mode, commandContext.acquireContext); - const existingPath = await resolveExistingPathIfExists(existingPathConfigWorker, commandContext.acquireContext, workerContext, utilContext, commandContext.versionSpecRequirement); + let existingPathForLog = ''; + const validator = new DotnetConditionValidator(workerContext, utilContext); // The setting is not intended to be used as the SDK, only the runtime for extensions to run on. Ex: PowerShell policy doesn't allow us to install the runtime, let users set the path manually. - if (existingPath && commandContext.acquireContext.mode !== 'sdk') + if (commandContext.acquireContext.mode !== 'sdk') { - // We don't need to validate the existing path as it gets validated in the lookup logic already. - globalEventStream.post(new DotnetFindPathSettingFound(`Found vscode setting.`)); - loggingObserver.dispose(); - return existingPath; + const existingHostRuntimeInternalExtensionPath = await resolveExistingPathIfExists(existingPathConfigWorker, commandContext.acquireContext, workerContext, utilContext, commandContext.versionSpecRequirement); + existingPathForLog = existingHostRuntimeInternalExtensionPath?.dotnetPath ?? ''; + if (existingHostRuntimeInternalExtensionPath) + { + // We don't need to validate the existing path as it gets validated in the lookup logic already. + globalEventStream.post(new DotnetFindPathSettingFound(`Found vscode setting.`)); + loggingObserver.dispose(); + return existingHostRuntimeInternalExtensionPath; + } + } + else if (commandContext.acquireContext.mode === 'sdk') + { + const existingHostSDKUserPath = extensionConfiguration.get(configKeys.dotnetSDKPath); + existingPathForLog = existingHostSDKUserPath ?? ''; + if (existingHostSDKUserPath) + { + const validatedSDKPath = await getPathIfValid(existingHostSDKUserPath, validator, commandContext); + if (validatedSDKPath) + { + loggingObserver.dispose(); + return { dotnetPath: validatedSDKPath }; + } + } } - const validator = new DotnetConditionValidator(workerContext, utilContext); const finder = new DotnetPathFinder(workerContext, utilContext); const dotnetOnShellSpawn = (await finder.findDotnetFastFromListOnly(requestedArchitecture))?.[0] ?? ''; @@ -639,7 +659,7 @@ export function activate(vsCodeContext: vscode.ExtensionContext, extensionContex loggingObserver.dispose(); globalEventStream.post(new DotnetFindPathNoPathMetCondition(`Could not find a single host path that met the conditions. -existingPath : ${existingPath?.dotnetPath} +codePathSetting : ${existingPathForLog} onPath : ${JSON.stringify(dotnetsOnPATH)} onRealPath : ${JSON.stringify(dotnetsOnRealPATH)} onRoot : ${dotnetOnROOT} diff --git a/vscode-dotnet-runtime-extension/src/test/functional/DotnetCoreAcquisitionExtension.test.ts b/vscode-dotnet-runtime-extension/src/test/functional/DotnetCoreAcquisitionExtension.test.ts index 1c5530a26b..99b8ffde58 100644 --- a/vscode-dotnet-runtime-extension/src/test/functional/DotnetCoreAcquisitionExtension.test.ts +++ b/vscode-dotnet-runtime-extension/src/test/functional/DotnetCoreAcquisitionExtension.test.ts @@ -63,6 +63,7 @@ suite('DotnetCoreAcquisitionExtension End to End', function () const existingPathVersionToFake = '5.0.1~x64' const pathWithIncorrectVersionForTest = path.join(__dirname, `/.dotnet/${existingPathVersionToFake}/${getDotnetExecutable()}`); + const pathForSDKSetting = path.join(__dirname, `/.dotnet/${getDotnetExecutable()}`); const mockExistingPathsWithGlobalConfig: IExistingPaths = { individualizedExtensionPaths: [{ extensionId: 'alternative.extension', path: pathWithIncorrectVersionForTest }], @@ -106,7 +107,7 @@ suite('DotnetCoreAcquisitionExtension End to End', function () extension.ReEnableActivationForManualActivation(); extension.activate(extensionContext, { telemetryReporter: new MockTelemetryReporter(), - extensionConfiguration: new MockExtensionConfiguration(mockExistingPathsWithGlobalConfig.individualizedExtensionPaths!, true, mockExistingPathsWithGlobalConfig.sharedExistingPath!), + extensionConfiguration: new MockExtensionConfiguration(mockExistingPathsWithGlobalConfig.individualizedExtensionPaths!, true, mockExistingPathsWithGlobalConfig.sharedExistingPath!, false, pathForSDKSetting), displayWorker: mockDisplayWorker, }); }); @@ -660,9 +661,9 @@ Paths: 'acquire returned: ${resultForAcquiringPathSettingRuntime.dotnetPath} whi const findPath = await vscode.commands.executeCommand('dotnet.findPath', { acquireContext: Object.assign({}, context, { mode: 'runtime' }), versionSpecRequirement: 'equal' }); assert.equal(findPath!.dotnetPath, pathWithIncorrectVersionForTest, 'findPath uses vscode setting for runtime'); // this is set for the alternative.extension in the settings - // check that find path does not use the setting even if its set because it should not use the wrong thing that does not meet the condition + // check that find path does uses the SDK setting for SDK lookup, and not the runtime setting const findSDKPath = await vscode.commands.executeCommand('dotnet.findPath', { acquireContext: Object.assign({}, context, { mode: 'sdk' }), versionSpecRequirement: 'equal' }); - assert.equal(findSDKPath?.dotnetPath ?? undefined, undefined, 'findPath does not find path setting for the SDK'); + assert.equal(findSDKPath?.dotnetPath ?? undefined, pathForSDKSetting, 'findPath uses find path setting for the SDK'); }).timeout(standardTimeoutTime * 3); test('List Sdks & Runtimes', async () => diff --git a/vscode-dotnet-runtime-library/src/test/mocks/MockObjects.ts b/vscode-dotnet-runtime-library/src/test/mocks/MockObjects.ts index 6ce72577df..1a02477fb5 100644 --- a/vscode-dotnet-runtime-library/src/test/mocks/MockObjects.ts +++ b/vscode-dotnet-runtime-library/src/test/mocks/MockObjects.ts @@ -693,7 +693,7 @@ export class MockLoggingObserver implements ILoggingObserver export class MockExtensionConfiguration implements IExtensionConfiguration { constructor(private readonly existingPaths: ILocalExistingPath[], private readonly enableTelemetry: boolean, private readonly existingSharedPath: string, - public allowInvalidPaths = false + public allowInvalidPaths = false, public sdkPath: string = '' ) {} public update(section: string, value: T): Thenable @@ -724,6 +724,10 @@ export class MockExtensionConfiguration implements IExtensionConfiguration { return true as unknown as T; } + else if (name === 'dotnetSDKPath') + { + return this.sdkPath as unknown as T; + } else { return undefined;