Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect visual studio installation using VSSetup module #2957

Merged
merged 1 commit into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/).
Expand Down
87 changes: 77 additions & 10 deletions lib/find-visualstudio.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class VisualStudioFinder {
}

const checks = [
() => this.findVisualStudio2017OrNewerUsingSetupModule(),
() => this.findVisualStudio2017OrNewer(),
() => this.findVisualStudio2015(),
() => this.findVisualStudio2013()
Expand Down Expand Up @@ -113,6 +114,52 @@ class VisualStudioFinder {
throw new Error('Could not find any Visual Studio installation to use')
}

async findVisualStudio2017OrNewerUsingSetupModule () {
const ps = path.join(process.env.SystemRoot, 'System32',
'WindowsPowerShell', 'v1.0', 'powershell.exe')
const vcInstallDir = this.envVcInstallDir

const checkModuleArgs = [
'-NoProfile',
'-Command',
'&{@(Get-Module -ListAvailable -Name VSSetup).Version.ToString()}'
]
this.log.silly('Running', ps, checkModuleArgs)
const [cErr] = await this.execFile(ps, checkModuleArgs)
if (cErr) {
this.addLog('VSSetup module doesn\'t seem to exist. You can install it via: "Install-Module VSSetup -Scope CurrentUser"')
this.log.silly('VSSetup error = %j', cErr && (cErr.stack || cErr))
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)
let parsedData = this.parseData(err, stdout, stderr)
if (parsedData === null) {
return null
}
this.log.silly('Parsed data', parsedData)
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 () {
Expand All @@ -128,24 +175,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)
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, 40))
StefanStojanovic marked this conversation as resolved.
Show resolved Hide resolved
}

let vsInfo
Expand All @@ -157,11 +215,16 @@ class VisualStudioFinder {
return failPowershell()
StefanStojanovic marked this conversation as resolved.
Show resolved Hide resolved
}

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)
Expand Down Expand Up @@ -438,6 +501,10 @@ class VisualStudioFinder {

return true
}

async execFile (exec, args) {
return await execFile(exec, args, { encoding: 'utf8' })
}
}

