mirror of https://github.com/mono/CppSharp.git
c-sharpdotnetmonobindingsbridgecclangcpluspluscppsharpglueinteropparserparsingpinvokeswigsyntax-treevisitorsxamarinxamarin-bindings
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
543 lines
20 KiB
543 lines
20 KiB
using System; |
|
using System.Collections.Generic; |
|
using System.Globalization; |
|
using System.IO; |
|
using System.Linq; |
|
using System.Text.RegularExpressions; |
|
using CppSharp.Parser; |
|
using CppSharp.Parser.AST; |
|
using Microsoft.Win32; |
|
|
|
namespace CppSharp |
|
{ |
|
/// Represents a Visual Studio version. |
|
public enum VisualStudioVersion |
|
{ |
|
VS2012, |
|
VS2013, |
|
VS2015, |
|
Latest |
|
} |
|
|
|
/// Represents a toolchain with associated version and directory. |
|
public struct ToolchainVersion |
|
{ |
|
/// Version of the toolchain. |
|
public float Version; |
|
|
|
/// Directory location of the toolchain. |
|
public string Directory; |
|
|
|
/// Extra data value associated with the toolchain. |
|
public string Value; |
|
|
|
public override string ToString() |
|
{ |
|
return string.Format("{0} (version: {1})", Directory, Version); |
|
} |
|
} |
|
|
|
public static class MSVCToolchain |
|
{ |
|
static void DumpSdks(string sku, IEnumerable<ToolchainVersion> sdks) |
|
{ |
|
Console.WriteLine("\n{0} SDKs:", sku); |
|
foreach (var sdk in sdks) |
|
Console.WriteLine("\t({0}) {1}", sdk.Version, sdk.Directory); |
|
} |
|
|
|
/// Dumps the detected SDK versions. |
|
public static void DumpSdks() |
|
{ |
|
List<ToolchainVersion> vsSdks; |
|
GetVisualStudioSdks(out vsSdks); |
|
DumpSdks("Visual Studio", vsSdks); |
|
|
|
List<ToolchainVersion> windowsSdks; |
|
GetWindowsSdks(out windowsSdks); |
|
DumpSdks("Windows", windowsSdks); |
|
|
|
List<ToolchainVersion> windowsKitsSdks; |
|
GetWindowsKitsSdks(out windowsKitsSdks); |
|
DumpSdks("Windows Kits", windowsKitsSdks); |
|
|
|
List<ToolchainVersion> netFrameworkSdks; |
|
GetNetFrameworkSdks(out netFrameworkSdks); |
|
DumpSdks(".NET Framework", netFrameworkSdks); |
|
|
|
List<ToolchainVersion> msbuildSdks; |
|
GetMSBuildSdks(out msbuildSdks); |
|
DumpSdks("MSBuild", msbuildSdks); |
|
} |
|
|
|
/// Dumps include directories for selected toolchain. |
|
public static void DumpSdkIncludes(VisualStudioVersion vsVersion = |
|
VisualStudioVersion.Latest) |
|
{ |
|
Console.WriteLine("\nInclude search path (VS: {0}):", vsVersion); |
|
var includes = GetSystemIncludes(vsVersion); |
|
foreach (var include in includes) |
|
Console.WriteLine("\t{0}", include); |
|
} |
|
|
|
public static int GetCLVersion(VisualStudioVersion vsVersion) |
|
{ |
|
int clVersion; |
|
switch (vsVersion) |
|
{ |
|
case VisualStudioVersion.VS2012: |
|
clVersion = 17; |
|
break; |
|
case VisualStudioVersion.VS2013: |
|
clVersion = 18; |
|
break; |
|
case VisualStudioVersion.VS2015: |
|
case VisualStudioVersion.Latest: |
|
clVersion = 19; |
|
break; |
|
default: |
|
throw new Exception("Unknown Visual Studio version"); |
|
} |
|
|
|
return clVersion; |
|
} |
|
|
|
static int GetVisualStudioVersion(VisualStudioVersion version) |
|
{ |
|
switch (version) |
|
{ |
|
case VisualStudioVersion.VS2012: |
|
return 11; |
|
case VisualStudioVersion.VS2013: |
|
return 12; |
|
case VisualStudioVersion.VS2015: |
|
case VisualStudioVersion.Latest: |
|
return 14; |
|
default: |
|
throw new Exception("Unknown Visual Studio version"); |
|
} |
|
} |
|
|
|
/// Gets the system include folders for the given Visual Studio version. |
|
public static List<string> GetSystemIncludes(VisualStudioVersion vsVersion) |
|
{ |
|
var includes = new List<string>(); |
|
|
|
List<ToolchainVersion> vsSdks; |
|
GetVisualStudioSdks(out vsSdks); |
|
|
|
if (vsSdks.Count == 0) |
|
throw new Exception("Could not find a valid Visual Studio toolchain"); |
|
|
|
var vsSdk = (vsVersion == VisualStudioVersion.Latest) |
|
? vsSdks.Last() |
|
: vsSdks.Find(version => |
|
(int)version.Version == GetVisualStudioVersion(vsVersion)); |
|
|
|
var vsDir = vsSdk.Directory; |
|
vsDir = vsDir.Substring(0, vsDir.LastIndexOf(@"\Common7\IDE", |
|
StringComparison.Ordinal)); |
|
|
|
includes.Add(Path.Combine(vsDir, @"VC\include")); |
|
|
|
// Check VCVarsQueryRegistry.bat to see which Windows SDK version |
|
// is supposed to be used with this VS version. |
|
var vcVarsPath = Path.Combine(vsDir, @"Common7\Tools\VCVarsQueryRegistry.bat"); |
|
|
|
int windowsSdkMajorVer = 0; |
|
string kitsRootKey = string.Empty; |
|
if (File.Exists(vcVarsPath)) |
|
{ |
|
var vcVarsFile = File.ReadAllText(vcVarsPath); |
|
var match = Regex.Match(vcVarsFile, @"Windows\\v([1-9][0-9]*)\.?([0-9]*)"); |
|
if (match.Success) |
|
windowsSdkMajorVer = int.Parse(match.Groups[1].Value); |
|
|
|
match = Regex.Match(vcVarsFile, @"KitsRoot([1-9][0-9]*)"); |
|
if (match.Success) |
|
kitsRootKey = match.Groups[0].Value; |
|
} |
|
|
|
List<ToolchainVersion> windowsSdks; |
|
GetWindowsSdks(out windowsSdks); |
|
|
|
// Older Visual Studio versions provide their own Windows SDK. |
|
if (windowsSdks.Count == 0) |
|
{ |
|
includes.Add(Path.Combine(vsDir, @"\VC\PlatformSDK\Include")); |
|
} |
|
else |
|
{ |
|
includes.AddRange(GetIncludeDirsFromWindowsSdks(windowsSdkMajorVer, windowsSdks)); |
|
} |
|
|
|
List<ToolchainVersion> windowsKitsSdks; |
|
GetWindowsKitsSdks(out windowsKitsSdks); |
|
|
|
var windowsKitSdk = (!string.IsNullOrWhiteSpace(kitsRootKey)) |
|
? windowsKitsSdks.Find(version => version.Value == kitsRootKey) |
|
: windowsKitsSdks.Last(); |
|
|
|
includes.AddRange( |
|
CollectUniversalCRuntimeIncludeDirs(vsDir, windowsKitSdk, windowsSdkMajorVer)); |
|
|
|
return includes; |
|
} |
|
|
|
private static IEnumerable<string> GetIncludeDirsFromWindowsSdks( |
|
int windowsSdkMajorVer, List<ToolchainVersion> windowsSdks) |
|
{ |
|
var includes = new List<string>(); |
|
var majorWindowsSdk = windowsSdks.Find( |
|
version => (int) Math.Floor(version.Version) == windowsSdkMajorVer); |
|
var windowsSdkDirs = majorWindowsSdk.Directory != null ? |
|
new[] { majorWindowsSdk.Directory } : |
|
windowsSdks.Select(w => w.Directory).Reverse(); |
|
foreach (var windowsSdkDir in windowsSdkDirs) |
|
{ |
|
if (windowsSdkMajorVer >= 8) |
|
{ |
|
var shared = Path.Combine(windowsSdkDir, "include", "shared"); |
|
var um = Path.Combine(windowsSdkDir, "include", "um"); |
|
var winrt = Path.Combine(windowsSdkDir, "include", "winrt"); |
|
if (Directory.Exists(shared) && Directory.Exists(um) && |
|
Directory.Exists(winrt)) |
|
return new[] { shared, um, winrt }; |
|
} |
|
else |
|
{ |
|
var include = Path.Combine(windowsSdkDir, "include"); |
|
if (Directory.Exists(include)) |
|
return new[] { include }; |
|
} |
|
} |
|
return new string[0]; |
|
} |
|
|
|
private static IEnumerable<string> CollectUniversalCRuntimeIncludeDirs( |
|
string vsDir, ToolchainVersion windowsKitSdk, int windowsSdkMajorVer) |
|
{ |
|
var includes = new List<string>(); |
|
|
|
// Check vsvars32.bat to see location of the new Universal C runtime library. |
|
string ucrtPaths = string.Empty; |
|
var vsVarsPath = Path.Combine(vsDir, @"Common7\Tools\vsvars32.bat"); |
|
if (File.Exists(vsVarsPath)) |
|
{ |
|
var vsVarsFile = File.ReadAllText(vsVarsPath); |
|
var match = Regex.Match(vsVarsFile, @"INCLUDE=%UniversalCRTSdkDir%(.+)%INCLUDE%"); |
|
if (match.Success) |
|
ucrtPaths = match.Groups[1].Value; |
|
} |
|
|
|
if (string.IsNullOrWhiteSpace(ucrtPaths)) return includes; |
|
|
|
// like the rest of GetSystemIncludes, this is a hack |
|
// which copies the logic in the vcvarsqueryregistry.bat |
|
// the correct way would be to execute the script and collect the values |
|
// there's a reference implementation in the 'get_MSVC_UCRTVersion_from_VS_batch_script' branch |
|
// however, it cannot be used because it needs invoking an external process to run the .bat |
|
// which is killed prematurely by VS when the pre-build events of wrapper projects are run |
|
const string ucrtVersionEnvVar = "%UCRTVersion%"; |
|
foreach (var path in ucrtPaths.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) |
|
{ |
|
var index = path.IndexOf(ucrtVersionEnvVar, StringComparison.Ordinal); |
|
if (index >= 0) |
|
{ |
|
var include = path.Substring(0, index); |
|
var parentIncludeDir = Path.Combine(windowsKitSdk.Directory, include); |
|
var dirPrefix = windowsSdkMajorVer + "."; |
|
var includeDir = |
|
(from dir in Directory.EnumerateDirectories(parentIncludeDir).OrderByDescending(d => d) |
|
where Path.GetFileName(dir).StartsWith(dirPrefix) |
|
select Path.Combine(windowsKitSdk.Directory, include, dir)).FirstOrDefault(); |
|
if (!string.IsNullOrEmpty(includeDir)) |
|
includes.Add(Path.Combine(includeDir, Path.GetFileName(path))); |
|
} |
|
else |
|
includes.Add(Path.Combine(windowsKitSdk.Directory, path)); |
|
} |
|
|
|
return includes; |
|
} |
|
|
|
/// Gets .NET framework installation directories. |
|
public static bool GetNetFrameworkSdks(out List<ToolchainVersion> versions) |
|
{ |
|
versions = new List<ToolchainVersion>(); |
|
|
|
GetToolchainsFromSystemRegistry( |
|
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\.NETFramework", |
|
"InstallRoot", versions, RegistryView.Registry32); |
|
|
|
if (versions.Count == 0 && Environment.Is64BitProcess) |
|
{ |
|
GetToolchainsFromSystemRegistry( |
|
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\.NETFramework", |
|
"InstallRoot", versions, RegistryView.Registry64); |
|
} |
|
|
|
versions.Sort((v1, v2) => (int)(v1.Version - v2.Version)); |
|
|
|
return versions.Count != 0; |
|
} |
|
|
|
/// Gets MSBuild installation directories. |
|
public static bool GetMSBuildSdks(out List<ToolchainVersion> versions) |
|
{ |
|
versions = new List<ToolchainVersion>(); |
|
|
|
GetToolchainsFromSystemRegistry( |
|
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions", |
|
"MSBuildToolsPath", versions, RegistryView.Registry32); |
|
|
|
if (versions.Count == 0 && Environment.Is64BitProcess) |
|
{ |
|
GetToolchainsFromSystemRegistry( |
|
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions", |
|
"MSBuildToolsPath", versions, RegistryView.Registry64); |
|
} |
|
|
|
versions.Sort((v1, v2) => (int)(v1.Version - v2.Version)); |
|
|
|
return versions.Count != 0; |
|
} |
|
|
|
/// Gets Windows SDK installation directories. |
|
public static bool GetWindowsSdks(out List<ToolchainVersion> versions) |
|
{ |
|
versions = new List<ToolchainVersion>(); |
|
|
|
GetToolchainsFromSystemRegistry( |
|
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows", |
|
"InstallationFolder", versions, RegistryView.Registry32); |
|
|
|
if (versions.Count == 0 && Environment.Is64BitProcess) |
|
{ |
|
GetToolchainsFromSystemRegistry( |
|
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows", |
|
"InstallationFolder", versions, RegistryView.Registry64); |
|
} |
|
|
|
versions.Sort((v1, v2) => (int)(v1.Version - v2.Version)); |
|
|
|
return versions.Count != 0; |
|
} |
|
|
|
/// Gets Windows Kits SDK installation directories. |
|
public static bool GetWindowsKitsSdks(out List<ToolchainVersion> versions) |
|
{ |
|
versions = new List<ToolchainVersion>(); |
|
|
|
GetToolchainsFromSystemRegistryValues( |
|
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", |
|
"KitsRoot", versions, RegistryView.Registry32); |
|
|
|
if (versions.Count == 0 && Environment.Is64BitProcess) |
|
{ |
|
GetToolchainsFromSystemRegistryValues( |
|
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", |
|
"KitsRoot", versions, RegistryView.Registry64); |
|
} |
|
|
|
versions.Sort((v1, v2) => (int)(v1.Version - v2.Version)); |
|
|
|
return true; |
|
} |
|
|
|
/// Gets Visual Studio installation directories. |
|
public static bool GetVisualStudioSdks(out List<ToolchainVersion> versions) |
|
{ |
|
versions = new List<ToolchainVersion>(); |
|
|
|
GetToolchainsFromSystemRegistry( |
|
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio", |
|
"InstallDir", versions, RegistryView.Registry32); |
|
|
|
if (versions.Count == 0 && Environment.Is64BitProcess) |
|
{ |
|
GetToolchainsFromSystemRegistry( |
|
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio", |
|
"InstallDir", versions, RegistryView.Registry64); |
|
} |
|
|
|
GetToolchainsFromSystemRegistry( |
|
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress", |
|
"InstallDir", versions, RegistryView.Registry32); |
|
|
|
if (versions.Count == 0 && Environment.Is64BitProcess) |
|
{ |
|
GetToolchainsFromSystemRegistry( |
|
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress", |
|
"InstallDir", versions, RegistryView.Registry64); |
|
} |
|
|
|
versions.Sort((v1, v2) => (int)(v1.Version - v2.Version)); |
|
|
|
return true; |
|
} |
|
|
|
/// Read registry strings looking for matching values. |
|
public static bool GetToolchainsFromSystemRegistryValues(string keyPath, |
|
string matchValue, ICollection<ToolchainVersion> entries, RegistryView view) |
|
{ |
|
string subKey; |
|
var hive = GetRegistryHive(keyPath, out subKey); |
|
using (var rootKey = RegistryKey.OpenBaseKey(hive, view)) |
|
using (var key = rootKey.OpenSubKey(subKey, writable: false)) |
|
{ |
|
if (key == null) |
|
return false; |
|
|
|
foreach (var valueName in key.GetValueNames()) |
|
{ |
|
if (!valueName.Contains(matchValue)) |
|
continue; |
|
|
|
var value = key.GetValue(valueName) as string; |
|
if (value == null) |
|
continue; |
|
|
|
float version = 0; |
|
|
|
// Get the number version from the key value. |
|
var match = Regex.Match(value, @".*([1-9][0-9]*\.?[0-9]*)"); |
|
if (match.Success) |
|
{ |
|
float.TryParse(match.Groups[1].Value, NumberStyles.Number, |
|
CultureInfo.InvariantCulture, out version); |
|
} |
|
|
|
var entry = new ToolchainVersion |
|
{ |
|
Directory = value, |
|
Version = version, |
|
Value = valueName |
|
}; |
|
|
|
entries.Add(entry); |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
/// Read registry string. |
|
/// This also supports a means to look for high-versioned keys by use |
|
/// of a $VERSION placeholder in the key path. |
|
/// $VERSION in the key path is a placeholder for the version number, |
|
/// causing the highest value path to be searched for and used. |
|
/// I.e. "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio". |
|
/// There can be additional characters in the component. Only the numeric |
|
/// characters are compared. |
|
static bool GetToolchainsFromSystemRegistry(string keyPath, string valueName, |
|
ICollection<ToolchainVersion> entries, RegistryView view) |
|
{ |
|
string subKey; |
|
var hive = GetRegistryHive(keyPath, out subKey); |
|
using (var rootKey = RegistryKey.OpenBaseKey(hive, view)) |
|
using (var key = rootKey.OpenSubKey(subKey, writable: false)) |
|
{ |
|
if (key == null) |
|
return false; |
|
|
|
foreach (var subKeyName in key.GetSubKeyNames()) |
|
{ |
|
ToolchainVersion entry; |
|
if (HandleToolchainRegistrySubKey(out entry, key, valueName, |
|
subKeyName)) |
|
entries.Add(entry); |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
static bool HandleToolchainRegistrySubKey(out ToolchainVersion entry, |
|
RegistryKey key, string valueName, string subKeyName) |
|
{ |
|
entry = new ToolchainVersion(); |
|
|
|
// Get the number version from the key. |
|
var match = Regex.Match(subKeyName, @"[1-9][0-9]*\.?[0-9]*"); |
|
if (!match.Success) |
|
return false; |
|
|
|
var versionText = match.Groups[0].Value; |
|
|
|
float version; |
|
float.TryParse(versionText, NumberStyles.Number, |
|
CultureInfo.InvariantCulture, out version); |
|
|
|
using (var versionKey = key.OpenSubKey(subKeyName)) |
|
{ |
|
if (versionKey == null) |
|
return false; |
|
|
|
// Check that the key has a value passed by the caller. |
|
var keyValue = versionKey.GetValue(valueName); |
|
if (keyValue == null) |
|
return false; |
|
|
|
entry = new ToolchainVersion |
|
{ |
|
Version = version, |
|
Directory = keyValue.ToString() |
|
}; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
static RegistryHive GetRegistryHive(string keyPath, out string subKey) |
|
{ |
|
var hive = (RegistryHive)0; |
|
subKey = null; |
|
|
|
if (keyPath.StartsWith("HKEY_CLASSES_ROOT\\")) |
|
{ |
|
hive = RegistryHive.ClassesRoot; |
|
subKey = keyPath.Substring(18); |
|
} |
|
else if (keyPath.StartsWith("HKEY_USERS\\")) |
|
{ |
|
hive = RegistryHive.Users; |
|
subKey = keyPath.Substring(11); |
|
} |
|
else if (keyPath.StartsWith("HKEY_LOCAL_MACHINE\\")) |
|
{ |
|
hive = RegistryHive.LocalMachine; |
|
subKey = keyPath.Substring(19); |
|
} |
|
else if (keyPath.StartsWith("HKEY_CURRENT_USER\\")) |
|
{ |
|
hive = RegistryHive.CurrentUser; |
|
subKey = keyPath.Substring(18); |
|
} |
|
|
|
return hive; |
|
} |
|
} |
|
|
|
public static partial class OptionsExtensions |
|
{ |
|
/// Sets up the parser options to work with the given Visual Studio toolchain. |
|
public static void SetupMSVC(this ParserOptions options, |
|
VisualStudioVersion vsVersion = VisualStudioVersion.Latest) |
|
{ |
|
options.MicrosoftMode = true; |
|
options.NoBuiltinIncludes = true; |
|
options.NoStandardIncludes = true; |
|
options.Abi = CppAbi.Microsoft; |
|
options.ToolSetToUse = MSVCToolchain.GetCLVersion(vsVersion) * 10000000; |
|
|
|
options.addArguments("-fms-extensions"); |
|
options.addArguments("-fms-compatibility"); |
|
options.addArguments("-fdelayed-template-parsing"); |
|
|
|
var includes = MSVCToolchain.GetSystemIncludes(vsVersion); |
|
foreach (var include in includes) |
|
options.addSystemIncludeDirs(include); |
|
} |
|
} |
|
}
|
|
|