Skip to content

Commit ae141e1

Browse files
refackjoaocgreis
authored andcommitted
win: find and setup for VS2017
PR-URL: #1130 Reviewed-By: João Reis <reis@janeasystems.com> Reviewed-By: Bartosz Sosnowski <bartosz@janeasystems.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Rod Vagg <rod@vagg.org>
1 parent ec5fc36 commit ae141e1

File tree

4 files changed

+353
-6
lines changed

4 files changed

+353
-6
lines changed

lib/Find-VS2017.cs

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

lib/build.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ 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'
1818

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

@@ -124,6 +124,13 @@ function build (gyp, argv, callback) {
124124
*/
125125

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

lib/configure.js

Lines changed: 31 additions & 5 deletions
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

@@ -86,11 +88,22 @@ function configure (gyp, argv, callback) {
8688
mkdirp(buildDir, function (err, isNew) {
8789
if (err) return callback(err)
8890
log.verbose('build dir', '"build" dir needed to be created?', isNew)
89-
createConfigFile()
91+
if (win && (!gyp.opts.msvs_version || gyp.opts.msvs_version === '2017')) {
92+
findVS2017(function (err, vsSetup) {
93+
if (err) {
94+
log.verbose('Not using VS2017:', err.message)
95+
createConfigFile()
96+
} else {
97+
createConfigFile(null, vsSetup)
98+
}
99+
})
100+
} else {
101+
createConfigFile()
102+
}
90103
})
91104
}
92105

93-
function createConfigFile (err) {
106+
function createConfigFile (err, vsSetup) {
94107
if (err) return callback(err)
95108

96109
var configFilename = 'config.gypi'
@@ -137,6 +150,19 @@ function configure (gyp, argv, callback) {
137150
// disable -T "thin" static archives by default
138151
variables.standalone_static_library = gyp.opts.thin ? 0 : 1
139152

153+
if (vsSetup) {
154+
// GYP doesn't (yet) have support for VS2017, so we force it to VS2015
155+
// to avoid pulling a floating patch that has not landed upstream.
156+
// Ref: https://chromium-review.googlesource.com/#/c/433540/
157+
gyp.opts.msvs_version = '2015'
158+
process.env['GYP_MSVS_VERSION'] = 2015
159+
process.env['GYP_MSVS_OVERRIDE_PATH'] = vsSetup.path
160+
defaults['msbuild_toolset'] = 'v141'
161+
defaults['msvs_windows_target_platform_version'] = vsSetup.sdk
162+
variables['msbuild_path'] = path.join(vsSetup.path, 'MSBuild', '15.0',
163+
'Bin', 'MSBuild.exe')
164+
}
165+
140166
// loop through the rest of the opts and add the unknown ones as variables.
141167
// this allows for module-specific configure flags like:
142168
//
@@ -317,9 +343,9 @@ function configure (gyp, argv, callback) {
317343
}
318344

319345
/**
320-
* Returns the first file or directory from an array of candidates that is
346+
* Returns the first file or directory from an array of candidates that is
321347
* readable by the current user, or undefined if none of the candidates are
322-
* readable.
348+
* readable.
323349
*/
324350
function findAccessibleSync (logprefix, dir, candidates) {
325351
for (var next = 0; next < candidates.length; next++) {

0 commit comments

Comments
 (0)