From d54e5504206712ceb76b7a657790e6216b6b6ebe Mon Sep 17 00:00:00 2001 From: Jaroslav Gorjatsev <375519+jarig@users.noreply.github.com> Date: Sat, 23 Dec 2023 22:31:30 +0200 Subject: [PATCH] feat: detect visual studio installation using VSSetup module and Get-VSSetupInstance method, it works even in systems with Constrained language mode of the powershell --- README.md | 3 + lib/find-visualstudio.js | 85 +++- .../VSSetup_VS_2022_multiple_install.txt | 369 ++++++++++++++++++ test/fixtures/VSSetup_VS_2022_workload.txt | 136 +++++++ .../VSSetup_VS_2022_workload_missing_sdk.txt | 152 ++++++++ test/test-find-visualstudio.js | 176 ++++++++- 6 files changed, 894 insertions(+), 27 deletions(-) create mode 100644 test/fixtures/VSSetup_VS_2022_multiple_install.txt create mode 100644 test/fixtures/VSSetup_VS_2022_workload.txt create mode 100644 test/fixtures/VSSetup_VS_2022_workload_missing_sdk.txt diff --git a/README.md b/README.md index f46ee06308..a996a07a64 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,9 @@ Install tools and configuration manually: To use the native ARM64 C++ compiler on Windows on ARM, ensure that you have Visual Studio 2022 [17.4 or later](https://devblogs.microsoft.com/visualstudio/arm64-visual-studio-is-officially-here/) installed. +It's advised to install following Powershell module: [VSSetup](https://github.com/microsoft/vssetup.powershell) using `Install-Module VSSetup -Scope CurrentUser`. +This will make Visual Studio detection logic to use more flexible and accessible method, avoiding Powershell's `ConstrainedLanguage` mode. + ### Configuring Python Dependency `node-gyp` requires that you have installed a [supported version of Python](https://devguide.python.org/versions/). diff --git a/lib/find-visualstudio.js b/lib/find-visualstudio.js index b57770259a..c8fe347a11 100644 --- a/lib/find-visualstudio.js +++ b/lib/find-visualstudio.js @@ -2,7 +2,7 @@ const log = require('./log') const { existsSync } = require('fs') -const { win32: path } = require('path') +const { path } = require('path') const { regSearchKeys, execFile } = require('./util') class VisualStudioFinder { @@ -54,6 +54,7 @@ class VisualStudioFinder { } const checks = [ + () => this.findVisualStudio2017OrNewerUsingSetupModule(this.envVcInstallDir), () => this.findVisualStudio2017OrNewer(), () => this.findVisualStudio2015(), () => this.findVisualStudio2013() @@ -113,6 +114,48 @@ class VisualStudioFinder { throw new Error('Could not find any Visual Studio installation to use') } + async findVisualStudio2017OrNewerUsingSetupModule (vcInstallDir) { + const ps = path.join(process.env.SystemRoot, 'System32', + 'WindowsPowerShell', 'v1.0', 'powershell.exe') + + const checkModuleArgs = [ + '-NoProfile', + '-Command', + '&{@(Get-Module -ListAvailable -Name VSSetup).Version.ToString()}' + ] + const [cErr] = await this.execFile(ps, checkModuleArgs, { encoding: 'utf8' }) + if (cErr) { + this.addLog('VSSetup module doesn\'t seem to exist. You can install it via: "Install-Module VSSetup -Scope CurrentUser"') + return null + } + const filterArg = vcInstallDir !== undefined ? `| where {$_.InstallationPath -eq '${vcInstallDir}' }` : '' + const psArgs = [ + '-NoProfile', + '-Command', + `&{Get-VSSetupInstance ${filterArg} | ConvertTo-Json -Depth 3}` + ] + + this.log.silly('Running', ps, psArgs) + const [err, stdout, stderr] = await this.execFile(ps, psArgs, { encoding: 'utf8' }) + let parsedData = this.parseData(err, stdout, stderr) + if (parsedData === null) { + return null + } + if (!Array.isArray(parsedData)) { + // if there are only 1 result, then Powershell will output non-array + parsedData = [parsedData] + } + // normalize output + parsedData = parsedData.map((info) => { + info.path = info.InstallationPath + info.version = `${info.InstallationVersion.Major}.${info.InstallationVersion.Minor}.${info.InstallationVersion.Build}.${info.InstallationVersion.Revision}` + info.packages = info.Packages.map((p) => p.Id) + return info + }) + // pass for further processing + return this.processData(parsedData) + } + // Invoke the PowerShell script to get information about Visual Studio 2017 // or newer installations async findVisualStudio2017OrNewer () { @@ -128,24 +171,35 @@ class VisualStudioFinder { ] this.log.silly('Running', ps, psArgs) - const [err, stdout, stderr] = await execFile(ps, psArgs, { encoding: 'utf8' }) - return this.parseData(err, stdout, stderr) + const [err, stdout, stderr] = await this.execFile(ps, psArgs, { encoding: 'utf8' }) + const parsedData = this.parseData(err, stdout, stderr, { checkIsArray: true }) + if (parsedData === null) { + return null + } + return this.processData(parsedData) } - // Parse the output of the PowerShell script and look for an installation - // of Visual Studio 2017 or newer to use - parseData (err, stdout, stderr) { + // Parse the output of the PowerShell script, make sanity checks + parseData (err, stdout, stderr, sanityCheckOptions) { + const defaultOptions = { + checkIsArray: false + } + + // Merging provided options with the default options + const sanityOptions = { ...defaultOptions, ...sanityCheckOptions } + this.log.silly('PS stderr = %j', stderr) - const failPowershell = () => { + const failPowershell = (failureDetails) => { this.addLog( - 'could not use PowerShell to find Visual Studio 2017 or newer, try re-running with \'--loglevel silly\' for more details') + `could not use PowerShell to find Visual Studio 2017 or newer, try re-running with '--loglevel silly' for more details. \n + Failure details: ${failureDetails}`) return null } if (err) { this.log.silly('PS err = %j', err && (err.stack || err)) - return failPowershell() + return failPowershell(`${err}`.substring(0, 15)) } let vsInfo @@ -157,11 +211,16 @@ class VisualStudioFinder { return failPowershell() } - if (!Array.isArray(vsInfo)) { + if (sanityOptions.checkIsArray && !Array.isArray(vsInfo)) { this.log.silly('PS stdout = %j', stdout) - return failPowershell() + return failPowershell('Expected array as output of the PS script') } + return vsInfo + } + // Process parsed data containing information about VS installations + // Look for the required parts, extract and output them back + processData (vsInfo) { vsInfo = vsInfo.map((info) => { this.log.silly(`processing installation: "${info.path}"`) info.path = path.resolve(info.path) @@ -438,6 +497,10 @@ class VisualStudioFinder { return true } + + async execFile (exec, args) { + return await execFile(exec, args, { encoding: 'utf8' }) + } } module.exports = VisualStudioFinder diff --git a/test/fixtures/VSSetup_VS_2022_multiple_install.txt b/test/fixtures/VSSetup_VS_2022_multiple_install.txt new file mode 100644 index 0000000000..2bfd67a190 --- /dev/null +++ b/test/fixtures/VSSetup_VS_2022_multiple_install.txt @@ -0,0 +1,369 @@ +[ + { + "InstanceId": "621862c0", + "InstallationName": "VisualStudio/17.8.3+34330.188", + "InstallationPath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise", + "InstallationVersion": { + "Major": 17, + "Minor": 8, + "Build": 34330, + "Revision": 188, + "MajorRevision": 0, + "MinorRevision": 188 + }, + "InstallDate": "\/Date(1703254955000)\/", + "State": 4294967295, + "DisplayName": "Visual Studio Enterprise 2022", + "Description": "Scalable, end-to-end solution for teams of any size", + "ProductPath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Common7\\IDE\\devenv.exe", + "Product": { + "Id": "Microsoft.VisualStudio.Product.Enterprise", + "Version": { + "Major": 17, + "Minor": 8, + "Build": 34330, + "Revision": 188, + "MajorRevision": 0, + "MinorRevision": 188 + }, + "Chip": "x64", + "Branch": null, + "Type": "Product", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.Product.Enterprise,version=17.8.34330.188,chip=x64" + }, + "Packages": [ + { + "Id": "Microsoft.VisualStudio.Product.Enterprise", + "Version": "17.8.34330.188", + "Chip": "x64", + "Branch": null, + "Type": "Product", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.Product.Enterprise,version=17.8.34330.188,chip=x64" + }, + { + "Id": "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "Version": "17.8.34129.139", + "Chip": null, + "Branch": null, + "Type": "Component", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.Component.VC.Tools.x86.x64,version=17.8.34129.139" + }, + { + "Id": "Microsoft.VisualStudio.Component.Windows11SDK.22000", + "Version": "17.8.34129.139", + "Chip": null, + "Branch": null, + "Type": "Component", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.Component.Windows11SDK.22000,version=17.8.34129.139" + }, + { + "Id": "Win11SDK_10.0.22000", + "Version": "10.0.22000.4", + "Chip": null, + "Branch": null, + "Type": "Exe", + "IsExtension": false, + "UniqueId": "Win11SDK_10.0.22000,version=10.0.22000.4" + }, + { + "Id": "Microsoft.VisualStudio.Component.Windows10SDK.20348", + "Version": "17.8.34129.139", + "Chip": null, + "Branch": null, + "Type": "Component", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.Component.Windows10SDK.20348,version=17.8.34129.139" + }, + { + "Id": "Win10SDK_10.0.20348", + "Version": "10.0.20348.3", + "Chip": null, + "Branch": null, + "Type": "Exe", + "IsExtension": false, + "UniqueId": "Win10SDK_10.0.20348,version=10.0.20348.3" + } + ], + "Properties": [ + { + "Key": "CampaignId", + "Value": "" + }, + { + "Key": "SetupEngineFilePath", + "Value": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\setup.exe" + }, + { + "Key": "Nickname", + "Value": "" + }, + { + "Key": "ChannelManifestId", + "Value": "VisualStudio.17.Release/17.8.3+34330.188" + } + ], + "Errors": null, + "EnginePath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\resources\\app\\ServiceHub\\Services\\Microsoft.VisualStudio.Setup.Service", + "IsComplete": true, + "IsLaunchable": true, + "CatalogInfo": [ + { + "Key": "Id", + "Value": "VisualStudio/17.8.3+34330.188" + }, + { + "Key": "BuildBranch", + "Value": "d17.8" + }, + { + "Key": "BuildVersion", + "Value": "17.8.34330.188" + }, + { + "Key": "LocalBuild", + "Value": "build-lab" + }, + { + "Key": "ManifestName", + "Value": "VisualStudio" + }, + { + "Key": "ManifestType", + "Value": "installer" + }, + { + "Key": "ProductDisplayVersion", + "Value": "17.8.3" + }, + { + "Key": "ProductLine", + "Value": "Dev17" + }, + { + "Key": "ProductLineVersion", + "Value": "2022" + }, + { + "Key": "ProductMilestone", + "Value": "RTW" + }, + { + "Key": "ProductMilestoneIsPreRelease", + "Value": "False" + }, + { + "Key": "ProductName", + "Value": "Visual Studio" + }, + { + "Key": "ProductPatchVersion", + "Value": "3" + }, + { + "Key": "ProductPreReleaseMilestoneSuffix", + "Value": "1.0" + }, + { + "Key": "ProductSemanticVersion", + "Value": "17.8.3+34330.188" + }, + { + "Key": "RequiredEngineVersion", + "Value": "3.8.2112.61926" + } + ], + "IsPrerelease": false, + "PSPath": "Microsoft.PowerShell.Core\\FileSystem::C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise", + "UpdateDate": "2023-12-22T14:22:35.1818213Z", + "ResolvedInstallationPath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise", + "ChannelId": "VisualStudio.17.Release", + "InstalledChannelId": "VisualStudio.17.Release", + "ChannelUri": "https://aka.ms/vs/17/release/channel", + "InstalledChannelUri": "https://aka.ms/vs/17/release/channel", + "ReleaseNotes": "https://docs.microsoft.com/en-us/visualstudio/releases/2022/release-notes-v17.8#17.8.3", + "ThirdPartyNotices": "https://go.microsoft.com/fwlink/?LinkId=661288" + }, + { + "InstanceId": "dd50c6cc", + "InstallationName": "VisualStudio/17.8.3+34330.188", + "InstallationPath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\BuildTools", + "InstallationVersion": { + "Major": 17, + "Minor": 8, + "Build": 34330, + "Revision": 188, + "MajorRevision": 0, + "MinorRevision": 188 + }, + "InstallDate": "\/Date(1703262914503)\/", + "State": 4294967295, + "DisplayName": "Visual Studio Build Tools 2022", + "Description": "The Visual Studio Build Tools allows you to build native and managed MSBuild-based applications without requiring the Visual Studio IDE. There are options to install the Visual C++ compilers and libraries, MFC, ATL, and C++/CLI support.", + "ProductPath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\BuildTools\\Common7\\Tools\\LaunchDevCmd.bat", + "Product": { + "Id": "Microsoft.VisualStudio.Product.BuildTools", + "Version": { + "Major": 17, + "Minor": 8, + "Build": 34330, + "Revision": 188, + "MajorRevision": 0, + "MinorRevision": 188 + }, + "Chip": null, + "Branch": null, + "Type": "Product", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.Product.BuildTools,version=17.8.34330.188" + }, + "Packages": [ + { + "Id": "Microsoft.VisualStudio.Product.BuildTools", + "Version": "17.8.34330.188", + "Chip": null, + "Branch": null, + "Type": "Product", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.Product.BuildTools,version=17.8.34330.188" + }, + { + "Id": "Microsoft.VisualStudio.Workload.MSBuildTools", + "Version": "17.8.34129.139", + "Chip": null, + "Branch": null, + "Type": "Workload", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.Workload.MSBuildTools,version=17.8.34129.139" + }, + { + "Id": "Microsoft.VisualStudio.NuGet.BuildTools", + "Version": "17.0.60800.131", + "Chip": null, + "Branch": null, + "Type": "Vsix", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.NuGet.BuildTools,version=17.0.60800.131" + }, + { + "Id": "Microsoft.Build.UnGAC", + "Version": "17.8.3.2351904", + "Chip": "neutral", + "Branch": null, + "Type": "Exe", + "IsExtension": false, + "UniqueId": "Microsoft.Build.UnGAC,version=17.8.3.2351904,chip=neutral,language=neutral" + }, + { + "Id": "Microsoft.VisualStudio.VC.Icons", + "Version": "17.8.34129.139", + "Chip": null, + "Branch": null, + "Type": "Vsix", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.VC.Icons,version=17.8.34129.139" + } + ], + "Properties": [ + { + "Key": "CampaignId", + "Value": "09" + }, + { + "Key": "SetupEngineFilePath", + "Value": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\setup.exe" + }, + { + "Key": "Nickname", + "Value": "2" + }, + { + "Key": "ChannelManifestId", + "Value": "VisualStudio.17.Release/17.8.3+34330.188" + } + ], + "Errors": null, + "EnginePath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\resources\\app\\ServiceHub\\Services\\Microsoft.VisualStudio.Setup.Service", + "IsComplete": true, + "IsLaunchable": true, + "CatalogInfo": [ + { + "Key": "Id", + "Value": "VisualStudio/17.8.3+34330.188" + }, + { + "Key": "BuildBranch", + "Value": "d17.8" + }, + { + "Key": "BuildVersion", + "Value": "17.8.34330.188" + }, + { + "Key": "LocalBuild", + "Value": "build-lab" + }, + { + "Key": "ManifestName", + "Value": "VisualStudio" + }, + { + "Key": "ManifestType", + "Value": "installer" + }, + { + "Key": "ProductDisplayVersion", + "Value": "17.8.3" + }, + { + "Key": "ProductLine", + "Value": "Dev17" + }, + { + "Key": "ProductLineVersion", + "Value": "2022" + }, + { + "Key": "ProductMilestone", + "Value": "RTW" + }, + { + "Key": "ProductMilestoneIsPreRelease", + "Value": "False" + }, + { + "Key": "ProductName", + "Value": "Visual Studio" + }, + { + "Key": "ProductPatchVersion", + "Value": "3" + }, + { + "Key": "ProductPreReleaseMilestoneSuffix", + "Value": "1.0" + }, + { + "Key": "ProductSemanticVersion", + "Value": "17.8.3+34330.188" + }, + { + "Key": "RequiredEngineVersion", + "Value": "3.8.2112.61926" + } + ], + "IsPrerelease": false, + "PSPath": "Microsoft.PowerShell.Core\\FileSystem::C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise", + "UpdateDate": "2023-12-22T14:22:35.1818213Z", + "ResolvedInstallationPath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise", + "ChannelId": "VisualStudio.17.Release", + "InstalledChannelId": "VisualStudio.17.Release", + "ChannelUri": "https://aka.ms/vs/17/release/channel", + "InstalledChannelUri": "https://aka.ms/vs/17/release/channel", + "ReleaseNotes": "https://docs.microsoft.com/en-us/visualstudio/releases/2022/release-notes-v17.8#17.8.3", + "ThirdPartyNotices": "https://go.microsoft.com/fwlink/?LinkId=661288" + } +] diff --git a/test/fixtures/VSSetup_VS_2022_workload.txt b/test/fixtures/VSSetup_VS_2022_workload.txt new file mode 100644 index 0000000000..3e1abdc30d --- /dev/null +++ b/test/fixtures/VSSetup_VS_2022_workload.txt @@ -0,0 +1,136 @@ +{ + "InstanceId": "621862c0", + "InstallationName": "VisualStudio/17.8.3+34330.188", + "InstallationPath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise", + "InstallationVersion": { + "Major": 17, + "Minor": 8, + "Build": 34330, + "Revision": 188, + "MajorRevision": 0, + "MinorRevision": 188 + }, + "InstallDate": "\/Date(1703254955000)\/", + "State": 4294967295, + "DisplayName": "Visual Studio Enterprise 2022", + "Description": "Scalable, end-to-end solution for teams of any size", + "ProductPath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Common7\\IDE\\devenv.exe", + "Product": { + "Id": "Microsoft.VisualStudio.Product.Enterprise", + "Version": { + "Major": 17, + "Minor": 8, + "Build": 34330, + "Revision": 188, + "MajorRevision": 0, + "MinorRevision": 188 + }, + "Chip": "x64", + "Branch": null, + "Type": "Product", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.Product.Enterprise,version=17.8.34330.188,chip=x64" + }, + "Packages": [ + { + "Id": "Microsoft.VisualStudio.Product.Enterprise", + "Version": "17.8.34330.188", + "Chip": "x64", + "Branch": null, + "Type": "Product", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.Product.Enterprise,version=17.8.34330.188,chip=x64" + }, + { + "Id": "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "Version": "17.8.34129.139", + "Chip": null, + "Branch": null, + "Type": "Component", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.Component.VC.Tools.x86.x64,version=17.8.34129.139" + }, + { + "Id": "Microsoft.VisualStudio.Component.Windows11SDK.22000", + "Version": "17.8.34129.139", + "Chip": null, + "Branch": null, + "Type": "Component", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.Component.Windows11SDK.22000,version=17.8.34129.139" + }, + { + "Id": "Win11SDK_10.0.22000", + "Version": "10.0.22000.4", + "Chip": null, + "Branch": null, + "Type": "Exe", + "IsExtension": false, + "UniqueId": "Win11SDK_10.0.22000,version=10.0.22000.4" + }, + { + "Id": "Microsoft.VisualStudio.Component.Windows10SDK.20348", + "Version": "17.8.34129.139", + "Chip": null, + "Branch": null, + "Type": "Component", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.Component.Windows10SDK.20348,version=17.8.34129.139" + }, + { + "Id": "Win10SDK_10.0.20348", + "Version": "10.0.20348.3", + "Chip": null, + "Branch": null, + "Type": "Exe", + "IsExtension": false, + "UniqueId": "Win10SDK_10.0.20348,version=10.0.20348.3" + } + ], + "Properties": [ + { + "Key": "CampaignId", + "Value": "" + }, + { + "Key": "SetupEngineFilePath", + "Value": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\setup.exe" + }, + { + "Key": "Nickname", + "Value": "" + }, + { + "Key": "ChannelManifestId", + "Value": "VisualStudio.17.Release/17.8.3+34330.188" + } + ], + "Errors": null, + "EnginePath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\resources\\app\\ServiceHub\\Services\\Microsoft.VisualStudio.Setup.Service", + "IsComplete": true, + "IsLaunchable": true, + "CatalogInfo": [ + { + "Key": "Id", + "Value": "VisualStudio/17.8.3+34330.188" + }, + { + "Key": "BuildBranch", + "Value": "d17.8" + }, + { + "Key": "BuildVersion", + "Value": "17.8.34330.188" + } + ], + "IsPrerelease": false, + "PSPath": "Microsoft.PowerShell.Core\\FileSystem::C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise", + "UpdateDate": "2023-12-22T14:22:35.1818213Z", + "ResolvedInstallationPath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise", + "ChannelId": "VisualStudio.17.Release", + "InstalledChannelId": "VisualStudio.17.Release", + "ChannelUri": "https://aka.ms/vs/17/release/channel", + "InstalledChannelUri": "https://aka.ms/vs/17/release/channel", + "ReleaseNotes": "https://docs.microsoft.com/en-us/visualstudio/releases/2022/release-notes-v17.8#17.8.3", + "ThirdPartyNotices": "https://go.microsoft.com/fwlink/?LinkId=661288" +} diff --git a/test/fixtures/VSSetup_VS_2022_workload_missing_sdk.txt b/test/fixtures/VSSetup_VS_2022_workload_missing_sdk.txt new file mode 100644 index 0000000000..8edee24607 --- /dev/null +++ b/test/fixtures/VSSetup_VS_2022_workload_missing_sdk.txt @@ -0,0 +1,152 @@ +{ + "InstanceId": "621862c0", + "InstallationName": "VisualStudio/17.8.3+34330.188", + "InstallationPath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise", + "InstallationVersion": { + "Major": 17, + "Minor": 8, + "Build": 34330, + "Revision": 188, + "MajorRevision": 0, + "MinorRevision": 188 + }, + "InstallDate": "\/Date(1703254955000)\/", + "State": 4294967295, + "DisplayName": "Visual Studio Enterprise 2022", + "Description": "Scalable, end-to-end solution for teams of any size", + "ProductPath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Common7\\IDE\\devenv.exe", + "Product": { + "Id": "Microsoft.VisualStudio.Product.Enterprise", + "Version": { + "Major": 17, + "Minor": 8, + "Build": 34330, + "Revision": 188, + "MajorRevision": 0, + "MinorRevision": 188 + }, + "Chip": "x64", + "Branch": null, + "Type": "Product", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.Product.Enterprise,version=17.8.34330.188,chip=x64" + }, + "Packages": [ + { + "Id": "Microsoft.VisualStudio.Product.Enterprise", + "Version": "17.8.34330.188", + "Chip": "x64", + "Branch": null, + "Type": "Product", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.Product.Enterprise,version=17.8.34330.188,chip=x64" + }, + { + "Id": "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "Version": "17.8.34129.139", + "Chip": null, + "Branch": null, + "Type": "Component", + "IsExtension": false, + "UniqueId": "Microsoft.VisualStudio.Component.VC.Tools.x86.x64,version=17.8.34129.139" + }, + ], + "Properties": [ + { + "Key": "CampaignId", + "Value": "" + }, + { + "Key": "SetupEngineFilePath", + "Value": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\setup.exe" + }, + { + "Key": "Nickname", + "Value": "" + }, + { + "Key": "ChannelManifestId", + "Value": "VisualStudio.17.Release/17.8.3+34330.188" + } + ], + "Errors": null, + "EnginePath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\resources\\app\\ServiceHub\\Services\\Microsoft.VisualStudio.Setup.Service", + "IsComplete": true, + "IsLaunchable": true, + "CatalogInfo": [ + { + "Key": "Id", + "Value": "VisualStudio/17.8.3+34330.188" + }, + { + "Key": "BuildBranch", + "Value": "d17.8" + }, + { + "Key": "BuildVersion", + "Value": "17.8.34330.188" + }, + { + "Key": "LocalBuild", + "Value": "build-lab" + }, + { + "Key": "ManifestName", + "Value": "VisualStudio" + }, + { + "Key": "ManifestType", + "Value": "installer" + }, + { + "Key": "ProductDisplayVersion", + "Value": "17.8.3" + }, + { + "Key": "ProductLine", + "Value": "Dev17" + }, + { + "Key": "ProductLineVersion", + "Value": "2022" + }, + { + "Key": "ProductMilestone", + "Value": "RTW" + }, + { + "Key": "ProductMilestoneIsPreRelease", + "Value": "False" + }, + { + "Key": "ProductName", + "Value": "Visual Studio" + }, + { + "Key": "ProductPatchVersion", + "Value": "3" + }, + { + "Key": "ProductPreReleaseMilestoneSuffix", + "Value": "1.0" + }, + { + "Key": "ProductSemanticVersion", + "Value": "17.8.3+34330.188" + }, + { + "Key": "RequiredEngineVersion", + "Value": "3.8.2112.61926" + } + ], + "IsPrerelease": false, + "PSPath": "Microsoft.PowerShell.Core\\FileSystem::C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise", + "UpdateDate": "2023-12-22T14:22:35.1818213Z", + "ResolvedInstallationPath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise", + "ChannelId": "VisualStudio.17.Release", + "InstalledChannelId": "VisualStudio.17.Release", + "ChannelUri": "https://aka.ms/vs/17/release/channel", + "InstalledChannelUri": "https://aka.ms/vs/17/release/channel", + "ReleaseNotes": "https://docs.microsoft.com/en-us/visualstudio/releases/2022/release-notes-v17.8#17.8.3", + "ThirdPartyNotices": "https://go.microsoft.com/fwlink/?LinkId=661288" +} diff --git a/test/test-find-visualstudio.js b/test/test-find-visualstudio.js index 08e9438c45..3a21514153 100644 --- a/test/test-find-visualstudio.js +++ b/test/test-find-visualstudio.js @@ -24,7 +24,9 @@ class TestVisualStudioFinder extends VisualStudioFinder { describe('find-visualstudio', function () { it('VS2013', async function () { const finder = new TestVisualStudioFinder(semverV1, null) - + finder.findVisualStudio2017OrNewerUsingSetupModule = async () => { + return null + } finder.findVisualStudio2017OrNewer = async () => { return finder.parseData(new Error(), '', '') } @@ -69,10 +71,15 @@ describe('find-visualstudio', function () { patch: 0 }, null) + finder.findVisualStudio2017OrNewerUsingSetupModule = async () => { + return null + } + finder.findVisualStudio2017OrNewer = async () => { const file = path.join(__dirname, 'fixtures', 'VS_2017_Unusable.txt') const data = fs.readFileSync(file) - return finder.parseData(null, data, '') + const vsInfo = finder.parseData(null, data, '', { checkIsArray: true }) + return finder.processData(vsInfo) } finder.regSearchKeys = async (keys, value, addOpts) => { for (let i = 0; i < keys.length; ++i) { @@ -96,6 +103,10 @@ describe('find-visualstudio', function () { it('VS2015', async function () { const finder = new TestVisualStudioFinder(semverV1, null) + finder.findVisualStudio2017OrNewerUsingSetupModule = async () => { + return null + } + finder.findVisualStudio2017OrNewer = async () => { return finder.parseData(new Error(), '', '') } @@ -144,7 +155,7 @@ describe('find-visualstudio', function () { finder.parseData(null, '', '', (info) => { assert.ok(/use PowerShell/i.test(finder.errorLog[0]), 'expect error') assert.ok(!info, 'no data') - }) + }, { checkIsArray: true }) }) it('output from PowerShell not JSON', async function () { @@ -153,7 +164,7 @@ describe('find-visualstudio', function () { finder.parseData(null, 'AAAABBBB', '', (info) => { assert.ok(/use PowerShell/i.test(finder.errorLog[0]), 'expect error') assert.ok(!info, 'no data') - }) + }, { checkIsArray: true }) }) it('wrong JSON from PowerShell', async function () { @@ -162,7 +173,7 @@ describe('find-visualstudio', function () { finder.parseData(null, '{}', '', (info) => { assert.ok(/use PowerShell/i.test(finder.errorLog[0]), 'expect error') assert.ok(!info, 'no data') - }) + }, { checkIsArray: true }) }) it('empty JSON from PowerShell', async function () { @@ -171,7 +182,7 @@ describe('find-visualstudio', function () { finder.parseData(null, '[]', '', (info) => { assert.ok(/find .* Visual Studio/i.test(finder.errorLog[0]), 'expect error') assert.ok(!info, 'no data') - }) + }, { checkIsArray: true }) }) it('future version', async function () { @@ -189,7 +200,7 @@ describe('find-visualstudio', function () { assert.ok(/unknown version/i.test(finder.errorLog[0]), 'expect error') assert.ok(/find .* Visual Studio/i.test(finder.errorLog[1]), 'expect error') assert.ok(!info, 'no data') - }) + }, { checkIsArray: true }) }) it('single unusable VS2017', async function () { @@ -201,18 +212,22 @@ describe('find-visualstudio', function () { assert.ok(/checking/i.test(finder.errorLog[0]), 'expect error') assert.ok(/find .* Visual Studio/i.test(finder.errorLog[2]), 'expect error') assert.ok(!info, 'no data') - }) + }, { checkIsArray: true }) }) it('minimal VS2017 Build Tools', async function () { const finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') + finder.findVisualStudio2017OrNewerUsingSetupModule = async () => { + return null + } finder.findVisualStudio2017OrNewer = async () => { const file = path.join(__dirname, 'fixtures', 'VS_2017_BuildTools_minimal.txt') const data = fs.readFileSync(file) - return finder.parseData(null, data, '') + const vsInfo = finder.parseData(null, data, '', { checkIsArray: true }) + return finder.processData(vsInfo) } const { err, info } = await finder.findVisualStudio() assert.strictEqual(err, null) @@ -234,11 +249,15 @@ describe('find-visualstudio', function () { const finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') + finder.findVisualStudio2017OrNewerUsingSetupModule = async () => { + return null + } finder.findVisualStudio2017OrNewer = async () => { const file = path.join(__dirname, 'fixtures', 'VS_2017_Community_workload.txt') const data = fs.readFileSync(file) - return finder.parseData(null, data, '') + const vsInfo = finder.parseData(null, data, '', { checkIsArray: true }) + return finder.processData(vsInfo) } const { err, info } = await finder.findVisualStudio() assert.strictEqual(err, null) @@ -260,10 +279,14 @@ describe('find-visualstudio', function () { const finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') + finder.findVisualStudio2017OrNewerUsingSetupModule = async () => { + return null + } finder.findVisualStudio2017OrNewer = async () => { const file = path.join(__dirname, 'fixtures', 'VS_2017_Express.txt') const data = fs.readFileSync(file) - return finder.parseData(null, data, '') + const vsInfo = finder.parseData(null, data, '', { checkIsArray: true }) + return finder.processData(vsInfo) } const { err, info } = await finder.findVisualStudio() assert.strictEqual(err, null) @@ -285,11 +308,15 @@ describe('find-visualstudio', function () { const finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') + finder.findVisualStudio2017OrNewerUsingSetupModule = async () => { + return null + } finder.findVisualStudio2017OrNewer = async () => { const file = path.join(__dirname, 'fixtures', 'VS_2019_Preview.txt') const data = fs.readFileSync(file) - return finder.parseData(null, data, '') + const vsInfo = finder.parseData(null, data, '', { checkIsArray: true }) + return finder.processData(vsInfo) } const { err, info } = await finder.findVisualStudio() assert.strictEqual(err, null) @@ -311,11 +338,15 @@ describe('find-visualstudio', function () { const finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') + finder.findVisualStudio2017OrNewerUsingSetupModule = async () => { + return null + } finder.findVisualStudio2017OrNewer = async () => { const file = path.join(__dirname, 'fixtures', 'VS_2019_BuildTools_minimal.txt') const data = fs.readFileSync(file) - return finder.parseData(null, data, '') + const vsInfo = finder.parseData(null, data, '', { checkIsArray: true }) + return finder.processData(vsInfo) } const { err, info } = await finder.findVisualStudio() assert.strictEqual(err, null) @@ -337,11 +368,15 @@ describe('find-visualstudio', function () { const finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') + finder.findVisualStudio2017OrNewerUsingSetupModule = async () => { + return null + } finder.findVisualStudio2017OrNewer = async () => { const file = path.join(__dirname, 'fixtures', 'VS_2019_Community_workload.txt') const data = fs.readFileSync(file) - return finder.parseData(null, data, '') + const vsInfo = finder.parseData(null, data, '', { checkIsArray: true }) + return finder.processData(vsInfo) } const { err, info } = await finder.findVisualStudio() assert.strictEqual(err, null) @@ -372,11 +407,15 @@ describe('find-visualstudio', function () { finder.msBuildPathExists = (path) => { return true } + finder.findVisualStudio2017OrNewerUsingSetupModule = async () => { + return null + } finder.findVisualStudio2017OrNewer = async () => { const file = path.join(__dirname, 'fixtures', 'VS_2022_Community_workload.txt') const data = fs.readFileSync(file) - return finder.parseData(null, data, '') + const vsInfo = finder.parseData(null, data, '', { checkIsArray: true }) + return finder.processData(vsInfo) } const { err, info } = await finder.findVisualStudio() assert.strictEqual(err, null) @@ -393,7 +432,111 @@ describe('find-visualstudio', function () { }) }) + it('VSSetup: VS2022 with C++ workload', async function () { + const msBuildPath = process.arch === 'arm64' + ? 'C:\\Program Files\\Microsoft Visual Studio\\2022\\' + + 'Enterprise\\MSBuild\\Current\\Bin\\arm64\\MSBuild.exe' + : 'C:\\Program Files\\Microsoft Visual Studio\\2022\\' + + 'Enterprise\\MSBuild\\Current\\Bin\\MSBuild.exe' + + const finder = new TestVisualStudioFinder(semverV1, null) + + poison(finder, 'regSearchKeys') + const expectedVSPath = 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise' + finder.msBuildPathExists = (path) => { + if (path.startsWith(expectedVSPath) && path.endsWith('MSBuild.exe')) { + return true + } + return false + } + finder.findVisualStudio2017OrNewer = async () => { + throw new Error("findVisualStudio2017OrNewer shouldn't be called") + } + setupExecFixture(finder, 'VSSetup_VS_2022_workload.txt') + const { err, info } = await finder.findVisualStudio() + assert.strictEqual(err, null) + assert.deepStrictEqual(info, { + msBuild: msBuildPath, + path: + 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise', + sdk: '10.0.22000.0', + toolset: 'v143', + version: '17.8.34330.188', + versionMajor: 17, + versionMinor: 8, + versionYear: 2022 + }) + }) + + it('VSSetup: VS2022 with C++ workload without SDK', async function () { + const finder = new TestVisualStudioFinder(semverV1, null) + finder.msBuildPathExists = (path) => { + return true + } + finder.findVisualStudio2017OrNewer = async () => { + return null + } + finder.findOldVS = async (info) => { + return null + } + setupExecFixture(finder, 'VSSetup_VS_2022_workload_missing_sdk.txt') + const { err, info } = await finder.findVisualStudio() + assert.match(err.message, /could not find/i) + assert.strictEqual(info, null) + }) + + it('VSSetup: VS2022 with multiple installations', async function () { + const msBuildPath = process.arch === 'arm64' + ? 'C:\\Program Files\\Microsoft Visual Studio\\2022\\' + + 'Enterprise\\MSBuild\\Current\\Bin\\arm64\\MSBuild.exe' + : 'C:\\Program Files\\Microsoft Visual Studio\\2022\\' + + 'Enterprise\\MSBuild\\Current\\Bin\\MSBuild.exe' + + const finder = new TestVisualStudioFinder(semverV1, null) + poison(finder, 'regSearchKeys') + finder.msBuildPathExists = (path) => { + return true + } + + finder.msBuildPathExists = (path) => { + return true + } + finder.findVisualStudio2017OrNewer = async () => { + throw new Error("findVisualStudio2017OrNewer shouldn't be called") + } + setupExecFixture(finder, 'VSSetup_VS_2022_multiple_install.txt') + const { err, info } = await finder.findVisualStudio() + assert.strictEqual(err, null) + assert.deepStrictEqual(info, { + msBuild: msBuildPath, + path: + 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise', + sdk: '10.0.22000.0', + toolset: 'v143', + version: '17.8.34330.188', + versionMajor: 17, + versionMinor: 8, + versionYear: 2022 + }) + }) + + function setupExecFixture (finder, fixtureName) { + finder.execFile = async (exec, args) => { + if (args.length > 2 && args[2].includes('Get-Module')) { + return [null, '1.0.0', ''] + } else if (args.length > 2 && args.at(-1).includes('Get-VSSetupInstance')) { + const file = path.join(__dirname, 'fixtures', fixtureName) + return [null, fs.readFileSync(file), ''] + } + return [new Error(), '', ''] + } + } + function allVsVersions (finder) { + finder.findVisualStudio2017OrNewerUsingSetupModule = async () => { + return null + } + finder.findVisualStudio2017OrNewer = async () => { const data0 = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures', 'VS_2017_Unusable.txt'))) @@ -413,7 +556,8 @@ describe('find-visualstudio', function () { 'VS_2022_Community_workload.txt'))) const data = JSON.stringify(data0.concat(data1, data2, data3, data4, data5, data6, data7)) - return finder.parseData(null, data, '') + const parsedData = finder.parseData(null, data, '', { checkIsArray: true }) + return finder.processData(parsedData) } finder.regSearchKeys = async (keys, value, addOpts) => { for (let i = 0; i < keys.length; ++i) {