summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2022-06-08 13:40:09 -0400
committerGitHub <noreply@github.com>2022-06-08 13:40:09 -0400
commitff2ae7e0c1b48fa222f14dc84f15d0178ed056a1 (patch)
treec4a3ab1e3441ec40267125086e511ef05342a547 /source
parent8e6e884eca5b33218a8cb2714266fb6ed4548d75 (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.cpp132
-rw-r--r--source/compiler-core/slang-downstream-compiler.h36
-rw-r--r--source/compiler-core/windows/slang-win-visual-studio-util.cpp401
-rw-r--r--source/compiler-core/windows/slang-win-visual-studio-util.h37
-rw-r--r--source/core/slang-semantic-version.cpp153
-rw-r--r--source/core/slang-semantic-version.h46
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