diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2022-06-08 13:40:09 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-06-08 13:40:09 -0400 |
| commit | ff2ae7e0c1b48fa222f14dc84f15d0178ed056a1 (patch) | |
| tree | c4a3ab1e3441ec40267125086e511ef05342a547 /source | |
| parent | 8e6e884eca5b33218a8cb2714266fb6ed4548d75 (diff) | |
Improvements around Visual Studio versions/matching versions (#2267)
* #include an absolute path didn't work - because paths were taken to always be relative.
* Use TerminatedUnownedStringSlice for literals in output C++.
* Remove Escape/Unescape functions used in slang-token-reader.cpp
Add target type of 'host-cpp' etc to map to the target types.
* Fix some corner cases around string encoding.
* Added unit test for string escaping.
Fixed some assorted escaping bugs.
* Updated test output.
* Added decode test.
* Stop using hex output, to get around 'greedy' aspect. Use octal instead.
* Added HostHostCallable
Small changes to use ArtifactDesc/Info instead of large switches.
* Fix C++ emit to handle arbitrary function export.
* Add options handling for callable without an output being specified.
* Can compile with COM interface. Added example using com interface.
* Use the IR Ptr type instead of hack in C++ emit for interfaces.
* Fix issue with outputting the COM call when ptr is used.
* Fix crash issue on compilation failure.
* Add support for __global.
* Added `ActualGlobalRate`
Added special handling around globals and COM interfaces.
Tested out in cpu-com-example.
* Fix typo in NodeBase.
* Support for accessing globals by name working.
* Check that actual global initialization is working.
* Refactor the com replacement such that it doesn't need a cache or do anything special with GlobalVar.
* Remove context.
Only create replacement if needed.
* Split out COM host-callable into a unit-test.
* host-callable com testing on C++and llvm.
* Comment around the COM ptr replacement.
* Disable com test on vs 32 bit.
Fix C++ prelude
* Disable 32 bit targets testing com host-callable.
* Use JSON parsing to locate VS version.
* Need platform detection in C++prelude.
* Fix com host callable test for LLVM.
* WIP improments finding downstream compiler version.
* Work around for not being able to include "targetConditionals.h"
* Matching semantic versioning support.
* DownstreamMatchVersion -> DownstreamCompilerMatchVersion
Small improvements.
Diffstat (limited to 'source')
| -rw-r--r-- | source/compiler-core/slang-downstream-compiler.cpp | 132 | ||||
| -rw-r--r-- | source/compiler-core/slang-downstream-compiler.h | 36 | ||||
| -rw-r--r-- | source/compiler-core/windows/slang-win-visual-studio-util.cpp | 401 | ||||
| -rw-r--r-- | source/compiler-core/windows/slang-win-visual-studio-util.h | 37 | ||||
| -rw-r--r-- | source/core/slang-semantic-version.cpp | 153 | ||||
| -rw-r--r-- | source/core/slang-semantic-version.h | 46 |
6 files changed, 546 insertions, 259 deletions
diff --git a/source/compiler-core/slang-downstream-compiler.cpp b/source/compiler-core/slang-downstream-compiler.cpp index 1dfaea0a4..5011b42a0 100644 --- a/source/compiler-core/slang-downstream-compiler.cpp +++ b/source/compiler-core/slang-downstream-compiler.cpp @@ -519,34 +519,32 @@ SlangResult CommandLineDownstreamCompiler::compile(const CompileOptions& inOptio /* !!!!!!!!!!!!!!!!!!!!!!!!! DownstreamCompiler::Desc !!!!!!!!!!!!!!!!!!!!!!*/ -static DownstreamCompiler::Desc _calcCompiledWithDesc() +static DownstreamCompilerMatchVersion _calcCompiledVersion() { - DownstreamCompiler::Desc desc; + DownstreamCompilerMatchVersion matchVersion; #if SLANG_VC - desc = WinVisualStudioUtil::getDesc(WinVisualStudioUtil::getCompiledVersion()); + matchVersion = WinVisualStudioUtil::getCompiledVersion(); #elif SLANG_CLANG - desc.type = SLANG_PASS_THROUGH_CLANG; - desc.majorVersion = Int(__clang_major__); - desc.minorVersion = Int(__clang_minor__); + matchVersion.type = SLANG_PASS_THROUGH_CLANG; + matchVersion.matchVersion.set(Index(__clang_major__), Index(__clang_minor__)); #elif SLANG_GCC - desc.type = SLANG_PASS_THROUGH_GCC; - desc.majorVersion = Int(__GNUC__); - desc.minorVersion = Int(__GNUC_MINOR__); + matchVersion.type = SLANG_PASS_THROUGH_GCC; + matchVersion.matchVersion.set(Index(__GNUC__), Index(__GNUC_MINOR__)); #else // TODO(JS): Hmmm None is not quite the same as unknown. It works for now, but we might want to have a distinct enum for unknown. - desc.type = SLANG_PASS_THROUGH_NONE; + matchVersion.type = SLANG_PASS_THROUGH_NONE; #endif - return desc; + return matchVersion; } /* !!!!!!!!!!!!!!!!!!!!!!!!! DownstreamCompilerUtil !!!!!!!!!!!!!!!!!!!!!!*/ -const DownstreamCompiler::Desc& DownstreamCompilerUtil::getCompiledWithDesc() +DownstreamCompilerMatchVersion DownstreamCompilerUtil::getCompiledVersion() { - static DownstreamCompiler::Desc s_desc = _calcCompiledWithDesc(); - return s_desc; + static DownstreamCompilerMatchVersion s_version = _calcCompiledVersion(); + return s_version; } /* static */DownstreamCompiler* DownstreamCompilerUtil::findCompiler(const DownstreamCompilerSet* set, MatchType matchType, const DownstreamCompiler::Desc& desc) @@ -626,22 +624,70 @@ const DownstreamCompiler::Desc& DownstreamCompilerUtil::getCompiledWithDesc() return (bestIndex >= 0) ? compilers[bestIndex] : nullptr; } -/* static */DownstreamCompiler* DownstreamCompilerUtil::findClosestCompiler(const List<DownstreamCompiler*>& compilers, const DownstreamCompiler::Desc& desc) +/* static */DownstreamCompiler* DownstreamCompilerUtil::findCompiler(const List<DownstreamCompiler*>& compilers, const DownstreamCompiler::Desc& desc) { - DownstreamCompiler* compiler; + for (auto compiler : compilers) + { + if (compiler->getDesc() == desc) + { + return compiler; + } + } + return nullptr; +} - compiler = findCompiler(compilers, MatchType::MinGreaterEqual, desc); - if (compiler) +/* static */DownstreamCompiler* DownstreamCompilerUtil::findCompiler(const List<DownstreamCompiler*>& compilers, SlangPassThrough type, const SemanticVersion& version) +{ + DownstreamCompiler::Desc desc; + desc.type = type; + desc.majorVersion = version.m_major; + desc.minorVersion = version.m_minor; + return findCompiler(compilers, desc); +} + +/* static */void DownstreamCompilerUtil::findVersions(const List<DownstreamCompiler*>& compilers, SlangPassThrough type, List<SemanticVersion>& outVersions) +{ + for (auto compiler : compilers) { - return compiler; + auto desc = compiler->getDesc(); + + if (desc.type == type) + { + outVersions.add(SemanticVersion(int(desc.majorVersion), int(desc.minorVersion), 0)); + } } - compiler = findCompiler(compilers, MatchType::MinAbsolute, desc); - if (compiler) +} + +/* static */DownstreamCompiler* DownstreamCompilerUtil::findClosestCompiler(const List<DownstreamCompiler*>& compilers, const DownstreamCompilerMatchVersion& matchVersion) +{ + List<SemanticVersion> versions; + + findVersions(compilers, matchVersion.type, versions); + + if (versions.getCount() > 0) { - return compiler; + if (versions.getCount() == 1) + { + // Must be that one + return findCompiler(compilers, matchVersion.type, versions[0]); + } + + // Okay lets find the best one + auto bestVersion = MatchSemanticVersion::findAnyBest(versions.getBuffer(), versions.getCount(), matchVersion.matchVersion); + + // If one is found use it + if (bestVersion.isSet()) + { + return findCompiler(compilers, matchVersion.type, bestVersion); + } } { + // TODO(JS): + // NOTE! This may not really be appropriate, because LLVM is *not* interchangable with + // a 'normal' C++ compiler as cannot access standard libraries/headers. + // So `slang-llvm` can't be used for 'host' code. + // These compilers should be usable interchangably. The order is important, as the first one that matches will // be used, so LLVM is used before CLANG or GCC if appropriate const SlangPassThrough compatiblePassThroughs[] = @@ -651,31 +697,20 @@ const DownstreamCompiler::Desc& DownstreamCompilerUtil::getCompiledWithDesc() SLANG_PASS_THROUGH_GCC, }; - bool isCompatible = false; - for (auto passThrough : compatiblePassThroughs) - { - if (desc.type == passThrough) - { - isCompatible = true; - break; - } - } - - if (isCompatible) + // Check the version is one of the compatible types + if (makeConstArrayView(compatiblePassThroughs).indexOf(matchVersion.type) >= 0) { + // Try each compatible type in turn for (auto passThrough : compatiblePassThroughs) { - if (passThrough != desc.type) + versions.clear(); + findVersions(compilers, passThrough, versions); + + if (versions.getCount() > 0) { - DownstreamCompiler::Desc compatible; - - compatible.type = passThrough; - // Find the latest version. - compiler = findCompiler(compilers, MatchType::Newest, compatible); - if (compiler) - { - return compiler; - } + // Get the latest version (as we have no way to really compare) + auto latestVersion = SemanticVersion::getLatest(versions.getBuffer(), versions.getCount()); + return findCompiler(compilers, matchVersion.type, latestVersion); } } } @@ -684,16 +719,11 @@ const DownstreamCompiler::Desc& DownstreamCompilerUtil::getCompiledWithDesc() return nullptr; } -/* static */DownstreamCompiler* DownstreamCompilerUtil::findClosestCompiler(const DownstreamCompilerSet* set, const DownstreamCompiler::Desc& desc) +/* static */DownstreamCompiler* DownstreamCompilerUtil::findClosestCompiler(const DownstreamCompilerSet* set, const DownstreamCompilerMatchVersion& matchVersion) { - DownstreamCompiler* compiler = set->getCompiler(desc); - if (compiler) - { - return compiler; - } List<DownstreamCompiler*> compilers; set->getCompilers(compilers); - return findClosestCompiler(compilers, desc); + return findClosestCompiler(compilers, matchVersion); } /* static */void DownstreamCompilerUtil::updateDefault(DownstreamCompilerSet* set, SlangSourceLanguage sourceLanguage) @@ -708,7 +738,7 @@ const DownstreamCompiler::Desc& DownstreamCompilerUtil::getCompiledWithDesc() // Find the compiler closest to the compiler this was compiled with if (!compiler) { - compiler = findClosestCompiler(set, getCompiledWithDesc()); + compiler = findClosestCompiler(set, getCompiledVersion()); } break; } diff --git a/source/compiler-core/slang-downstream-compiler.h b/source/compiler-core/slang-downstream-compiler.h index db89eeaf1..9d58f6678 100644 --- a/source/compiler-core/slang-downstream-compiler.h +++ b/source/compiler-core/slang-downstream-compiler.h @@ -154,6 +154,21 @@ protected: ComPtr<ISlangBlob> m_blob; }; +// Combination of a downstream compiler type (pass through) and +// a match version. +struct DownstreamCompilerMatchVersion +{ + DownstreamCompilerMatchVersion(SlangPassThrough inType, MatchSemanticVersion inMatchVersion): + type(inType), + matchVersion(inMatchVersion) + {} + + DownstreamCompilerMatchVersion():type(SLANG_PASS_THROUGH_NONE) {} + + SlangPassThrough type; ///< The type of the compiler + MatchSemanticVersion matchVersion; ///< The match version +}; + class DownstreamCompiler: public RefObject { public: @@ -190,6 +205,8 @@ public: Info infos[int(SLANG_PASS_THROUGH_COUNT_OF)]; }; + + // Compiler description struct Desc { typedef Desc ThisType; @@ -208,11 +225,17 @@ public: /// Ctor explicit Desc(SlangPassThrough inType = SLANG_PASS_THROUGH_NONE, Int inMajorVersion = 0, Int inMinorVersion = 0):type(inType), majorVersion(inMajorVersion), minorVersion(inMinorVersion) {} + explicit Desc(SlangPassThrough inType, const SemanticVersion& version):type(inType), majorVersion(version.m_major), minorVersion(version.m_minor) {} + SlangPassThrough type; ///< The type of the compiler + + /// TODO(JS): Would probably be better if changed to SemanticVersion, but not trivial to change + // because this type is part of the DownstreamCompiler interface, which is used with `slang-llvm`. Int majorVersion; ///< Major version (interpretation is type specific) Int minorVersion; ///< Minor version (interpretation is type specific) }; + enum class OptimizationLevel { None, ///< Don't optimize at all. @@ -526,12 +549,19 @@ struct DownstreamCompilerUtil: public DownstreamCompilerBaseUtil static DownstreamCompiler* findCompiler(const DownstreamCompilerSet* set, MatchType matchType, const DownstreamCompiler::Desc& desc); static DownstreamCompiler* findCompiler(const List<DownstreamCompiler*>& compilers, MatchType matchType, const DownstreamCompiler::Desc& desc); + static DownstreamCompiler* findCompiler(const List<DownstreamCompiler*>& compilers, SlangPassThrough type, const SemanticVersion& version); + static DownstreamCompiler* findCompiler(const List<DownstreamCompiler*>& compilers, const DownstreamCompiler::Desc& desc); + + /// Find all the compilers with the version + static void findVersions(const List<DownstreamCompiler*>& compilers, SlangPassThrough compiler, List<SemanticVersion>& versions); + + /// Find the compiler closest to the desc - static DownstreamCompiler* findClosestCompiler(const List<DownstreamCompiler*>& compilers, const DownstreamCompiler::Desc& desc); - static DownstreamCompiler* findClosestCompiler(const DownstreamCompilerSet* set, const DownstreamCompiler::Desc& desc); + static DownstreamCompiler* findClosestCompiler(const List<DownstreamCompiler*>& compilers, const DownstreamCompilerMatchVersion& version); + static DownstreamCompiler* findClosestCompiler(const DownstreamCompilerSet* set, const DownstreamCompilerMatchVersion& version); /// Get the information on the compiler used to compile this source - static const DownstreamCompiler::Desc& getCompiledWithDesc(); + static DownstreamCompilerMatchVersion getCompiledVersion(); static void updateDefault(DownstreamCompilerSet* set, SlangSourceLanguage sourceLanguage); static void updateDefaults(DownstreamCompilerSet* set); diff --git a/source/compiler-core/windows/slang-win-visual-studio-util.cpp b/source/compiler-core/windows/slang-win-visual-studio-util.cpp index 9b175f308..b0b634d72 100644 --- a/source/compiler-core/windows/slang-win-visual-studio-util.cpp +++ b/source/compiler-core/windows/slang-win-visual-studio-util.cpp @@ -32,8 +32,6 @@ namespace Slang { namespace { // anonymous -typedef WinVisualStudioUtil::Version Version; - struct RegistryInfo { const char* regName; ///< The name of the entry in the registry @@ -42,7 +40,7 @@ struct RegistryInfo struct VersionInfo { - Version version; ///< The version + SemanticVersion version; ///< The version const char* name; ///< The name of the registry key }; @@ -75,13 +73,28 @@ static SlangResult _readRegistryKey(const char* path, const char* keyName, Strin } // Make easier to set up the array -static Version _makeVersion(int main, int dot = 0) { return WinVisualStudioUtil::makeVersion(main, dot); } + +static DownstreamCompilerMatchVersion _makeVersion(int main) +{ + DownstreamCompilerMatchVersion version; + version.type = SLANG_PASS_THROUGH_VISUAL_STUDIO; + version.matchVersion.set(main); + return version; +} + +static DownstreamCompilerMatchVersion _makeVersion(int main, int dot) +{ + DownstreamCompilerMatchVersion version; + version.type = SLANG_PASS_THROUGH_VISUAL_STUDIO; + version.matchVersion.set(main, dot); + return version; +} VersionInfo _makeVersionInfo(const char* name, int high, int dot = 0) { VersionInfo info; info.name = name; - info.version = WinVisualStudioUtil::makeVersion(high, dot); + info.version = SemanticVersion(high, dot); return info; } @@ -110,36 +123,41 @@ static const RegistryInfo s_regInfos[] = {"SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", "VC\\Auxiliary\\Build\\" }, }; -static bool _canUseVSWhere(Version version) +static bool _canUseVSWhere(SemanticVersion version) { // If greater than 15.0 we can use vswhere tool - return (int(version) >= int(_makeVersion(15))); + return version.m_major >= 15; } -static int _getRegistryKeyIndex(Version version) +static int _getRegistryKeyIndex(const SemanticVersion& version) { - if (int(version) >= int(_makeVersion(15))) + if (version.m_major >= 15) { return 1; } return 0; } -/* static */void WinVisualStudioUtil::getVersions(List<Version>& outVersions) +static SlangResult _parseVersion(UnownedStringSlice versionString, SemanticVersion& outVersion) { - const int count = SLANG_COUNT_OF(s_versionInfos); - outVersions.setCount(count); - - Version* dst = outVersions.begin(); - for (int i = 0; i < count; ++i) + // We only want the first 2 semantic numbers as 3rd looks like a build number, and too large + List<UnownedStringSlice> slices; + StringUtil::split(versionString, '.', slices); + if (slices.getCount() >= 2) { - dst[i] = s_versionInfos[i].version; + versionString = UnownedStringSlice(versionString.begin(), slices[1].end()); } + + // Extract the version + SemanticVersion semanticVersion; + return SemanticVersion::parse(versionString, outVersion); } -/* static */WinVisualStudioUtil::Version WinVisualStudioUtil::getCompiledVersion() + +/* static */DownstreamCompilerMatchVersion WinVisualStudioUtil::getCompiledVersion() { // Get the version of visual studio used to compile this source + // Not const, because otherwise we get an warning/error about constant expression... uint32_t version = _MSC_VER; switch (version) @@ -149,42 +167,65 @@ static int _getRegistryKeyIndex(Version version) 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); - } default: break; } // Seems like versions go in runs of 10 at this point // https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170 - - if (version >= 1920 && version < 1930) + // https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?redirectedfrom=MSDN&view=msvc-170 + if (version >= 1900 && version < 1910) { - return _makeVersion(16); + return _makeVersion(14); + } + else if (version >= 1910 && version < 1920) + { + switch (version) + { + case 1910: return _makeVersion(15, 0); + case 1911: return _makeVersion(15, 3); + case 1912: return _makeVersion(15, 5); + case 1913: return _makeVersion(15, 6); + case 1914: return _makeVersion(15, 7); + case 1915: return _makeVersion(15, 8); + case 1916: return _makeVersion(15, 9); + default: return _makeVersion(15); + } + } + else if (version >= 1920 && version < 1930) + { + switch (version) + { + case 1920: return _makeVersion(16, 0); + case 1921: return _makeVersion(16, 1); + case 1922: return _makeVersion(16, 2); + case 1923: return _makeVersion(16, 3); + case 1924: return _makeVersion(16, 4); + case 1925: return _makeVersion(16, 5); + case 1926: return _makeVersion(16, 6); + case 1927: return _makeVersion(16, 7); + case 1928: return _makeVersion(16, 9); + case 1929: return _makeVersion(16, 11); + default: return _makeVersion(16); + } } else if (version >= 1930 && version < 1940) { - // We are going to assume it's a run of t0 - return _makeVersion(17); + switch (version) + { + case 1930: return _makeVersion(17, 0); + case 1931: return _makeVersion(17, 1); + case 1932: return _makeVersion(17, 2); + default: return _makeVersion(17); + } } else if (version >= 1940) { // Its an unknown newer version - return Version::Future; + return DownstreamCompilerMatchVersion(SLANG_PASS_THROUGH_VISUAL_STUDIO, MatchSemanticVersion::makeFuture()); } // Unknown version - return Version::Unknown; + return DownstreamCompilerMatchVersion(SLANG_PASS_THROUGH_VISUAL_STUDIO, MatchSemanticVersion()); } static SlangResult _parseJson(const String& contents, DiagnosticSink* sink, JSONContainer* container, JSONValue& outRoot) @@ -206,180 +247,217 @@ static SlangResult _parseJson(const String& contents, DiagnosticSink* sink, JSON return SLANG_OK; } -static SlangResult _find(int versionIndex, WinVisualStudioUtil::VersionPath& outPath) +static void _orderVersions(List<WinVisualStudioUtil::VersionPath>& ioVersions) { - const auto& versionInfo = s_versionInfos[versionIndex]; + typedef WinVisualStudioUtil::VersionPath VersionPath; + // Put into increasing version order, from oldest to newest + ioVersions.sort([&](const VersionPath& a, const VersionPath& b) -> bool { return a.version < b.version; }); +} - auto version = versionInfo.version; +static SlangResult _findVersionsWithVSWhere(const VersionInfo* versionInfo, List<WinVisualStudioUtil::VersionPath>& outVersions) +{ + typedef WinVisualStudioUtil::VersionPath VersionPath; - outPath.version = version; - outPath.vcvarsPath = String(); + CommandLine cmd; - 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(); - // 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); - char programFilesPath[_MAX_PATH]; - SHGetFolderPathA(hwnd, CSIDL_PROGRAM_FILESX86, NULL, 0, programFilesPath); + String vswherePath = programFilesPath; + vswherePath.append("\\Microsoft Visual Studio\\Installer\\vswhere"); - String vswherePath = programFilesPath; - vswherePath.append("\\Microsoft Visual Studio\\Installer\\vswhere"); + cmd.setExecutableLocation(ExecutableLocation(vswherePath)); - cmd.setExecutableLocation(ExecutableLocation(vswherePath)); + // Using -? we can find out vswhere options. - const auto desc = WinVisualStudioUtil::getDesc(version); + // Previous args - works but returns multiple versions, without listing what version is associated with which path + // or the order. + //String args[] = { "-version", versionName, "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", ""-property", "installationPath" }; - StringBuilder versionName; - WinVisualStudioUtil::append(version, versionName); + // Use JSON parsing, we can verify the versions for a path, otherwise multiple versions are returned + // not just the version specified. The ordering isn't defined (and -sort doesn't appear to work) - // Using -? we can find out vswhere options. - - // Previous args - works but returns multiple versions, without listing what version is associated with which path - // or the order. - //String args[] = { "-version", versionName, "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "-json", "-property", "installationPath", "-property", "installationVersion" }; + SemanticVersion requiredVersion; + if (versionInfo) + { + StringBuilder versionName; + versionInfo->version.append(versionName); - // Use JSON parsing, we can verify the versions for a path, otherwise multiple versions are returned - // not just the version specified. The ordering isn't defined (and -sort doesn't appear to work) - String args[] = { "-version", versionName, "-format", "json", "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64"}; + cmd.addArg("-version"); + cmd.addArg(versionName); + } + // Add other args + { + // TODO(JS): + // For arm targets will probably need something different for tooling + String args[] = { "-format", "json", "-utf8", "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64" }; cmd.addArgs(args, SLANG_COUNT_OF(args)); + } - SourceManager sourceManager; - sourceManager.initialize(nullptr, nullptr); - DiagnosticSink sink(&sourceManager, nullptr); - - RefPtr<JSONContainer> container = new JSONContainer(&sourceManager); - - ExecuteResult exeRes; - if (SLANG_SUCCEEDED(ProcessUtil::execute(cmd, exeRes))) - { - JSONValue jsonRoot; - SLANG_RETURN_ON_FAIL(_parseJson(exeRes.standardOutput, &sink, container, jsonRoot)); - - // Search through the array... - if (jsonRoot.getKind() != JSONValue::Kind::Array) - { - return SLANG_FAIL; - } - - auto arr = container->getArray(jsonRoot); - - const auto pathKey = container->getKey(UnownedStringSlice::fromLiteral("installationPath")); - const auto versionKey = container->getKey(UnownedStringSlice::fromLiteral("installationVersion")); + // We are going to use JSON parser to extract the info + SourceManager sourceManager; + sourceManager.initialize(nullptr, nullptr); + DiagnosticSink sink(&sourceManager, nullptr); - for (auto elem : arr) - { - // Get the path and the name - if (elem.getKind() != JSONValue::Kind::Object) - { - continue; - } + RefPtr<JSONContainer> container = new JSONContainer(&sourceManager); - auto pathJsonValue = container->findObjectValue(elem, pathKey); - auto versionJsonValue = container->findObjectValue(elem, versionKey); + ExecuteResult exeRes; + SLANG_RETURN_ON_FAIL(ProcessUtil::execute(cmd, exeRes)); - if (!pathJsonValue.isValid() || !versionJsonValue.isValid()) - { - continue; - } + JSONValue jsonRoot; + SLANG_RETURN_ON_FAIL(_parseJson(exeRes.standardOutput, &sink, container, jsonRoot)); - auto pathString = container->getString(pathJsonValue); - auto versionString = container->getString(versionJsonValue).trim(); + // Search through the array... + if (jsonRoot.getKind() != JSONValue::Kind::Array) + { + return SLANG_FAIL; + } - // If the versionString matches - List<UnownedStringSlice> versionSlices; - StringUtil::split(versionString, '.', versionSlices); + auto arr = container->getArray(jsonRoot); - if (versionSlices.getCount() <= 0) - { - continue; - } + const auto pathKey = container->getKey(UnownedStringSlice::fromLiteral("installationPath")); + const auto versionKey = container->getKey(UnownedStringSlice::fromLiteral("installationVersion")); - Int versionValue; - SLANG_RETURN_ON_FAIL(StringUtil::parseInt(versionSlices[0], versionValue)); + // Find all the versions, that match + for (auto elem : arr) + { + // Get the path and the name + if (elem.getKind() != JSONValue::Kind::Object) + { + continue; + } - if (versionValue != desc.majorVersion) - { - continue; - } + auto pathJsonValue = container->findObjectValue(elem, pathKey); + auto versionJsonValue = container->findObjectValue(elem, versionKey); - outPath.vcvarsPath = pathString; - outPath.vcvarsPath.append("\\VC\\Auxiliary\\Build\\"); - return SLANG_OK; - } + if (!pathJsonValue.isValid() || !versionJsonValue.isValid()) + { + continue; } - } - const Int keyIndex = _getRegistryKeyIndex(version); - if (keyIndex >= 0) - { - SLANG_ASSERT(keyIndex < SLANG_COUNT_OF(s_regInfos)); + auto pathString = container->getString(pathJsonValue); + auto versionString = container->getString(versionJsonValue).trim(); - // Try reading the key - const auto& keyInfo = s_regInfos[keyIndex]; + // Extract the version + SemanticVersion semanticVersion; + if (SLANG_SUCCEEDED(_parseVersion(versionString, semanticVersion))) + { + if (!requiredVersion.isSet() || requiredVersion.m_major == semanticVersion.m_major) + { + WinVisualStudioUtil::VersionPath versionPath; - StringBuilder keyName; - WinVisualStudioUtil::append(versionInfo.version, keyName); + versionPath.vcvarsPath = pathString; + versionPath.vcvarsPath.append("\\VC\\Auxiliary\\Build\\"); + versionPath.version = semanticVersion; - String value; - if (SLANG_SUCCEEDED(_readRegistryKey(keyInfo.regName, keyName.getBuffer(), value))) - { - outPath.vcvarsPath = value; - return SLANG_OK; + outVersions.add(versionPath); + } } } - return SLANG_FAIL; + return SLANG_OK; } -/* static */SlangResult WinVisualStudioUtil::find(List<VersionPath>& outVersionPaths) +static SlangResult _findVersionsWithRegistery(List<WinVisualStudioUtil::VersionPath>& outVersions) { - outVersionPaths.clear(); + typedef WinVisualStudioUtil::VersionPath VersionPath; const int versionCount = SLANG_COUNT_OF(s_versionInfos); for (int i = versionCount - 1; i >= 0; --i) { - VersionPath versionPath; - if (SLANG_SUCCEEDED(_find(i, versionPath))) + const auto versionInfo = s_versionInfos[i]; + + auto version = versionInfo.version; + + // Try locating via the registry + const Int keyIndex = _getRegistryKeyIndex(version); + if (keyIndex >= 0) { - outVersionPaths.add(versionPath); + SLANG_ASSERT(keyIndex < SLANG_COUNT_OF(s_regInfos)); + + // Try reading the key + const auto& keyInfo = s_regInfos[keyIndex]; + + StringBuilder keyName; + versionInfo.version.append(keyName); + + String value; + if (SLANG_SUCCEEDED(_readRegistryKey(keyInfo.regName, keyName.getBuffer(), value))) + { + VersionPath versionPath; + versionPath.version = versionInfo.version; + versionPath.vcvarsPath = value; + + // Append + if (keyInfo.pathFix && keyInfo.pathFix[0] != 0) + { + versionPath.vcvarsPath.append(keyInfo.pathFix); + } + + outVersions.add(versionPath); + } } } return SLANG_OK; } -/* static */SlangResult WinVisualStudioUtil::find(Version version, VersionPath& outPath) +/* static */SlangResult WinVisualStudioUtil::find(List<VersionPath>& outVersionPaths) { - const int versionCount = SLANG_COUNT_OF(s_versionInfos); + outVersionPaths.clear(); + + List<VersionPath> regVersions; - for (int i = 0; i < versionCount; ++i) + // Find all versions with vswhere + _findVersionsWithVSWhere(nullptr, outVersionPaths); + // Find all with the registry + _findVersionsWithRegistery(regVersions); + + // Merge + for (const auto& regVersion : regVersions) { - const auto& versionInfo = s_versionInfos[i]; - if (versionInfo.version == version) + Index foundIndex = -1; + if (_canUseVSWhere(regVersion.version)) + { + // If there is a major version already from vswhere, we don't need to merge + const auto majorVersion = regVersion.version.m_major; + foundIndex = outVersionPaths.findFirstIndex([&](const VersionPath& cur) -> bool { return cur.version.m_major == majorVersion; }); + } + else { - return _find(i, outPath); + // See if we can find the exact version + foundIndex = outVersionPaths.findFirstIndex([&](const VersionPath& cur) -> bool { return cur.version == regVersion.version; }); + } + + // If it wasn't found add it. + if (foundIndex < 0) + { + outVersionPaths.add(regVersion); } } - return SLANG_FAIL; + // Sort + _orderVersions(outVersionPaths); + return SLANG_OK; } /* static */SlangResult WinVisualStudioUtil::find(DownstreamCompilerSet* set) { - const int versionCount = SLANG_COUNT_OF(s_versionInfos); + List<VersionPath> versionPaths; + SLANG_RETURN_ON_FAIL(find(versionPaths)); - for (int i = versionCount - 1; i >= 0; --i) + for (const auto& versionPath : versionPaths) { - const auto& versionInfo = s_versionInfos[i]; - auto desc = getDesc(versionInfo.version); + // Turn into a desc + const DownstreamCompiler::Desc desc(SLANG_PASS_THROUGH_VISUAL_STUDIO, versionPath.version); - VersionPath versionPath; - if (!set->getCompiler(desc) && SLANG_SUCCEEDED(_find(i, versionPath))) + // If not in set add it + if (!set->getCompiler(desc)) { RefPtr<CommandLineDownstreamCompiler> compiler = new VisualStudioDownstreamCompiler(desc); calcExecuteCompilerArgs(versionPath, compiler->m_cmdLine); @@ -431,25 +509,4 @@ static SlangResult _find(int versionIndex, WinVisualStudioUtil::VersionPath& out 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 diff --git a/source/compiler-core/windows/slang-win-visual-studio-util.h b/source/compiler-core/windows/slang-win-visual-studio-util.h index 05a3ea061..4dfac8f34 100644 --- a/source/compiler-core/windows/slang-win-visual-studio-util.h +++ b/source/compiler-core/windows/slang-win-visual-studio-util.h @@ -12,24 +12,15 @@ namespace Slang { struct WinVisualStudioUtil { - enum class Version: uint32_t - { - Unknown = 0, ///< This is an unknown (and not later) version - Future = 0xff * 10, ///< This is a version 'from the future' - that isn't specifically known. Will be treated as latest - }; - struct VersionPath { - Version version; ///< The visual studio version - String vcvarsPath; ///< The path to vcvars bat files, that need to be executed before executing the compiler + SemanticVersion version; ///< The visual studio version + String vcvarsPath; ///< The path to `vcvars.bat` files, that need to be executed before executing the compiler }; /// Find all the installations static SlangResult find(List<VersionPath>& outVersionPaths); - /// Given a version find it's path - static SlangResult find(Version version, VersionPath& outPath); - /// Find and add to the set (if not already there) static SlangResult find(DownstreamCompilerSet* set); @@ -39,28 +30,8 @@ struct WinVisualStudioUtil /// Run visual studio on specified path with the parameters specified on the command line. Output placed in outResult. static SlangResult executeCompiler(const VersionPath& versionPath, const CommandLine& commandLine, ExecuteResult& outResult); - /// Get all the known version numbers - static void getVersions(List<Version>& outVersions); - - /// Gets the msc compiler used to compile this version. Returning Version(0) means unknown - static Version getCompiledVersion(); - - /// Create a version from a high and low indices - static Version makeVersion(int high, int low = 0) { SLANG_ASSERT(low >= 0 && low <= 9); return Version(high * 10 + low); } - - /// Convert a version number into a string - static void append(Version version, StringBuilder& outBuilder); - - /// Get version as desc - static DownstreamCompiler::Desc getDesc(Version version) - { - DownstreamCompiler::Desc desc; - desc.type = SLANG_PASS_THROUGH_VISUAL_STUDIO; - desc.majorVersion = Int(version) / 10; - desc.minorVersion = Int(version) % 10; - return desc; - } - + /// Gets the msc compiler used to compile this version. + static DownstreamCompilerMatchVersion getCompiledVersion(); }; } // namespace Slang diff --git a/source/core/slang-semantic-version.cpp b/source/core/slang-semantic-version.cpp index 7f603fd9c..cc631d292 100644 --- a/source/core/slang-semantic-version.cpp +++ b/source/core/slang-semantic-version.cpp @@ -7,6 +7,8 @@ namespace Slang { +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! SemanticVersion !!!!!!!!!!!!!!!!!!!!!!!!!!!!! + SlangResult SemanticVersion::parse(const UnownedStringSlice& value, char separatorChar, SemanticVersion& outVersion) { outVersion.reset(); @@ -52,4 +54,155 @@ void SemanticVersion::append(StringBuilder& buf) const } } +/* static */SemanticVersion SemanticVersion::getEarliest(const ThisType* versions, Count count) +{ + if (count <= 0) + { + return SemanticVersion(); + } + + SemanticVersion bestVersion = versions[0]; + for (const auto version : makeConstArrayView(versions + 1, count - 1)) + { + if (version < bestVersion) + { + bestVersion = version; + } + } + return bestVersion; +} + +/* static */SemanticVersion SemanticVersion::getLatest(const ThisType* versions, Count count) +{ + if (count <= 0) + { + return SemanticVersion(); + } + + SemanticVersion bestVersion = versions[0]; + for (const auto version : makeConstArrayView(versions + 1, count - 1)) + { + if (version > bestVersion) + { + bestVersion = version; + } + } + return bestVersion; +} + + + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! MatchSemanticVersion !!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +/* static */SemanticVersion MatchSemanticVersion::findAnyBest(const SemanticVersion* versions, Count count, const ThisType& matchVersion) +{ + // If there aren't any we are done + if (count <= 0) + { + return SemanticVersion(); + } + + // If there is only one it must be the best + if (count == 1) + { + return versions[0]; + } + + // Define a version range [start, end) + SemanticVersion start, end; + + switch (matchVersion.m_kind) + { + case Kind::Past: + { + return SemanticVersion::getEarliest(versions, count); + } + case Kind::Unknown: + case Kind::Future: + { + // If it's unknown, we just get the latest + return SemanticVersion::getLatest(versions, count); + } + case Kind::Major: + { + start = SemanticVersion(matchVersion.m_version.m_major, 0, 0); + end = SemanticVersion(matchVersion.m_version.m_major + 1, 0, 0); + break; + } + case Kind::MajorMinor: + { + start = SemanticVersion(matchVersion.m_version.m_major, matchVersion.m_version.m_minor, 0); + end = SemanticVersion(matchVersion.m_version.m_major, matchVersion.m_version.m_minor + 1, 0); + break; + } + case Kind::MajorMinorPatch: + { + start = SemanticVersion(matchVersion.m_version); + end = SemanticVersion(matchVersion.m_version.m_major, matchVersion.m_version.m_minor, matchVersion.m_version.m_patch + 1); + break; + } + default: break; + } + + List<SemanticVersion> sortedVersions; + sortedVersions.addRange(versions, count); + + // Sort into increasing values + sortedVersions.sort([&](const SemanticVersion& a, const SemanticVersion& b) -> bool { return a < b; }); + + Index startIndex = 0; + for (; startIndex < count && sortedVersions[startIndex] < start; ++startIndex); + + Index endIndex = startIndex; + for (; endIndex < count && sortedVersions[endIndex] < end; ++endIndex); + + // If we have a span of versions, get the last in the span + if (startIndex < endIndex) + { + // Get the last one + return sortedVersions[endIndex - 1]; + } + + // Get the next greatest if there is one + if (endIndex < count) + { + return sortedVersions[endIndex]; + } + + // Get the prior prior to the start + if (startIndex > 0) + { + return sortedVersions[startIndex - 1]; + } + + // All cases should be covered, but return the last one + return sortedVersions[count - 1]; +} + +void MatchSemanticVersion::append(StringBuilder& buf) const +{ + switch (m_kind) + { + default: + case Kind::Unknown: buf << "unknown"; break; + case Kind::Past: buf << "past"; break; + case Kind::Future: buf << "future"; break; + case Kind::Major: + { + buf << m_version.m_major; + break; + } + case Kind::MajorMinor: + { + buf << m_version.m_major << "." << m_version.m_minor; + break; + } + case Kind::MajorMinorPatch: + { + m_version.append(buf); + break; + } + } +} + } // namespace Slang diff --git a/source/core/slang-semantic-version.h b/source/core/slang-semantic-version.h index d33116de6..4d64627c2 100644 --- a/source/core/slang-semantic-version.h +++ b/source/core/slang-semantic-version.h @@ -47,6 +47,9 @@ struct SemanticVersion static SlangResult parse(const UnownedStringSlice& value, SemanticVersion& outVersion); static SlangResult parse(const UnownedStringSlice& value, char separatorChar, SemanticVersion& outVersion); + static ThisType getEarliest(const ThisType* versions, Count count); + static ThisType getLatest(const ThisType* versions, Count count); + void append(StringBuilder& buf) const; bool operator>(const ThisType& rhs) const { return toInteger() > rhs.toInteger(); } @@ -63,5 +66,48 @@ struct SemanticVersion uint16_t m_patch; }; +/* Adds to the semantic versioning information for an incomplete version that can be matched */ +struct MatchSemanticVersion +{ + typedef MatchSemanticVersion ThisType; + + enum class Kind + { + Unknown, ///< Not known + Past, ///< Some unknown past version + Future, ///< Some future unknown version + Major, ///< Major version is defined (minor is in effect undefined) + MajorMinor, ///< Major and minor version are defined + MajorMinorPatch, ///< All elements of semantic version are defined + }; + + /// True if has a complete version + bool hasCompleteVersion() const { return m_kind == Kind::MajorMinorPatch; } + /// True if has some version information + bool hasVersion() const { return Index(m_kind) >= Index(Kind::Major); } + + void set(Index major) { m_kind = Kind::Major; m_version = SemanticVersion(int(major), 0, 0); } + void set(Index major, Index minor) { m_kind = Kind::MajorMinor; m_version = SemanticVersion(int(major), int(minor), 0); } + void set(Index major, Index minor, Index patch) { m_kind = Kind::MajorMinorPatch; m_version = SemanticVersion(int(major), int(minor), int(patch)); } + + void append(StringBuilder& buf) const; + + static MatchSemanticVersion makeFuture() { MatchSemanticVersion version; version.m_kind = Kind::Future; return version; } + + /// Finds the 'best' version based on the versions passed. + /// Doesn't follow strict semantic rules as will attempt to return the closest 'any' in past or future + /// If none can be found, returns an empty semantic version + static SemanticVersion findAnyBest(const SemanticVersion* versions, Count count, const ThisType& matchVersion); + + MatchSemanticVersion():m_kind(Kind::Unknown) {} + MatchSemanticVersion(Kind kind, const SemanticVersion& version): + m_kind(kind), + m_version(version) + {} + + Kind m_kind; + SemanticVersion m_version; +}; + } #endif |
