Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1f79c98

Browse files
refackjoaocgreis
authored andcommittedFeb 27, 2017
win: find and setup for VS2017
Reviewed-By: João Reis <[email protected]>
1 parent ec5fc36 commit 1f79c98

File tree

4 files changed

+336
-4
lines changed

4 files changed

+336
-4
lines changed
 

Diff for: ‎lib/Find-VS2017.cs

+262
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
// powershell -ExecutionPolicy Unrestricted -Version "2.0" -Command "&{Add-Type -Path Find-VS2017.cs; [VisualStudioConfiguration.Main]::Query()}"
2+
using System;
3+
using System.Text;
4+
using System.Runtime.InteropServices;
5+
6+
namespace VisualStudioConfiguration
7+
{
8+
[Flags]
9+
public enum InstanceState : uint
10+
{
11+
None = 0,
12+
Local = 1,
13+
Registered = 2,
14+
NoRebootRequired = 4,
15+
NoErrors = 8,
16+
Complete = 4294967295,
17+
}
18+
19+
[Guid("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848")]
20+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
21+
[ComImport]
22+
public interface IEnumSetupInstances
23+
{
24+
25+
void Next([MarshalAs(UnmanagedType.U4), In] int celt,
26+
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Interface), Out] ISetupInstance[] rgelt,
27+
[MarshalAs(UnmanagedType.U4)] out int pceltFetched);
28+
29+
void Skip([MarshalAs(UnmanagedType.U4), In] int celt);
30+
31+
void Reset();
32+
33+
[return: MarshalAs(UnmanagedType.Interface)]
34+
IEnumSetupInstances Clone();
35+
}
36+
37+
[Guid("42843719-DB4C-46C2-8E7C-64F1816EFD5B")]
38+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
39+
[ComImport]
40+
public interface ISetupConfiguration
41+
{
42+
}
43+
44+
[Guid("26AAB78C-4A60-49D6-AF3B-3C35BC93365D")]
45+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
46+
[ComImport]
47+
public interface ISetupConfiguration2 : ISetupConfiguration
48+
{
49+
50+
[return: MarshalAs(UnmanagedType.Interface)]
51+
IEnumSetupInstances EnumInstances();
52+
53+
[return: MarshalAs(UnmanagedType.Interface)]
54+
ISetupInstance GetInstanceForCurrentProcess();
55+
56+
[return: MarshalAs(UnmanagedType.Interface)]
57+
ISetupInstance GetInstanceForPath([MarshalAs(UnmanagedType.LPWStr), In] string path);
58+
59+
[return: MarshalAs(UnmanagedType.Interface)]
60+
IEnumSetupInstances EnumAllInstances();
61+
}
62+
63+
[Guid("B41463C3-8866-43B5-BC33-2B0676F7F42E")]
64+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
65+
[ComImport]
66+
public interface ISetupInstance
67+
{
68+
}
69+
70+
[Guid("89143C9A-05AF-49B0-B717-72E218A2185C")]
71+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
72+
[ComImport]
73+
public interface ISetupInstance2 : ISetupInstance
74+
{
75+
[return: MarshalAs(UnmanagedType.BStr)]
76+
string GetInstanceId();
77+
78+
[return: MarshalAs(UnmanagedType.Struct)]
79+
System.Runtime.InteropServices.ComTypes.FILETIME GetInstallDate();
80+
81+
[return: MarshalAs(UnmanagedType.BStr)]
82+
string GetInstallationName();
83+
84+
[return: MarshalAs(UnmanagedType.BStr)]
85+
string GetInstallationPath();
86+
87+
[return: MarshalAs(UnmanagedType.BStr)]
88+
string GetInstallationVersion();
89+
90+
[return: MarshalAs(UnmanagedType.BStr)]
91+
string GetDisplayName([MarshalAs(UnmanagedType.U4), In] int lcid);
92+
93+
[return: MarshalAs(UnmanagedType.BStr)]
94+
string GetDescription([MarshalAs(UnmanagedType.U4), In] int lcid);
95+
96+
[return: MarshalAs(UnmanagedType.BStr)]
97+
string ResolvePath([MarshalAs(UnmanagedType.LPWStr), In] string pwszRelativePath);
98+
99+
[return: MarshalAs(UnmanagedType.U4)]
100+
InstanceState GetState();
101+
102+
[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)]
103+
ISetupPackageReference[] GetPackages();
104+
105+
ISetupPackageReference GetProduct();
106+
107+
[return: MarshalAs(UnmanagedType.BStr)]
108+
string GetProductPath();
109+
110+
[return: MarshalAs(UnmanagedType.VariantBool)]
111+
bool IsLaunchable();
112+
113+
[return: MarshalAs(UnmanagedType.VariantBool)]
114+
bool IsComplete();
115+
116+
ISetupPropertyStore GetProperties();
117+
118+
[return: MarshalAs(UnmanagedType.BStr)]
119+
string GetEnginePath();
120+
}
121+
122+
[Guid("DA8D8A16-B2B6-4487-A2F1-594CCCCD6BF5")]
123+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
124+
[ComImport]
125+
public interface ISetupPackageReference
126+
{
127+
128+
[return: MarshalAs(UnmanagedType.BStr)]
129+
string GetId();
130+
131+
[return: MarshalAs(UnmanagedType.BStr)]
132+
string GetVersion();
133+
134+
[return: MarshalAs(UnmanagedType.BStr)]
135+
string GetChip();
136+
137+
[return: MarshalAs(UnmanagedType.BStr)]
138+
string GetLanguage();
139+
140+
[return: MarshalAs(UnmanagedType.BStr)]
141+
string GetBranch();
142+
143+
[return: MarshalAs(UnmanagedType.BStr)]
144+
string GetType();
145+
146+
[return: MarshalAs(UnmanagedType.BStr)]
147+
string GetUniqueId();
148+
149+
[return: MarshalAs(UnmanagedType.VariantBool)]
150+
bool GetIsExtension();
151+
}
152+
153+
[Guid("c601c175-a3be-44bc-91f6-4568d230fc83")]
154+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
155+
[ComImport]
156+
public interface ISetupPropertyStore
157+
{
158+
159+
[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
160+
string[] GetNames();
161+
162+
object GetValue([MarshalAs(UnmanagedType.LPWStr), In] string pwszName);
163+
}
164+
165+
[Guid("42843719-DB4C-46C2-8E7C-64F1816EFD5B")]
166+
[CoClass(typeof(SetupConfigurationClass))]
167+
[ComImport]
168+
public interface SetupConfiguration : ISetupConfiguration2, ISetupConfiguration
169+
{
170+
}
171+
172+
[Guid("177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D")]
173+
[ClassInterface(ClassInterfaceType.None)]
174+
[ComImport]
175+
public class SetupConfigurationClass
176+
{
177+
}
178+
179+
public static class Main
180+
{
181+
public static void Query()
182+
{
183+
ISetupConfiguration query = new SetupConfiguration();
184+
ISetupConfiguration2 query2 = (ISetupConfiguration2)query;
185+
IEnumSetupInstances e = query2.EnumAllInstances();
186+
187+
int pceltFetched;
188+
ISetupInstance2[] rgelt = new ISetupInstance2[1];
189+
StringBuilder log = new StringBuilder();
190+
while (true)
191+
{
192+
e.Next(1, rgelt, out pceltFetched);
193+
if (pceltFetched <= 0)
194+
{
195+
Console.WriteLine(String.Format("{{\"log\":\"{0}\"}}", log.ToString()));
196+
return;
197+
}
198+
if (CheckInstance(rgelt[0], ref log))
199+
return;
200+
}
201+
}
202+
203+
private static bool CheckInstance(ISetupInstance2 setupInstance2, ref StringBuilder log)
204+
{
205+
// Visual Studio Community 2017 component directory:
206+
// https://www.visualstudio.com/en-us/productinfo/vs2017-install-product-Community.workloads
207+
208+
string path = setupInstance2.GetInstallationPath().Replace("\\", "\\\\");
209+
log.Append(String.Format("Found installation at: {0}\\n", path));
210+
211+
bool hasMSBuild = false;
212+
bool hasVCTools = false;
213+
uint Win10SDKVer = 0;
214+
bool hasWin8SDK = false;
215+
216+
foreach (ISetupPackageReference package in setupInstance2.GetPackages())
217+
{
218+
const string Win10SDKPrefix = "Microsoft.VisualStudio.Component.Windows10SDK.";
219+
220+
string id = package.GetId();
221+
if (id == "Microsoft.Component.MSBuild")
222+
hasMSBuild = true;
223+
else if (id == "Microsoft.VisualStudio.Component.VC.Tools.x86.x64")
224+
hasVCTools = true;
225+
else if (id.StartsWith(Win10SDKPrefix))
226+
Win10SDKVer = Math.Max(Win10SDKVer, UInt32.Parse(id.Substring(Win10SDKPrefix.Length)));
227+
else if (id == "Microsoft.VisualStudio.Component.Windows81SDK")
228+
hasWin8SDK = true;
229+
else
230+
continue;
231+
232+
log.Append(String.Format(" - Found {0}\\n", id));
233+
}
234+
235+
if (!hasMSBuild)
236+
log.Append(" - Missing MSBuild (Microsoft.Component.MSBuild)\\n");
237+
if (!hasVCTools)
238+
log.Append(" - Missing VC++ 2017 v141 toolset (x86,x64) (Microsoft.VisualStudio.Component.VC.Tools.x86.x64)\\n");
239+
if ((Win10SDKVer == 0) && (!hasWin8SDK))
240+
log.Append(" - Missing a Windows SDK (Microsoft.VisualStudio.Component.Windows10SDK.* or Microsoft.VisualStudio.Component.Windows81SDK)\\n");
241+
242+
if (hasMSBuild && hasVCTools)
243+
{
244+
if (Win10SDKVer > 0)
245+
{
246+
log.Append(" - Using this installation with Windows 10 SDK"/*\\n*/);
247+
Console.WriteLine(String.Format("{{\"log\":\"{0}\",\"path\":\"{1}\",\"sdk\":\"10.0.{2}.0\"}}", log.ToString(), path, Win10SDKVer));
248+
return true;
249+
}
250+
else if (hasWin8SDK)
251+
{
252+
log.Append(" - Using this installation with Windows 8.1 SDK"/*\\n*/);
253+
Console.WriteLine(String.Format("{{\"log\":\"{0}\",\"path\":\"{1}\",\"sdk\":\"8.1\"}}", log.ToString(), path));
254+
return true;
255+
}
256+
}
257+
258+
log.Append(" - Some required components are missing, not using this installation\\n");
259+
return false;
260+
}
261+
}
262+
}

