diff options
Diffstat (limited to 'source/core/windows/slang-win-visual-studio-util.cpp')
| -rw-r--r-- | source/core/windows/slang-win-visual-studio-util.cpp | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/source/core/windows/slang-win-visual-studio-util.cpp b/source/core/windows/slang-win-visual-studio-util.cpp new file mode 100644 index 000000000..edf33110c --- /dev/null +++ b/source/core/windows/slang-win-visual-studio-util.cpp @@ -0,0 +1,339 @@ +#include "slang-win-visual-studio-util.h" + +#include "../slang-common.h" +#include "../slang-process-util.h" +#include "../slang-string-util.h" + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# define NOMINMAX +# include <Windows.h> +# undef WIN32_LEAN_AND_MEAN +# undef NOMINMAX + +# include <Shlobj.h> + +#endif + +// The method used to invoke VS was originally inspired by some ideas in +// https://github.com/RuntimeCompiledCPlusPlus/RuntimeCompiledCPlusPlus/ + +namespace Slang { + +// Information on VS versioning can be found here +// https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering + + +namespace { // anonymous + +typedef WinVisualStudioUtil::Version Version; + +struct RegistryInfo +{ + const char* regName; ///< The name of the entry in the registry + const char* pathFix; ///< With the value from the registry how to fix the path +}; + +struct VersionInfo +{ + Version version; ///< The version + const char* name; ///< The name of the registry key +}; + +} // anonymous + + +static SlangResult _readRegistryKey(const char* path, const char* keyName, String& outString) +{ + // https://docs.microsoft.com/en-us/windows/desktop/api/winreg/nf-winreg-regopenkeyexa + HKEY key; + LONG ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, path, 0, KEY_READ | KEY_WOW64_32KEY, &key); + if (ret != ERROR_SUCCESS) + { + return SLANG_FAIL; + } + + char value[MAX_PATH]; + DWORD size = MAX_PATH; + + // https://docs.microsoft.com/en-us/windows/desktop/api/winreg/nf-winreg-regqueryvalueexa + ret = RegQueryValueExA(key, keyName, nullptr, nullptr, (LPBYTE)value, &size); + RegCloseKey(key); + + if (ret != ERROR_SUCCESS) + { + return SLANG_FAIL; + } + + outString = value; + return SLANG_OK; +} + +// Make easier to set up the array +static Version _makeVersion(int main, int dot = 0) { return WinVisualStudioUtil::makeVersion(main, dot); } + +VersionInfo _makeVersionInfo(const char* name, int high, int dot = 0) +{ + VersionInfo info; + info.name = name; + info.version = WinVisualStudioUtil::makeVersion(high, dot); + return info; +} + +static const VersionInfo s_versionInfos[] = +{ + _makeVersionInfo("VS 2005", 8), + _makeVersionInfo("VS 2008", 9), + _makeVersionInfo("VS 2010", 10), + _makeVersionInfo("VS 2012", 11), + _makeVersionInfo("VS 2013", 12), + _makeVersionInfo("VS 2015", 14), + _makeVersionInfo("VS 2017", 15), + _makeVersionInfo("VS 2019", 16), +}; + +// When trying to figure out how this stuff works by running regedit - care is needed, +// because what regedit displays varies on which version of regedit is used. +// In order to use the registry paths used here it's necessary to use Start/Run with +// %systemroot%\syswow64\regedit to view 32 bit keys + +static const RegistryInfo s_regInfos[] = +{ + {"SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7", "" }, + {"SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", "VC\\Auxiliary\\Build\\" }, +}; + +static bool _canUseVSWhere(Version version) +{ + // If greater than 15.0 we can use vswhere tool + return (int(version) >= int(_makeVersion(15))); +} + +static int _getRegistryKeyIndex(Version version) +{ + if (int(version) >= int(_makeVersion(15))) + { + return 1; + } + return 0; +} + +/* static */void WinVisualStudioUtil::getVersions(List<Version>& outVersions) +{ + const int count = SLANG_COUNT_OF(s_versionInfos); + outVersions.setCount(count); + + Version* dst = outVersions.begin(); + for (int i = 0; i < count; ++i) + { + dst[i] = s_versionInfos[i].version; + } +} + +/* static */WinVisualStudioUtil::Version WinVisualStudioUtil::getCompiledVersion() +{ + // Get the version of visual studio used to compile this source + const uint32_t version = _MSC_VER; + + switch (version) + { + case 1400: return _makeVersion(8); + case 1500: return _makeVersion(9); + case 1600: return _makeVersion(10); + case 1700: return _makeVersion(11); + case 1800: return _makeVersion(12); + case 1900: + { + return _makeVersion(14); + } + case 1911: + case 1912: + case 1913: + case 1914: + case 1915: + case 1916: + { + return _makeVersion(15); + } + case 1920: + { + return _makeVersion(16); + } + default: + { + int lastKnownVersion = 1920; + if (version > lastKnownVersion) + { + // Its an unknown newer version + return Version::Future; + } + break; + } + } + + // Unknown version + return Version::Unknown; +} + +static SlangResult _find(int versionIndex, WinVisualStudioUtil::VersionPath& outPath) +{ + const auto& versionInfo = s_versionInfos[versionIndex]; + + auto version = versionInfo.version; + + outPath.version = version; + outPath.vcvarsPath = String(); + + if (_canUseVSWhere(version)) + { + CommandLine cmd; + + // Lookup directly %ProgramFiles(x86)% path + // https://docs.microsoft.com/en-us/windows/desktop/api/shlobj_core/nf-shlobj_core-shgetfolderpatha + HWND hwnd = GetConsoleWindow(); + + char programFilesPath[_MAX_PATH]; + SHGetFolderPathA(hwnd, CSIDL_PROGRAM_FILESX86, NULL, 0, programFilesPath); + + String vswherePath = programFilesPath; + vswherePath.append("\\Microsoft Visual Studio\\Installer\\vswhere"); + + cmd.setExecutableFilename(vswherePath); + + StringBuilder versionName; + WinVisualStudioUtil::append(version, versionName); + + String args[] = { "-version", versionName, "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "-property", "installationPath" }; + cmd.addArgs(args, SLANG_COUNT_OF(args)); + + ExecuteResult exeRes; + if (SLANG_SUCCEEDED(ProcessUtil::execute(cmd, exeRes))) + { + // We need to chopoff CR/LF if there is one + List<UnownedStringSlice> lines; + StringUtil::calcLines(exeRes.standardOutput.getUnownedSlice(), lines); + + if (lines.getCount()) + { + outPath.vcvarsPath = lines[0]; + outPath.vcvarsPath.append("\\VC\\Auxiliary\\Build\\"); + return SLANG_OK; + } + } + } + + const Int keyIndex = _getRegistryKeyIndex(version); + if (keyIndex >= 0) + { + SLANG_ASSERT(keyIndex < SLANG_COUNT_OF(s_regInfos)); + + // Try reading the key + const auto& keyInfo = s_regInfos[keyIndex]; + + StringBuilder keyName; + WinVisualStudioUtil::append(versionInfo.version, keyName); + + String value; + if (SLANG_SUCCEEDED(_readRegistryKey(keyInfo.regName, keyName.getBuffer(), value))) + { + outPath.vcvarsPath = value; + return SLANG_OK; + } + } + + return SLANG_FAIL; +} + +/* static */SlangResult WinVisualStudioUtil::find(List<VersionPath>& outVersionPaths) +{ + outVersionPaths.clear(); + + const int versionCount = SLANG_COUNT_OF(s_versionInfos); + + for (int i = versionCount - 1; i >= 0; --i) + { + VersionPath versionPath; + if (SLANG_SUCCEEDED(_find(i, versionPath))) + { + outVersionPaths.add(versionPath); + } + } + + return SLANG_OK; +} + +/* static */SlangResult WinVisualStudioUtil::find(Version version, VersionPath& outPath) +{ + const int versionCount = SLANG_COUNT_OF(s_versionInfos); + + for (int i = 0; i < versionCount; ++i) + { + const auto& versionInfo = s_versionInfos[i]; + if (versionInfo.version == version) + { + return _find(i, outPath); + } + } + return SLANG_FAIL; +} + +/* static */SlangResult WinVisualStudioUtil::executeCompiler(const VersionPath& versionPath, const CommandLine& commandLine, ExecuteResult& outResult) +{ + // To invoke cl we need to run the suitable vcvars. In order to run this we have to have MS CommandLine. + // So here we build up a cl command line that is run by first running vcvars, and then executing cl with the parameters as passed to commandLine + + StringBuilder builder; + CommandLine cmdLine; + + cmdLine.setExecutableFilename("cmd.exe"); + { + String options[] = { "/q", "/c", "@prompt", "$" }; + cmdLine.addArgs(options, SLANG_COUNT_OF(options)); + } + + cmdLine.addArg("&&"); + + { + StringBuilder path; + path << versionPath.vcvarsPath; + path << "\\vcvarsall.bat"; + cmdLine.addArg(path); + } + +#if SLANG_PTR_IS_32 + cmdLine.addArg("x86"); +#else + cmdLine.addArg("x86_amd64"); +#endif + + cmdLine.addArg("&&"); + cmdLine.addArg("cl"); + + // Append the command line options + cmdLine.addArgs(commandLine.m_args.getBuffer(), commandLine.m_args.getCount()); + + return ProcessUtil::execute(cmdLine, outResult); +} + +/* static */void WinVisualStudioUtil::append(Version version, StringBuilder& outBuilder) +{ + switch (version) + { + case Version::Unknown: + { + outBuilder << "unknown"; + } + case Version::Future: + { + outBuilder << "future"; + break; + } + default: + { + outBuilder << (int(version) / 10) << "." << (int(version) % 10); + break; + } + } +} + +} // namespace Slang |