module.exports = VisualStudioFinder
235 changes: 235 additions & 0 deletions test/fixtures/VSSetup_VS_2019_Professional_workload.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
[
{
"InstanceId": "2619cf21",
"InstallationName": "VisualStudio/16.11.33+34407.143",
"InstallationPath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional",
"InstallationVersion": {
"Major": 16,
"Minor": 11,
"Build": 34407,
"Revision": 143,
"MajorRevision": 0,
"MinorRevision": 143
},
"InstallDate": "\/Date(1706804943503)\/",
"State": 4294967295,
"DisplayName": "Visual Studio Professional 2019",
"Description": "Professional IDE best suited to small teams",
"ProductPath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\Common7\\IDE\\devenv.exe",
"Product": {
"Id": "Microsoft.VisualStudio.Product.Professional",
"Version": {
"Major": 16,
"Minor": 11,
"Build": 34407,
"Revision": 143,
"MajorRevision": 0,
"MinorRevision": 143
},
"Chip": null,
"Branch": null,
"Type": "Product",
"IsExtension": false,
"UniqueId": "Microsoft.VisualStudio.Product.Professional,version=16.11.34407.143"
},
"Packages": [
{
"Id": "Microsoft.VisualStudio.Product.Professional",
"Version": "16.11.34407.143",
"Chip": null,
"Branch": null,
"Type": "Product",
"IsExtension": false,
"UniqueId": "Microsoft.VisualStudio.Product.Professional,version=16.11.34407.143"
},
{
"Id": "Microsoft.VisualStudio.Component.VC.14.29.16.10.ATL",
"Version": "16.11.31314.313",
"Chip": null,
"Branch": null,
"Type": "Component",
"IsExtension": false,
"UniqueId": "Microsoft.VisualStudio.Component.VC.14.29.16.10.ATL,version=16.11.31314.313"
},
{
"Id": "Microsoft.VisualStudio.VC.MSBuild.X64.v142",
"Version": "16.11.31503.54",
"Chip": null,
"Branch": null,
"Type": "Vsix",
"IsExtension": false,
"UniqueId": "Microsoft.VisualStudio.VC.MSBuild.X64.v142,version=16.11.31503.54"
},
{
"Id": "Microsoft.VisualStudio.VC.MSBuild.X64",
"Version": "16.11.31503.54",
"Chip": null,
"Branch": null,
"Type": "Vsix",
"IsExtension": false,
"UniqueId": "Microsoft.VisualStudio.VC.MSBuild.X64,version=16.11.31503.54"
},
{
"Id": "Microsoft.VisualStudio.VC.MSBuild.x86.v142",
"Version": "16.11.31503.54",
"Chip": null,
"Branch": null,
"Type": "Vsix",
"IsExtension": false,
"UniqueId": "Microsoft.VisualStudio.VC.MSBuild.x86.v142,version=16.11.31503.54"
},
{
"Id": "Microsoft.VisualStudio.VC.MSBuild.X86",
"Version": "16.11.31503.54",
"Chip": null,
"Branch": null,
"Type": "Vsix",
"IsExtension": false,
"UniqueId": "Microsoft.VisualStudio.VC.MSBuild.X86,version=16.11.31503.54"
},
{
"Id": "Microsoft.VisualStudio.VC.MSBuild.Base",
"Version": "16.11.31829.152",
"Chip": null,
"Branch": null,
"Type": "Vsix",
"IsExtension": false,
"UniqueId": "Microsoft.VisualStudio.VC.MSBuild.Base,version=16.11.31829.152"
},
{
"Id": "Microsoft.VisualStudio.VC.MSBuild.Base.Resources",
"Version": "16.11.31829.152",
"Chip": null,
"Branch": null,
"Type": "Vsix",
"IsExtension": false,
"UniqueId": "Microsoft.VisualStudio.VC.MSBuild.Base.Resources,version=16.11.31829.152,language=en-US"
},
{
"Id": "Microsoft.VisualStudio.Branding.Professional",
"Version": "16.11.31605.320",
"Chip": null,
"Branch": null,
"Type": "Vsix",
"IsExtension": false,
"UniqueId": "Microsoft.VisualStudio.Branding.Professional,version=16.11.31605.320,language=en-US"
},
{
"Id": "Microsoft.VisualStudio.Component.Windows10SDK.19041",
"Version": "16.10.31205.252",
"Chip": null,
"Branch": null,
"Type": "Component",
"IsExtension": false,
"UniqueId": "Microsoft.VisualStudio.Component.Windows10SDK.19041,version=16.10.31205.252"
},
{
"Id": "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
"Version": "16.11.32406.258",
"Chip": null,
"Branch": null,
"Type": "Component",
"IsExtension": false,
"UniqueId": "Microsoft.VisualStudio.Component.VC.Tools.x86.x64,version=16.11.32406.258"
}
],
"Properties": [
{
"Key": "CampaignId",
"Value": "09"
},
{
"Key": "SetupEngineFilePath",
"Value": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\setup.exe"
},
{
"Key": "Nickname",
"Value": ""
},
{
"Key": "ChannelManifestId",
"Value": "VisualStudio.16.Release/16.11.33+34407.143"
}
],
"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/16.11.33+34407.143"
},
{
"Key": "BuildBranch",
"Value": "d16.11"
},
{
"Key": "BuildVersion",
"Value": "16.11.34407.143"
},
{
"Key": "LocalBuild",
"Value": "build-lab"
},
{
"Key": "ManifestName",
"Value": "VisualStudio"
},
{
"Key": "ManifestType",
"Value": "installer"
},
{
"Key": "ProductDisplayVersion",
"Value": "16.11.33"
},
{
"Key": "ProductLine",
"Value": "Dev16"
},
{
"Key": "ProductLineVersion",
"Value": "2019"
},
{
"Key": "ProductMilestone",
"Value": "RTW"
},
{
"Key": "ProductMilestoneIsPreRelease",
"Value": "False"
},
{
"Key": "ProductName",
"Value": "Visual Studio"
},
{
"Key": "ProductPatchVersion",
"Value": "33"
},
{
"Key": "ProductPreReleaseMilestoneSuffix",
"Value": "1.0"
},
{
"Key": "ProductSemanticVersion",
"Value": "16.11.33+34407.143"
},
{
"Key": "RequiredEngineVersion",
"Value": "2.11.72.18200"
}
],
"IsPrerelease": false,
"PSPath": "Microsoft.PowerShell.Core\\FileSystem::C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise",
"UpdateDate": "2024-01-09T19:19:11.0115234Z",
"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.4",
"ThirdPartyNotices": "https://go.microsoft.com/fwlink/?LinkId=661288"
}
]
Loading