Diff for: ‎lib/build.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ var fs = require('graceful-fs')
1414
, mkdirp = require('mkdirp')
1515
, exec = require('child_process').exec
1616
, processRelease = require('./process-release')
17-
, win = process.platform == 'win32'
17+
, win = process.platform === 'win32'
18+
if (win)
19+
var findVS2017 = require('./find-vs2017')
1820

1921
exports.usage = 'Invokes `' + (win ? 'msbuild' : 'make') + '` and builds the module'
2022

@@ -124,6 +126,13 @@ function build (gyp, argv, callback) {
124126
*/
125127

126128
function findMsbuild () {
129+
if (config.variables.msbuild_path) {
130+
command = config.variables.msbuild_path
131+
log.verbose('using MSBuild:', command)
132+
copyNodeLib()
133+
return
134+
}
135+
127136
log.verbose('could not find "msbuild.exe" in PATH - finding location in registry')
128137
var notfoundErr = 'Can\'t find "msbuild.exe". Do you have Microsoft Visual Studio C++ 2008+ installed?'
129138
var cmd = 'reg query "HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions" /s'

Diff for: ‎lib/configure.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ var fs = require('graceful-fs')
1919
, cp = require('child_process')
2020
, extend = require('util')._extend
2121
, processRelease = require('./process-release')
22-
, win = process.platform == 'win32'
22+
, win = process.platform === 'win32'
2323
, findNodeDirectory = require('./find-node-directory')
2424
, msgFormat = require('util').format
25+
if (win)
26+
var findVS2017 = require('./find-vs2017')
2527

2628
exports.usage = 'Generates ' + (win ? 'MSVC project files' : 'a Makefile') + ' for the current module'
2729

@@ -137,6 +139,18 @@ function configure (gyp, argv, callback) {
137139
// disable -T "thin" static archives by default
138140
variables.standalone_static_library = gyp.opts.thin ? 0 : 1
139141

142+
if (win && !(gyp.opts.msvs_version && gyp.opts.msvs_version !== '2017')) {
143+
const vsSetup = findVS2017()
144+
if (vsSetup) {
145+
gyp.opts.msvs_version = '2015'
146+
process.env['GYP_MSVS_VERSION'] = 2015
147+
process.env['GYP_MSVS_OVERRIDE_PATH'] = vsSetup.path
148+
defaults['msbuild_toolset'] = 'v141'
149+
defaults['msvs_windows_target_platform_version'] = vsSetup.sdk
150+
variables['msbuild_path'] = path.join(vsSetup.path, 'MSBuild', '15.0', 'Bin', 'MSBuild.exe')
151+
}
152+
}
153+
140154
// loop through the rest of the opts and add the unknown ones as variables.
141155
// this allows for module-specific configure flags like:
142156
//
@@ -317,9 +331,9 @@ function configure (gyp, argv, callback) {
317331
}
318332

319333
/**
320-
* Returns the first file or directory from an array of candidates that is
334+
* Returns the first file or directory from an array of candidates that is
321335
* readable by the current user, or undefined if none of the candidates are
322-
* readable.
336+
* readable.
323337
*/
324338
function findAccessibleSync (logprefix, dir, candidates) {
325339
for (var next = 0; next < candidates.length; next++) {

Diff for: ‎lib/find-vs2017.js

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
const log = require('npmlog')
2+
, execSync = require('child_process').execSync
3+
, path = require('path')
4+
5+
var hasCache = false
6+
, cache = null
7+
8+
function findVS2017() {
9+
if (hasCache)
10+
return cache
11+
12+
hasCache = true
13+
14+
const ps = 'PowerShell -ExecutionPolicy Unrestricted -Command '
15+
const csFile = path.join(__dirname, 'Find-VS2017.cs')
16+
const psQuery = ps + '"&{Add-Type -Path \'' + csFile + '\'; [VisualStudioConfiguration.Main]::Query()}" 2>&1'
17+
18+
var vsSetup
19+
try {
20+
const vsSetupRaw = execSync(psQuery, { encoding: 'utf8' })
21+
log.silly('find vs2017', 'vsSetupRaw:', vsSetupRaw)
22+
vsSetup = JSON.parse(vsSetupRaw)
23+
log.silly('find vs2017', 'vsSetup:', vsSetup)
24+
} catch (e) {
25+
log.silly('find vs2017', e)
26+
log.verbose('find vs2017', 'could not use PowerShell to find VS2017')
27+
return cache
28+
}
29+
30+
if (vsSetup && vsSetup.log)
31+
log.verbose('find vs2017', vsSetup.log.trimRight())
32+
33+
if (!vsSetup || !vsSetup.path || !vsSetup.sdk) {
34+
log.verbose('find vs2017', 'no usable installation found')
35+
return cache
36+
}
37+
38+
cache = {
39+
"path": vsSetup.path,
40+
"sdk": vsSetup.sdk
41+
}
42+
43+
log.verbose('find vs2017', 'using installation:', cache.path)
44+
return cache
45+
}
46+
47+
module.exports = findVS2017

0 commit comments

Comments
 (0)
Please sign in to comment.