527 lines
20 KiB
C#
527 lines
20 KiB
C#
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
|
|
|
// ReSharper disable InconsistentNaming
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using Flax.Build.Projects;
|
|
using Flax.Build.Projects.VisualStudio;
|
|
using Flax.Build.Projects.VisualStudioCode;
|
|
using Microsoft.Win32;
|
|
|
|
#pragma warning disable CA1416
|
|
|
|
namespace Flax.Build.Platforms
|
|
{
|
|
/// <summary>
|
|
/// The Windows platform toolset versions.
|
|
/// </summary>
|
|
public enum WindowsPlatformToolset
|
|
{
|
|
/// <summary>
|
|
/// The same as Visual Studio version (for the project files generation).
|
|
/// </summary>
|
|
Default = 0,
|
|
|
|
/// <summary>
|
|
/// Use the latest toolset.
|
|
/// </summary>
|
|
Latest = 1,
|
|
|
|
/// <summary>
|
|
/// Visual Studio 2015
|
|
/// </summary>
|
|
v140 = 140,
|
|
|
|
/// <summary>
|
|
/// Visual Studio 2017
|
|
/// </summary>
|
|
v141 = 141,
|
|
|
|
/// <summary>
|
|
/// Visual Studio 2019
|
|
/// </summary>
|
|
v142 = 142,
|
|
|
|
/// <summary>
|
|
/// Visual Studio 2022
|
|
/// </summary>
|
|
v143 = 143,
|
|
|
|
/// <summary>
|
|
/// Visual Studio 2022 (v17.10 and later)
|
|
/// </summary>
|
|
v144 = 144,
|
|
}
|
|
|
|
/// <summary>
|
|
/// The Windows platform SDK versions.
|
|
/// </summary>
|
|
public enum WindowsPlatformSDK
|
|
{
|
|
/// <summary>
|
|
/// Use the latest SDK.
|
|
/// </summary>
|
|
Latest,
|
|
|
|
/// <summary>
|
|
/// Windows 8.1 SDK
|
|
/// </summary>
|
|
v8_1,
|
|
|
|
/// <summary>
|
|
/// Windows 10 SDK (10.0.10240.0) RTM (even if never named like that officially)
|
|
/// </summary>
|
|
v10_0_10240_0,
|
|
|
|
/// <summary>
|
|
/// Windows 10 SDK (10.0.10586.0) November 2015 Update
|
|
/// </summary>
|
|
v10_0_10586_0,
|
|
|
|
/// <summary>
|
|
/// Windows 10 SDK (10.0.14393.0) 2016 Anniversary Update
|
|
/// </summary>
|
|
v10_0_14393_0,
|
|
|
|
/// <summary>
|
|
/// Windows 10 SDK (10.0.15063.0) 2017 Creators Update
|
|
/// </summary>
|
|
v10_0_15063_0,
|
|
|
|
/// <summary>
|
|
/// Windows 10 SDK (10.0.16299.0) 2017 Fall Creators Update
|
|
/// </summary>
|
|
v10_0_16299_0,
|
|
|
|
/// <summary>
|
|
/// Windows 10 SDK (10.0.17134.0) April 2018 Update
|
|
/// </summary>
|
|
v10_0_17134_0,
|
|
|
|
/// <summary>
|
|
/// Windows 10 SDK (10.0.17763.0) October 2018 Update
|
|
/// </summary>
|
|
v10_0_17763_0,
|
|
|
|
/// <summary>
|
|
/// Windows 10 SDK (10.0.18362.0) May 2019 Update
|
|
/// </summary>
|
|
v10_0_18362_0,
|
|
|
|
/// <summary>
|
|
/// Windows 10 SDK (10.0.19041.0)
|
|
/// </summary>
|
|
v10_0_19041_0,
|
|
|
|
/// <summary>
|
|
/// Windows 10 SDK (10.0.20348.0) 21H1
|
|
/// </summary>
|
|
v10_0_20348_0,
|
|
|
|
/// <summary>
|
|
/// Windows 11 SDK (10.0.22000.0)
|
|
/// </summary>
|
|
v10_0_22000_0,
|
|
|
|
/// <summary>
|
|
/// Windows 11 SDK (10.0.22621.0) 22H2
|
|
/// </summary>
|
|
v10_0_22621_0,
|
|
}
|
|
|
|
/// <summary>
|
|
/// The Microsoft Windows base platform implementation.
|
|
/// </summary>
|
|
/// <seealso cref="Platform" />
|
|
public abstract class WindowsPlatformBase : Platform, IProjectCustomizer
|
|
{
|
|
private static Dictionary<WindowsPlatformToolset, string> _toolsets;
|
|
private static Dictionary<WindowsPlatformSDK, string> _sdks;
|
|
|
|
/// <summary>
|
|
/// The flag used for <see cref="HasRequiredSDKsInstalled"/>.
|
|
/// </summary>
|
|
protected bool _hasRequiredSDKsInstalled;
|
|
|
|
/// <inheritdoc />
|
|
public override bool HasRequiredSDKsInstalled => _hasRequiredSDKsInstalled;
|
|
|
|
/// <inheritdoc />
|
|
public override bool HasSharedLibrarySupport => true;
|
|
|
|
/// <inheritdoc />
|
|
public override bool HasExecutableFileReferenceSupport => true;
|
|
|
|
/// <inheritdoc />
|
|
public override string ExecutableFileExtension => ".exe";
|
|
|
|
/// <inheritdoc />
|
|
public override string SharedLibraryFileExtension => ".dll";
|
|
|
|
/// <inheritdoc />
|
|
public override string StaticLibraryFileExtension => ".lib";
|
|
|
|
/// <inheritdoc />
|
|
public override string ProgramDatabaseFileExtension => ".pdb";
|
|
|
|
/// <inheritdoc />
|
|
public override ProjectFormat DefaultProjectFormat
|
|
{
|
|
get
|
|
{
|
|
if (VisualStudioInstance.HasIDE())
|
|
return ProjectFormat.VisualStudio;
|
|
if (VisualStudioCodeInstance.HasIDE())
|
|
return ProjectFormat.VisualStudioCode;
|
|
return ProjectFormat.VisualStudio;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="WindowsPlatformBase"/> class.
|
|
/// </summary>
|
|
protected WindowsPlatformBase()
|
|
{
|
|
var sdsk = GetSDKs();
|
|
var toolsets = GetToolsets();
|
|
_hasRequiredSDKsInstalled = sdsk.Count > 0 && toolsets.Count > 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to reads a directory name stored in a registry key.
|
|
/// </summary>
|
|
/// <param name="keyName">The key to read from.</param>
|
|
/// <param name="valueName">The value within the key to read.</param>
|
|
/// <param name="value">The directory read from the registry key.</param>
|
|
/// <returns>True if the key was read, false if it was missing or empty.</returns>
|
|
public static bool TryReadDirRegistryKey(string keyName, string valueName, out string value)
|
|
{
|
|
value = Registry.GetValue(keyName, valueName, null) as string;
|
|
if (string.IsNullOrEmpty(value))
|
|
{
|
|
value = null;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to reads an install directory for a 32-bit program from a registry key. It checks for per-user and machine wide settings, and under the Wow64 virtual keys.
|
|
/// </summary>
|
|
/// <param name="keySuffix">The path to the key to read, under one of the roots listed above.</param>
|
|
/// <param name="valueName">The value to be read.</param>
|
|
/// <param name="dir">When this method completes with success it contains a directory corresponding to the value read.</param>
|
|
/// <returns>True if the key was read, false otherwise.</returns>
|
|
public static bool TryReadInstallDirRegistryKey32(string keySuffix, string valueName, out string dir)
|
|
{
|
|
if (TryReadDirRegistryKey("HKEY_CURRENT_USER\\SOFTWARE\\" + keySuffix, valueName, out dir))
|
|
return true;
|
|
if (TryReadDirRegistryKey("HKEY_LOCAL_MACHINE\\SOFTWARE\\" + keySuffix, valueName, out dir))
|
|
return true;
|
|
if (TryReadDirRegistryKey("HKEY_CURRENT_USER\\SOFTWARE\\Wow6432Node\\" + keySuffix, valueName, out dir))
|
|
return true;
|
|
if (TryReadDirRegistryKey("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\" + keySuffix, valueName, out dir))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
private static void FindMsvcToolsets(string rootDir)
|
|
{
|
|
if (!Directory.Exists(rootDir))
|
|
return;
|
|
var toolsets = Directory.GetDirectories(rootDir);
|
|
foreach (var toolset in toolsets)
|
|
{
|
|
if (Version.TryParse(Path.GetFileName(toolset), out var version) &&
|
|
(File.Exists(Path.Combine(toolset, "bin", "Hostx64", "x64", "cl.exe")) ||
|
|
File.Exists(Path.Combine(toolset, "bin", "Hostx86", "x64", "cl.exe"))))
|
|
{
|
|
if (version.Major == 14 && version.Minor / 10 == 1)
|
|
_toolsets[WindowsPlatformToolset.v141] = toolset;
|
|
else if (version.Major == 14 && version.Minor / 10 == 2)
|
|
_toolsets[WindowsPlatformToolset.v142] = toolset;
|
|
else if (version.Major == 14 && version.Minor / 10 == 3)
|
|
_toolsets[WindowsPlatformToolset.v143] = toolset;
|
|
else if (version.Major == 14 && version.Minor / 10 == 4)
|
|
_toolsets[WindowsPlatformToolset.v144] = toolset;
|
|
else
|
|
Log.Warning("Found Unsupported MSVC toolset version: " + version);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds all the directories containing the Windows platform toolset.
|
|
/// </summary>
|
|
/// <returns>The collection of installed toolsets.</returns>
|
|
public static Dictionary<WindowsPlatformToolset, string> GetToolsets()
|
|
{
|
|
if (_toolsets != null)
|
|
return _toolsets;
|
|
_toolsets = new Dictionary<WindowsPlatformToolset, string>();
|
|
|
|
// Skip if running on non-Windows system
|
|
if (BuildTargetPlatform != TargetPlatform.Windows)
|
|
return _toolsets;
|
|
|
|
var vsInstances = VisualStudioInstance.GetInstances();
|
|
|
|
// Visual Studio 2015 - single instance
|
|
var vs2015 = vsInstances.FirstOrDefault(x => x.Version == VisualStudioVersion.VisualStudio2015);
|
|
if (vs2015 != null)
|
|
{
|
|
string rootDir = Path.Combine(vs2015.Path, "VC");
|
|
if (Directory.Exists(rootDir) &&
|
|
(File.Exists(Path.Combine(rootDir, "bin", "amd64", "cl.exe")) ||
|
|
File.Exists(Path.Combine(rootDir, "bin", "x86_amd64", "cl.exe"))))
|
|
{
|
|
_toolsets[WindowsPlatformToolset.v140] = rootDir;
|
|
}
|
|
}
|
|
|
|
// Visual Studio 2017-2022 - multiple instances
|
|
foreach (var vs in vsInstances.Where(x =>
|
|
x.Version == VisualStudioVersion.VisualStudio2017 ||
|
|
x.Version == VisualStudioVersion.VisualStudio2019 ||
|
|
x.Version == VisualStudioVersion.VisualStudio2022
|
|
))
|
|
{
|
|
FindMsvcToolsets(Path.Combine(vs.Path, "VC", "Tools", "MSVC"));
|
|
}
|
|
|
|
foreach (var e in _toolsets)
|
|
Log.Verbose(string.Format("Found Windows toolset {0} at {1}", e.Key, e.Value));
|
|
return _toolsets;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the SDK version.
|
|
/// </summary>
|
|
/// <param name="sdk">The SDK.</param>
|
|
/// <returns>The version of the SDK enum.</returns>
|
|
public static Version GetSDKVersion(WindowsPlatformSDK sdk)
|
|
{
|
|
switch (sdk)
|
|
{
|
|
case WindowsPlatformSDK.v8_1: return new Version(8, 1);
|
|
case WindowsPlatformSDK.v10_0_10240_0: return new Version(10, 0, 10240, 0);
|
|
case WindowsPlatformSDK.v10_0_10586_0: return new Version(10, 0, 10586, 0);
|
|
case WindowsPlatformSDK.v10_0_14393_0: return new Version(10, 0, 14393, 0);
|
|
case WindowsPlatformSDK.v10_0_15063_0: return new Version(10, 0, 15063, 0);
|
|
case WindowsPlatformSDK.v10_0_16299_0: return new Version(10, 0, 16299, 0);
|
|
case WindowsPlatformSDK.v10_0_17134_0: return new Version(10, 0, 17134, 0);
|
|
case WindowsPlatformSDK.v10_0_17763_0: return new Version(10, 0, 17763, 0);
|
|
case WindowsPlatformSDK.v10_0_18362_0: return new Version(10, 0, 18362, 0);
|
|
case WindowsPlatformSDK.v10_0_19041_0: return new Version(10, 0, 19041, 0);
|
|
case WindowsPlatformSDK.v10_0_20348_0: return new Version(10, 0, 20348, 0);
|
|
case WindowsPlatformSDK.v10_0_22000_0: return new Version(10, 0, 22000, 0);
|
|
case WindowsPlatformSDK.v10_0_22621_0: return new Version(10, 0, 22621, 0);
|
|
default: throw new ArgumentOutOfRangeException(nameof(sdk), sdk, null);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds all the directories containing the Windows SDKs.
|
|
/// </summary>
|
|
/// <returns>The collection of installed SDks.</returns>
|
|
public static Dictionary<WindowsPlatformSDK, string> GetSDKs()
|
|
{
|
|
if (_sdks != null)
|
|
return _sdks;
|
|
_sdks = new Dictionary<WindowsPlatformSDK, string>();
|
|
|
|
// Skip if running on non-Windows system
|
|
if (BuildTargetPlatform != TargetPlatform.Windows)
|
|
return _sdks;
|
|
|
|
// Check Windows 8.1 SDK
|
|
if (TryReadInstallDirRegistryKey32("Microsoft\\Microsoft SDKs\\Windows\\v8.1", "InstallationFolder", out var sdk81))
|
|
{
|
|
if (File.Exists(Path.Combine(sdk81, "Include", "um", "windows.h")))
|
|
{
|
|
_sdks.Add(WindowsPlatformSDK.v8_1, sdk81);
|
|
}
|
|
}
|
|
|
|
// Check Windows 10 SDKs
|
|
var sdk10Roots = new HashSet<string>();
|
|
{
|
|
string rootDir;
|
|
if (TryReadInstallDirRegistryKey32("Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10", out rootDir))
|
|
{
|
|
sdk10Roots.Add(rootDir);
|
|
}
|
|
if (TryReadInstallDirRegistryKey32("Microsoft\\Microsoft SDKs\\Windows\\v10.0", "InstallationFolder", out rootDir))
|
|
{
|
|
sdk10Roots.Add(rootDir);
|
|
}
|
|
}
|
|
var win10Sdks = new[]
|
|
{
|
|
WindowsPlatformSDK.v10_0_10240_0,
|
|
WindowsPlatformSDK.v10_0_10586_0,
|
|
WindowsPlatformSDK.v10_0_14393_0,
|
|
WindowsPlatformSDK.v10_0_15063_0,
|
|
WindowsPlatformSDK.v10_0_16299_0,
|
|
WindowsPlatformSDK.v10_0_17134_0,
|
|
WindowsPlatformSDK.v10_0_17763_0,
|
|
WindowsPlatformSDK.v10_0_18362_0,
|
|
WindowsPlatformSDK.v10_0_19041_0,
|
|
WindowsPlatformSDK.v10_0_20348_0,
|
|
WindowsPlatformSDK.v10_0_22000_0,
|
|
WindowsPlatformSDK.v10_0_22621_0,
|
|
};
|
|
foreach (var sdk10 in sdk10Roots)
|
|
{
|
|
var includeRootDir = Path.Combine(sdk10, "Include");
|
|
if (Directory.Exists(includeRootDir))
|
|
{
|
|
foreach (var includeDir in Directory.GetDirectories(includeRootDir))
|
|
{
|
|
if (Version.TryParse(Path.GetFileName(includeDir), out var version) && File.Exists(Path.Combine(includeDir, "um", "windows.h")))
|
|
{
|
|
bool unknown = true;
|
|
foreach (var win10Sdk in win10Sdks)
|
|
{
|
|
if (version == GetSDKVersion(win10Sdk))
|
|
{
|
|
_sdks.Add(win10Sdk, sdk10);
|
|
unknown = false;
|
|
break;
|
|
}
|
|
}
|
|
if (unknown)
|
|
Log.Warning(string.Format("Unknown Windows 10 SDK version {0} at {1}", version, sdk10));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var e in _sdks)
|
|
{
|
|
Log.Verbose(string.Format("Found Windows SDK {0} at {1}", e.Key, e.Value));
|
|
}
|
|
|
|
return _sdks;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the path to the 32-bit tool binaries.
|
|
/// </summary>
|
|
/// <param name="toolset">The version of the toolset to use.</param>
|
|
/// <returns>The directory containing the 64-bit toolchain binaries.</returns>
|
|
public static string GetVCToolPath32(WindowsPlatformToolset toolset)
|
|
{
|
|
var toolsets = GetToolsets();
|
|
var vcToolChainDir = toolsets[toolset];
|
|
|
|
switch (toolset)
|
|
{
|
|
case WindowsPlatformToolset.v140:
|
|
{
|
|
string compilerPath = Path.Combine(vcToolChainDir, "bin", "cl.exe");
|
|
if (File.Exists(compilerPath))
|
|
{
|
|
return Path.GetDirectoryName(compilerPath);
|
|
}
|
|
|
|
throw new Exception(string.Format("No 32-bit compiler toolchain found in {0}", compilerPath));
|
|
}
|
|
case WindowsPlatformToolset.v141:
|
|
case WindowsPlatformToolset.v142:
|
|
case WindowsPlatformToolset.v143:
|
|
case WindowsPlatformToolset.v144:
|
|
{
|
|
/*
|
|
string crossCompilerPath = Path.Combine(vcToolChainDir, "bin", "HostX64", "x86", "cl.exe");
|
|
if (File.Exists(crossCompilerPath))
|
|
{
|
|
return Path.GetDirectoryName(crossCompilerPath);
|
|
}
|
|
*/
|
|
|
|
string nativeCompilerPath = Path.Combine(vcToolChainDir, "bin", "HostX86", "x86", "cl.exe");
|
|
if (File.Exists(nativeCompilerPath))
|
|
{
|
|
return Path.GetDirectoryName(nativeCompilerPath);
|
|
}
|
|
|
|
//throw new Exception(string.Format("No 32-bit compiler toolchain found in {0} or {1}", crossCompilerPath, nativeCompilerPath));
|
|
throw new Exception(string.Format("No 32-bit compiler toolchain found in {0}", nativeCompilerPath));
|
|
}
|
|
default: throw new ArgumentOutOfRangeException(nameof(toolset), toolset, null);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the path to the 64-bit tool binaries.
|
|
/// </summary>
|
|
/// <param name="toolset">The version of the toolset to use.</param>
|
|
/// <returns>The directory containing the 64-bit toolchain binaries.</returns>
|
|
public static string GetVCToolPath64(WindowsPlatformToolset toolset)
|
|
{
|
|
var toolsets = GetToolsets();
|
|
var vcToolChainDir = toolsets[toolset];
|
|
|
|
switch (toolset)
|
|
{
|
|
case WindowsPlatformToolset.v140:
|
|
{
|
|
string nativeCompilerPath = Path.Combine(vcToolChainDir, "bin", "amd64", "cl.exe");
|
|
if (File.Exists(nativeCompilerPath))
|
|
{
|
|
return Path.GetDirectoryName(nativeCompilerPath);
|
|
}
|
|
|
|
string crossCompilerPath = Path.Combine(vcToolChainDir, "bin", "x86_amd64", "cl.exe");
|
|
if (File.Exists(crossCompilerPath))
|
|
{
|
|
return Path.GetDirectoryName(crossCompilerPath);
|
|
}
|
|
|
|
throw new Exception(string.Format("No 64-bit compiler toolchain found in {0} or {1}", nativeCompilerPath, crossCompilerPath));
|
|
}
|
|
case WindowsPlatformToolset.v141:
|
|
case WindowsPlatformToolset.v142:
|
|
case WindowsPlatformToolset.v143:
|
|
case WindowsPlatformToolset.v144:
|
|
{
|
|
string nativeCompilerPath = Path.Combine(vcToolChainDir, "bin", "HostX64", "x64", "cl.exe");
|
|
if (File.Exists(nativeCompilerPath))
|
|
{
|
|
return Path.GetDirectoryName(nativeCompilerPath);
|
|
}
|
|
|
|
string crossCompilerPath = Path.Combine(vcToolChainDir, "bin", "HostX86", "x64", "cl.exe");
|
|
if (File.Exists(crossCompilerPath))
|
|
{
|
|
return Path.GetDirectoryName(crossCompilerPath);
|
|
}
|
|
|
|
throw new Exception(string.Format("No 64-bit compiler toolchain found in {0} or {1}", nativeCompilerPath, crossCompilerPath));
|
|
}
|
|
default: throw new ArgumentOutOfRangeException(nameof(toolset), toolset, null);
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
void IProjectCustomizer.GetSolutionArchitectureName(TargetArchitecture architecture, ref string name)
|
|
{
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
void IProjectCustomizer.GetProjectArchitectureName(Project project, Platform platform, TargetArchitecture architecture, ref string name)
|
|
{
|
|
if (architecture == TargetArchitecture.x86)
|
|
{
|
|
name = "Win32";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#pragma warning restore CA1416
|