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

win,build: add Visual Studio 2017 support #11867

Closed
wants to merge 3 commits into from
Closed
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ ipch/
*.VC.opendb
.vs/
.vscode/
/Set_VS*.bat

/config.mk
/config.gypi
Expand Down
3 changes: 3 additions & 0 deletions BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ Prerequisites:
* [Visual Studio 2015 Update 3](https://www.visualstudio.com/), all editions
including the Community edition (remember to select
"Common Tools for Visual C++ 2015" feature during installation).
* [Visual Studio 2017](https://www.visualstudio.com/), all editions. The
required components are "VC++ 2017 v141 toolset", "Visual Studio C++ core
features" and one of the "Windows 10 SDK".
* Basic Unix tools required for some tests,
[Git for Windows](http://git-scm.com/download/win) includes Git Bash
and tools which can be included in the global `PATH`.
Expand Down
270 changes: 270 additions & 0 deletions tools/Find-VS2017.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
// Copyright 2017 - Refael Ackermann
// Distributed under MIT style license
// See accompanying file LICENSE at https://github.com/node4good/windows-autoconf

// Usage:
// powershell -ExecutionPolicy Unrestricted -Version "2.0" -Command "&{Add-Type -Path Find-VS2017.cs; [VisualStudioConfiguration.Main]::Query()}"
using System;
using System.Text;
using System.Runtime.InteropServices;

namespace VisualStudioConfiguration
{
[Flags]
public enum InstanceState : uint
{
None = 0,
Local = 1,
Registered = 2,
NoRebootRequired = 4,
NoErrors = 8,
Complete = 4294967295,
}

[Guid("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IEnumSetupInstances
{

void Next([MarshalAs(UnmanagedType.U4), In] int celt,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Interface), Out] ISetupInstance[] rgelt,
[MarshalAs(UnmanagedType.U4)] out int pceltFetched);

void Skip([MarshalAs(UnmanagedType.U4), In] int celt);

void Reset();

[return: MarshalAs(UnmanagedType.Interface)]
IEnumSetupInstances Clone();
}

[Guid("42843719-DB4C-46C2-8E7C-64F1816EFD5B")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface ISetupConfiguration
{
}

[Guid("26AAB78C-4A60-49D6-AF3B-3C35BC93365D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface ISetupConfiguration2 : ISetupConfiguration
{

[return: MarshalAs(UnmanagedType.Interface)]
IEnumSetupInstances EnumInstances();

[return: MarshalAs(UnmanagedType.Interface)]
ISetupInstance GetInstanceForCurrentProcess();

[return: MarshalAs(UnmanagedType.Interface)]
ISetupInstance GetInstanceForPath([MarshalAs(UnmanagedType.LPWStr), In] string path);

[return: MarshalAs(UnmanagedType.Interface)]
IEnumSetupInstances EnumAllInstances();
}

[Guid("B41463C3-8866-43B5-BC33-2B0676F7F42E")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface ISetupInstance
{
}

[Guid("89143C9A-05AF-49B0-B717-72E218A2185C")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface ISetupInstance2 : ISetupInstance
{
[return: MarshalAs(UnmanagedType.BStr)]
string GetInstanceId();

[return: MarshalAs(UnmanagedType.Struct)]
System.Runtime.InteropServices.ComTypes.FILETIME GetInstallDate();

[return: MarshalAs(UnmanagedType.BStr)]
string GetInstallationName();

[return: MarshalAs(UnmanagedType.BStr)]
string GetInstallationPath();

[return: MarshalAs(UnmanagedType.BStr)]
string GetInstallationVersion();

[return: MarshalAs(UnmanagedType.BStr)]
string GetDisplayName([MarshalAs(UnmanagedType.U4), In] int lcid);

[return: MarshalAs(UnmanagedType.BStr)]
string GetDescription([MarshalAs(UnmanagedType.U4), In] int lcid);

[return: MarshalAs(UnmanagedType.BStr)]
string ResolvePath([MarshalAs(UnmanagedType.LPWStr), In] string pwszRelativePath);

[return: MarshalAs(UnmanagedType.U4)]
InstanceState GetState();

[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)]
ISetupPackageReference[] GetPackages();

ISetupPackageReference GetProduct();

[return: MarshalAs(UnmanagedType.BStr)]
string GetProductPath();

[return: MarshalAs(UnmanagedType.VariantBool)]
bool IsLaunchable();

[return: MarshalAs(UnmanagedType.VariantBool)]
bool IsComplete();

[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)]
ISetupPropertyStore GetProperties();

[return: MarshalAs(UnmanagedType.BStr)]
string GetEnginePath();
}

[Guid("DA8D8A16-B2B6-4487-A2F1-594CCCCD6BF5")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface ISetupPackageReference
{

[return: MarshalAs(UnmanagedType.BStr)]
string GetId();

[return: MarshalAs(UnmanagedType.BStr)]
string GetVersion();

[return: MarshalAs(UnmanagedType.BStr)]
string GetChip();

[return: MarshalAs(UnmanagedType.BStr)]
string GetLanguage();

[return: MarshalAs(UnmanagedType.BStr)]
string GetBranch();

[return: MarshalAs(UnmanagedType.BStr)]
string GetType();

[return: MarshalAs(UnmanagedType.BStr)]
string GetUniqueId();

[return: MarshalAs(UnmanagedType.VariantBool)]
bool GetIsExtension();
}

[Guid("c601c175-a3be-44bc-91f6-4568d230fc83")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface ISetupPropertyStore
{

[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
string[] GetNames();

object GetValue([MarshalAs(UnmanagedType.LPWStr), In] string pwszName);
}

[Guid("42843719-DB4C-46C2-8E7C-64F1816EFD5B")]
[CoClass(typeof(SetupConfigurationClass))]
[ComImport]
public interface SetupConfiguration : ISetupConfiguration2, ISetupConfiguration
{
}

[Guid("177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D")]
[ClassInterface(ClassInterfaceType.None)]
[ComImport]
public class SetupConfigurationClass
{
}

public static class Main
{
public static void Query()
{
ISetupConfiguration query = new SetupConfiguration();
ISetupConfiguration2 query2 = (ISetupConfiguration2)query;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use var to prevent redundancy?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're trying to support Windows 7/2008R2 with the tools that it ships with by default, that means C# version 2. var was only introduced in version 3.

IEnumSetupInstances e = query2.EnumAllInstances();

int pceltFetched;
ISetupInstance2[] rgelt = new ISetupInstance2[1];
while (true)
{
e.Next(1, rgelt, out pceltFetched);
if (pceltFetched <= 0)
{
Console.WriteLine("No usable installation of VS2017 found");
return;
}
if (CheckInstance(rgelt[0]))
return;
}
}

private static bool CheckInstance(ISetupInstance2 setupInstance2)
{
// Visual Studio Community 2017 component directory:
// https://www.visualstudio.com/en-us/productinfo/vs2017-install-product-Community.workloads

string path = setupInstance2.GetInstallationPath();
Console.WriteLine(String.Format("Found VS2017 installation at: {0}", path));

bool hasMSBuild = false;
bool hasVCTools = false;
uint Win10SDKVer = 0;
bool hasWin8SDK = false;

foreach (ISetupPackageReference package in setupInstance2.GetPackages())
{
const string Win10SDKPrefix = "Microsoft.VisualStudio.Component.Windows10SDK.";

string id = package.GetId();
if (id == "Microsoft.VisualStudio.VC.MSBuild.Base")
hasMSBuild = true;
else if (id == "Microsoft.VisualStudio.Component.VC.Tools.x86.x64")
hasVCTools = true;
else if (id.StartsWith(Win10SDKPrefix))
Win10SDKVer = Math.Max(Win10SDKVer, UInt32.Parse(id.Substring(Win10SDKPrefix.Length)));
else if (id == "Microsoft.VisualStudio.Component.Windows81SDK")
hasWin8SDK = true;
else
continue;

Console.WriteLine(String.Format(" - Found {0}", id));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onsole.WriteLine(String.Format(" - Found {0}", id)); [](start = 17, length = 53)

unreachable?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is reached when any of the if conditions above is true

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, ofcourse. I misread the code.

}

if (!hasMSBuild)
Console.WriteLine(" - Missing Visual Studio C++ core features (Microsoft.VisualStudio.VC.MSBuild.Base)");
if (!hasVCTools)
Console.WriteLine(" - Missing VC++ 2017 v141 toolset (x86,x64) (Microsoft.VisualStudio.Component.VC.Tools.x86.x64)");
if ((Win10SDKVer == 0) && (!hasWin8SDK))
Console.WriteLine(" - Missing a Windows SDK (Microsoft.VisualStudio.Component.Windows10SDK.* or Microsoft.VisualStudio.Component.Windows81SDK)");

if (hasMSBuild && hasVCTools)
{
string setPath = String.Format("set \"VS2017_INSTALL={0}\"", path);
if (Win10SDKVer > 0)
{
Console.WriteLine(" - Using this installation with Windows 10 SDK");
string[] lines = { setPath, String.Format("set \"VS2017_SDK=10.0.{0}.0\"", Win10SDKVer) };
System.IO.File.WriteAllLines(@"Set_VS2017.bat", lines);
return true;
}
else if (hasWin8SDK)
{
Console.WriteLine(" - Using this installation with Windows 8.1 SDK");
string[] lines = { setPath, "set \"VS2017_SDK=8.1\"" };
System.IO.File.WriteAllLines(@"Set_VS2017.bat", lines);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This smell bad...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be done differently?

return true;
}
}

Console.WriteLine(" - Some required components are missing, not using this installation");
return false;
}
}
}
33 changes: 32 additions & 1 deletion tools/gyp/pylib/gyp/MSVSVersion.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ def SetupScript(self, target_arch):
# vcvars32, which it can only find if VS??COMNTOOLS is set, which it
# isn't always.
if target_arch == 'x86':
if self.short_name == '2017':
return [os.path.normpath(
os.path.join(self.path, 'Common7/Tools/VsDevCmd.bat')), '/no_logo',
'/arch=x86']
if self.short_name >= '2013' and self.short_name[-1] != 'e' and (
os.environ.get('PROCESSOR_ARCHITECTURE') == 'AMD64' or
os.environ.get('PROCESSOR_ARCHITEW6432') == 'AMD64'):
Expand All @@ -96,6 +100,10 @@ def SetupScript(self, target_arch):
os.path.join(self.path, 'Common7/Tools/vsvars32.bat'))]
else:
assert target_arch == 'x64'
if self.short_name == '2017':
return [os.path.normpath(
os.path.join(self.path, 'Common7/Tools/VsDevCmd.bat')), '/no_logo',
'/arch=x64']
arg = 'x86_amd64'
# Use the 64-on-64 compiler if we're not using an express
# edition and we're running on a 64bit OS.
Expand Down Expand Up @@ -226,6 +234,15 @@ def _CreateVersion(name, path, sdk_based=False):
if path:
path = os.path.normpath(path)
versions = {
'2017': VisualStudioVersion('2017',
'Visual Studio 2017',
solution_version='12.00',
project_version='14.0',
flat_sln=False,
uses_vcxproj=True,
path=path,
sdk_based=sdk_based,
default_toolset='v141'),
'2015': VisualStudioVersion('2015',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is a de-novo patch, not based on the strategy in node-gyp:configure.js .
Could be done in configure which generates node.gypi

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Node-gyp is very adverse to floating patches, but that is not the case here. I'd rather have the floating patch and not hack gyp. This patch is small enough to be reviewed here, and contained in a git commit so it can be easily reverted if a different approach lands upstream.

'Visual Studio 2015',
solution_version='12.00',
Expand Down Expand Up @@ -346,6 +363,7 @@ def _DetectVisualStudioVersions(versions_to_check, force_express):
2012(e) - Visual Studio 2012 (11)
2013(e) - Visual Studio 2013 (12)
2015 - Visual Studio 2015 (14)
2017 - Visual Studio 2017 (15)
Where (e) is e for express editions of MSVS and blank otherwise.
"""
version_to_year = {
Expand All @@ -355,6 +373,7 @@ def _DetectVisualStudioVersions(versions_to_check, force_express):
'11.0': '2012',
'12.0': '2013',
'14.0': '2015',
'15.0': '2017',
}
versions = []
for version in versions_to_check:
Expand Down Expand Up @@ -395,6 +414,17 @@ def _DetectVisualStudioVersions(versions_to_check, force_express):
versions.append(_CreateVersion(version_to_year[version] + 'e',
os.path.join(path, '..'), sdk_based=True))

if version == '15.0':
# The VC++ 2017 install location needs to be located using COM instead of
# the registry. For details see:
# https://blogs.msdn.microsoft.com/heaths/2016/09/15/changes-to-visual-studio-15-setup/
# For now we use a hardcoded default with an environment variable
# override.
path = r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional'
path = os.environ.get('vs2017_install', path)
if os.path.exists(path):
versions.append(_CreateVersion('2017', path))

return versions


Expand All @@ -410,7 +440,7 @@ def SelectVisualStudioVersion(version='auto', allow_fallback=True):
if version == 'auto':
version = os.environ.get('GYP_MSVS_VERSION', 'auto')
version_map = {
'auto': ('14.0', '12.0', '10.0', '9.0', '8.0', '11.0'),
'auto': ('15.0', '14.0', '12.0', '10.0', '9.0', '8.0', '11.0'),
'2005': ('8.0',),
'2005e': ('8.0',),
'2008': ('9.0',),
Expand All @@ -422,6 +452,7 @@ def SelectVisualStudioVersion(version='auto', allow_fallback=True):
'2013': ('12.0',),
'2013e': ('12.0',),
'2015': ('14.0',),
'2017': ('15.0',),
}
override_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH')
if override_path:
Expand Down
Loading