summaryrefslogtreecommitdiffstats
path: root/source/core
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2021-04-01 13:39:11 -0400
committerGitHub <noreply@github.com>2021-04-01 10:39:11 -0700
commitfa31d21ba92669a521a7768467246918e3947e02 (patch)
treeaf98a593e24bc6309ac4d11a59562be4b22c93d7 /source/core
parent3f1632a1450a5879f337b4bd178e48880cd583f8 (diff)
Added compiler-core project (#1775)
* #include an absolute path didn't work - because paths were taken to always be relative. * Split out compiler-core initially with just slang-source-loc.cpp * More lexer, name, token to compiler-core. * Split Lexer and Core diagnostics. * Move slang-file-system to core. * Add slang-file-system to core. * More DownstreamCompiler into compiler-core * Fix typo. * Add compiler-core to bootstrap proj. * Small fixes to premake * For linux try with compiler-core * Remove compiler-core from examples. * Added NameConventionUtil to compiler-core * Add global function to CharUtil to *hopefully* avoid linking issue. * Hack to make linkage of CharUtil work on linux.
Diffstat (limited to 'source/core')
-rw-r--r--source/core/slang-char-util.cpp11
-rw-r--r--source/core/slang-char-util.h9
-rw-r--r--source/core/slang-downstream-compiler.cpp716
-rw-r--r--source/core/slang-downstream-compiler.h501
-rw-r--r--source/core/slang-file-system.cpp888
-rw-r--r--source/core/slang-file-system.h253
-rw-r--r--source/core/slang-gcc-compiler-util.cpp645
-rw-r--r--source/core/slang-gcc-compiler-util.h56
-rw-r--r--source/core/slang-name-convention-util.cpp213
-rw-r--r--source/core/slang-name-convention-util.h54
-rw-r--r--source/core/slang-nvrtc-compiler.cpp773
-rw-r--r--source/core/slang-nvrtc-compiler.h19
-rw-r--r--source/core/slang-string.cpp6
-rw-r--r--source/core/slang-visual-studio-compiler-util.cpp452
-rw-r--r--source/core/slang-visual-studio-compiler-util.h42
-rw-r--r--source/core/windows/slang-win-visual-studio-util.cpp366
-rw-r--r--source/core/windows/slang-win-visual-studio-util.h68
17 files changed, 1162 insertions, 3910 deletions
diff --git a/source/core/slang-char-util.cpp b/source/core/slang-char-util.cpp
index 298f9b75f..ea9e6dbf2 100644
--- a/source/core/slang-char-util.cpp
+++ b/source/core/slang-char-util.cpp
@@ -2,10 +2,8 @@
namespace Slang {
-static const CharUtil::CharFlagMap _calcCharFlagsMap()
+/* static */CharUtil::CharFlagMap CharUtil::makeCharFlagMap()
{
- typedef CharUtil::Flag Flag;
-
CharUtil::CharFlagMap map;
memset(&map, 0, sizeof(map));
@@ -48,6 +46,11 @@ static const CharUtil::CharFlagMap _calcCharFlagsMap()
return map;
}
-/* static */const CharUtil::CharFlagMap CharUtil::g_charFlagMap = _calcCharFlagsMap();
+/* static */int CharUtil::_ensureLink()
+{
+ return makeCharFlagMap().flags[0];
+}
+
+/* static */const CharUtil::CharFlagMap CharUtil::g_charFlagMap = makeCharFlagMap();
} // namespace Slang
diff --git a/source/core/slang-char-util.h b/source/core/slang-char-util.h
index 2434697d4..8f7f69c90 100644
--- a/source/core/slang-char-util.h
+++ b/source/core/slang-char-util.h
@@ -41,12 +41,19 @@ struct CharUtil
/// Given a character return the upper case equivalent
SLANG_FORCE_INLINE static char toUpper(char c) { return (c >= 'a' && c <= 'z') ? (c -'a' + 'A') : c; }
-
+
struct CharFlagMap
{
Flags flags[0x100];
};
+ static CharFlagMap makeCharFlagMap();
+
+ // HACK!
+ // JS: Many of the inlined functions of CharUtil just access a global map. That referencing this global is *NOT* enough to
+ // link correctly with CharUtil on linux for a shared library. Caling this function can force linkage.
+ static int _ensureLink();
+
static const CharFlagMap g_charFlagMap;
};
diff --git a/source/core/slang-downstream-compiler.cpp b/source/core/slang-downstream-compiler.cpp
deleted file mode 100644
index 1a81fcac8..000000000
--- a/source/core/slang-downstream-compiler.cpp
+++ /dev/null
@@ -1,716 +0,0 @@
-// slang-downstream-compiler.cpp
-#include "slang-downstream-compiler.h"
-
-#include "slang-common.h"
-#include "../../slang-com-helper.h"
-#include "slang-string-util.h"
-
-#include "slang-type-text-util.h"
-
-#include "slang-io.h"
-#include "slang-shared-library.h"
-#include "slang-blob.h"
-
-#ifdef SLANG_VC
-# include "windows/slang-win-visual-studio-util.h"
-#endif
-
-#include "slang-visual-studio-compiler-util.h"
-#include "slang-gcc-compiler-util.h"
-#include "slang-nvrtc-compiler.h"
-
-namespace Slang
-{
-
-static DownstreamCompiler::Infos _calcInfos()
-{
- typedef DownstreamCompiler::Info Info;
- typedef DownstreamCompiler::SourceLanguageFlag SourceLanguageFlag;
- typedef DownstreamCompiler::SourceLanguageFlags SourceLanguageFlags;
-
- DownstreamCompiler::Infos infos;
-
- infos.infos[int(SLANG_PASS_THROUGH_CLANG)] = Info(SourceLanguageFlag::CPP | SourceLanguageFlag::C);
- infos.infos[int(SLANG_PASS_THROUGH_VISUAL_STUDIO)] = Info(SourceLanguageFlag::CPP | SourceLanguageFlag::C);
- infos.infos[int(SLANG_PASS_THROUGH_GCC)] = Info(SourceLanguageFlag::CPP | SourceLanguageFlag::C);
-
- infos.infos[int(SLANG_PASS_THROUGH_NVRTC)] = Info(SourceLanguageFlag::CUDA);
-
- infos.infos[int(SLANG_PASS_THROUGH_DXC)] = Info(SourceLanguageFlag::HLSL);
- infos.infos[int(SLANG_PASS_THROUGH_FXC)] = Info(SourceLanguageFlag::HLSL);
- infos.infos[int(SLANG_PASS_THROUGH_GLSLANG)] = Info(SourceLanguageFlag::GLSL);
-
- return infos;
-}
-
-/* static */DownstreamCompiler::Infos DownstreamCompiler::s_infos = _calcInfos();
-
-/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DownstreamCompiler::Desc !!!!!!!!!!!!!!!!!!!!!!*/
-
-void DownstreamCompiler::Desc::appendAsText(StringBuilder& out) const
-{
- out << TypeTextUtil::getPassThroughAsHumanText(type);
-
- // Append the version if there is a version
- if (majorVersion || minorVersion)
- {
- out << " ";
- out << majorVersion;
- out << ".";
- out << minorVersion;
- }
-}
-
-/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DownstreamDiagnostic !!!!!!!!!!!!!!!!!!!!!!!!*/
-
-/* static */UnownedStringSlice DownstreamDiagnostic::getSeverityText(Severity severity)
-{
- switch (severity)
- {
- default: return UnownedStringSlice::fromLiteral("Unknown");
- case Severity::Info: return UnownedStringSlice::fromLiteral("Info");
- case Severity::Warning: return UnownedStringSlice::fromLiteral("Warning");
- case Severity::Error: return UnownedStringSlice::fromLiteral("Error");
- }
-}
-
-/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DownstreamCompiler !!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
-
-
-/* static */bool DownstreamCompiler::canCompile(SlangPassThrough compiler, SlangSourceLanguage sourceLanguage)
-{
- const auto& info = getInfo(compiler);
- return (info.sourceLanguageFlags & (SourceLanguageFlags(1) << int(sourceLanguage))) != 0;
-}
-
-/* static */SlangCompileTarget DownstreamCompiler::getCompileTarget(SlangSourceLanguage sourceLanguage)
-{
- switch (sourceLanguage)
- {
- case SLANG_SOURCE_LANGUAGE_HLSL: return SLANG_HLSL;
- case SLANG_SOURCE_LANGUAGE_GLSL: return SLANG_GLSL;
- case SLANG_SOURCE_LANGUAGE_C: return SLANG_C_SOURCE;
- case SLANG_SOURCE_LANGUAGE_CPP: return SLANG_CPP_SOURCE;
- case SLANG_SOURCE_LANGUAGE_CUDA: return SLANG_CUDA_SOURCE;
- default: return SLANG_TARGET_UNKNOWN;
- }
-}
-
-/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DownstreamDiagnostics !!!!!!!!!!!!!!!!!!!!!!*/
-
-Index DownstreamDiagnostics::getCountBySeverity(Diagnostic::Severity severity) const
-{
- Index count = 0;
- for (const auto& msg : diagnostics)
- {
- count += Index(msg.severity == severity);
- }
- return count;
-}
-
-Int DownstreamDiagnostics::countByStage(Diagnostic::Stage stage, Index counts[Int(Diagnostic::Severity::CountOf)]) const
-{
- Int count = 0;
- ::memset(counts, 0, sizeof(Index) * Int(Diagnostic::Severity::CountOf));
- for (const auto& diagnostic : diagnostics)
- {
- if (diagnostic.stage == stage)
- {
- count++;
- counts[Index(diagnostic.severity)]++;
- }
- }
- return count++;
-}
-
-static void _appendCounts(const Index counts[Int(DownstreamDiagnostic::Severity::CountOf)], StringBuilder& out)
-{
- typedef DownstreamDiagnostic::Severity Severity;
-
- for (Index i = 0; i < Int(Severity::CountOf); i++)
- {
- if (counts[i] > 0)
- {
- out << DownstreamDiagnostic::getSeverityText(Severity(i)) << "(" << counts[i] << ") ";
- }
- }
-}
-
-static void _appendSimplified(const Index counts[Int(DownstreamDiagnostic::Severity::CountOf)], StringBuilder& out)
-{
- typedef DownstreamDiagnostic::Severity Severity;
- for (Index i = 0; i < Int(Severity::CountOf); i++)
- {
- if (counts[i] > 0)
- {
- out << DownstreamDiagnostic::getSeverityText(Severity(i)) << " ";
- }
- }
-}
-
-void DownstreamDiagnostics::appendSummary(StringBuilder& out) const
-{
- Index counts[Int(Diagnostic::Severity::CountOf)];
- if (countByStage(Diagnostic::Stage::Compile, counts) > 0)
- {
- out << "Compile: ";
- _appendCounts(counts, out);
- out << "\n";
- }
- if (countByStage(Diagnostic::Stage::Link, counts) > 0)
- {
- out << "Link: ";
- _appendCounts(counts, out);
- out << "\n";
- }
-}
-
-void DownstreamDiagnostics::appendSimplifiedSummary(StringBuilder& out) const
-{
- Index counts[Int(Diagnostic::Severity::CountOf)];
- if (countByStage(Diagnostic::Stage::Compile, counts) > 0)
- {
- out << "Compile: ";
- _appendSimplified(counts, out);
- out << "\n";
- }
- if (countByStage(Diagnostic::Stage::Link, counts) > 0)
- {
- out << "Link: ";
- _appendSimplified(counts, out);
- out << "\n";
- }
-}
-
-void DownstreamDiagnostics::removeBySeverity(Diagnostic::Severity severity)
-{
- Index count = diagnostics.getCount();
- for (Index i = 0; i < count; ++i)
- {
- if (diagnostics[i].severity == severity)
- {
- diagnostics.removeAt(i);
- i--;
- count--;
- }
- }
-}
-
-/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CommandLineDownstreamCompileResult !!!!!!!!!!!!!!!!!!!!!!*/
-
-SlangResult CommandLineDownstreamCompileResult::getHostCallableSharedLibrary(ComPtr<ISlangSharedLibrary>& outLibrary)
-{
- if (m_hostCallableSharedLibrary)
- {
- outLibrary = m_hostCallableSharedLibrary;
- return SLANG_OK;
- }
-
- // Okay we want to load
- // Try loading the shared library
- SharedLibrary::Handle handle;
- if (SLANG_FAILED(SharedLibrary::loadWithPlatformPath(m_moduleFilePath.getBuffer(), handle)))
- {
- return SLANG_FAIL;
- }
- // The shared library needs to keep temp files in scope
- RefPtr<TemporarySharedLibrary> sharedLib(new TemporarySharedLibrary(handle, m_moduleFilePath));
- sharedLib->m_temporaryFileSet = m_temporaryFiles;
-
- m_hostCallableSharedLibrary = sharedLib;
- outLibrary = m_hostCallableSharedLibrary;
- return SLANG_OK;
-}
-
-SlangResult CommandLineDownstreamCompileResult::getBinary(ComPtr<ISlangBlob>& outBlob)
-{
- if (m_binaryBlob)
- {
- outBlob = m_binaryBlob;
- return SLANG_OK;
- }
-
- // Read the binary
- try
- {
- // Read the contents of the binary
- List<uint8_t> contents = File::readAllBytes(m_moduleFilePath);
-
- m_binaryBlob = new ScopeRefObjectBlob(ListBlob::moveCreate(contents), m_temporaryFiles);
- outBlob = m_binaryBlob;
- return SLANG_OK;
- }
- catch (const Slang::IOException&)
- {
- return SLANG_FAIL;
- }
-}
-
-/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CommandLineDownstreamCompiler !!!!!!!!!!!!!!!!!!!!!!*/
-
-static bool _isContentsInFile(const DownstreamCompiler::CompileOptions& options)
-{
- if (options.sourceContentsPath.getLength() <= 0)
- {
- return false;
- }
-
- // We can see if we can load it
- if (File::exists(options.sourceContentsPath))
- {
- // Here we look for the file on the regular file system (as opposed to using the
- // ISlangFileSystem. This is unfortunate but necessary - because when we call out
- // to the compiler all it is able to (currently) see are files on the file system.
- //
- // Note that it could be coincidence that the filesystem has a file that's identical in
- // contents/name. That being the case though, any includes wouldn't work for a generated
- // file either from some specialized ISlangFileSystem, so this is probably as good as it gets
- // until we can integrate directly to a C/C++ compiler through say a shared library where we can control
- // file system access.
- try
- {
- String readContents = File::readAllText(options.sourceContentsPath);
- // We should see if they are the same
- return options.sourceContents == readContents.getUnownedSlice();
- }
- catch (const Slang::IOException&)
- {
- }
- }
- return false;
-}
-
-SlangResult CommandLineDownstreamCompiler::compile(const CompileOptions& inOptions, RefPtr<DownstreamCompileResult>& out)
-{
- // Copy the command line options
- CommandLine cmdLine(m_cmdLine);
-
- CompileOptions options(inOptions);
-
- // Find all the files that will be produced
- RefPtr<TemporaryFileSet> productFileSet(new TemporaryFileSet);
-
- if (options.modulePath.getLength() == 0 || options.sourceContents.getLength() != 0)
- {
- String modulePath = options.modulePath;
-
- // If there is no module path, generate one.
- if (modulePath.getLength() == 0)
- {
- SLANG_RETURN_ON_FAIL(File::generateTemporary(UnownedStringSlice::fromLiteral("slang-generated"), modulePath));
- options.modulePath = modulePath;
- }
-
- if (_isContentsInFile(options))
- {
- options.sourceFiles.add(options.sourceContentsPath);
- }
- else
- {
- String compileSourcePath = modulePath;
-
- // NOTE: Strictly speaking producing filenames by modifying the generateTemporary path that may introduce a temp filename clash, but in practice is extraordinary unlikely
- compileSourcePath.append("-src");
-
- // Make the temporary filename have the appropriate extension.
- if (options.sourceLanguage == SLANG_SOURCE_LANGUAGE_C)
- {
- compileSourcePath.append(".c");
- }
- else
- {
- compileSourcePath.append(".cpp");
- }
-
- // Write it out
- try
- {
- productFileSet->add(compileSourcePath);
-
- File::writeAllText(compileSourcePath, options.sourceContents);
- }
- catch (...)
- {
- return SLANG_FAIL;
- }
-
- // Add it as a source file
- options.sourceFiles.add(compileSourcePath);
- }
-
- // There is no source contents
- options.sourceContents = String();
- options.sourceContentsPath = String();
- }
-
- // Append command line args to the end of cmdLine using the target specific function for the specified options
- SLANG_RETURN_ON_FAIL(calcArgs(options, cmdLine));
-
- String moduleFilePath;
-
- {
- StringBuilder builder;
- SLANG_RETURN_ON_FAIL(calcModuleFilePath(options, builder));
- moduleFilePath = builder.ProduceString();
- }
-
- {
- List<String> paths;
- SLANG_RETURN_ON_FAIL(calcCompileProducts(options, DownstreamCompiler::ProductFlag::All, paths));
- productFileSet->add(paths);
- }
-
- ExecuteResult exeRes;
-
-#if 0
- // Test
- {
- String line = ProcessUtil::getCommandLineString(cmdLine);
- printf("%s", line.getBuffer());
- }
-#endif
-
- SLANG_RETURN_ON_FAIL(ProcessUtil::execute(cmdLine, exeRes));
-
-#if 0
- {
- printf("stdout=\"%s\"\nstderr=\"%s\"\nret=%d\n", exeRes.standardOutput.getBuffer(), exeRes.standardError.getBuffer(), int(exeRes.resultCode));
- }
-#endif
-
- DownstreamDiagnostics diagnostics;
- SLANG_RETURN_ON_FAIL(parseOutput(exeRes, diagnostics));
-
-
- out = new CommandLineDownstreamCompileResult(diagnostics, moduleFilePath, productFileSet);
-
- return SLANG_OK;
-}
-
-/* !!!!!!!!!!!!!!!!!!!!!!!!! DownstreamCompiler::Desc !!!!!!!!!!!!!!!!!!!!!!*/
-
-static DownstreamCompiler::Desc _calcCompiledWithDesc()
-{
- DownstreamCompiler::Desc desc;
-
-#if SLANG_VC
- desc = WinVisualStudioUtil::getDesc(WinVisualStudioUtil::getCompiledVersion());
-#elif SLANG_CLANG
- desc.type = SLANG_PASS_THROUGH_CLANG;
- desc.majorVersion = Int(__clang_major__);
- desc.minorVersion = Int(__clang_minor__);
-#elif SLANG_GCC
- desc.type = SLANG_PASS_THROUGH_GCC;
- desc.majorVersion = Int(__GNUC__);
- desc.minorVersion = Int(__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;
-#endif
-
- return desc;
-}
-
-/* !!!!!!!!!!!!!!!!!!!!!!!!! DownstreamCompilerUtil !!!!!!!!!!!!!!!!!!!!!!*/
-
-const DownstreamCompiler::Desc& DownstreamCompilerUtil::getCompiledWithDesc()
-{
- static DownstreamCompiler::Desc s_desc = _calcCompiledWithDesc();
- return s_desc;
-}
-
-/* static */DownstreamCompiler* DownstreamCompilerUtil::findCompiler(const DownstreamCompilerSet* set, MatchType matchType, const DownstreamCompiler::Desc& desc)
-{
- List<DownstreamCompiler*> compilers;
- set->getCompilers(compilers);
- return findCompiler(compilers, matchType, desc);
-}
-
-/* static */DownstreamCompiler* DownstreamCompilerUtil::findCompiler(const List<DownstreamCompiler*>& compilers, MatchType matchType, const DownstreamCompiler::Desc& desc)
-{
- if (compilers.getCount() <= 0)
- {
- return nullptr;
- }
-
- Int bestIndex = -1;
-
- const SlangPassThrough compilerType = desc.type;
-
- Int maxVersionValue = 0;
- Int minVersionDiff = 0x7fffffff;
-
- Int descVersionValue = desc.getVersionValue();
-
- // If we don't have version set, then anything 0 or above is good enough, and just take newest
- if (descVersionValue == 0)
- {
- maxVersionValue = -1;
- matchType = MatchType::Newest;
- }
-
- for (Index i = 0; i < compilers.getCount(); ++i)
- {
- DownstreamCompiler* compiler = compilers[i];
- auto compilerDesc = compiler->getDesc();
-
- if (compilerType == compilerDesc.type)
- {
- const Int versionValue = compilerDesc.getVersionValue();
- switch (matchType)
- {
- case MatchType::MinGreaterEqual:
- {
- auto diff = descVersionValue - versionValue;
- if (diff >= 0 && diff < minVersionDiff)
- {
- bestIndex = i;
- minVersionDiff = diff;
- }
- break;
- }
- case MatchType::MinAbsolute:
- {
- auto diff = descVersionValue - versionValue;
- diff = (diff >= 0) ? diff : -diff;
- if (diff < minVersionDiff)
- {
- bestIndex = i;
- minVersionDiff = diff;
- }
- break;
- }
- case MatchType::Newest:
- {
- if (versionValue > maxVersionValue)
- {
- maxVersionValue = versionValue;
- bestIndex = i;
- }
- break;
- }
- }
- }
- }
-
- return (bestIndex >= 0) ? compilers[bestIndex] : nullptr;
-}
-
-/* static */DownstreamCompiler* DownstreamCompilerUtil::findClosestCompiler(const List<DownstreamCompiler*>& compilers, const DownstreamCompiler::Desc& desc)
-{
- DownstreamCompiler* compiler;
-
- compiler = findCompiler(compilers, MatchType::MinGreaterEqual, desc);
- if (compiler)
- {
- return compiler;
- }
- compiler = findCompiler(compilers, MatchType::MinAbsolute, desc);
- if (compiler)
- {
- return compiler;
- }
-
- // If we are gcc, we can try clang and vice versa
- if (desc.type == SLANG_PASS_THROUGH_GCC || desc.type == SLANG_PASS_THROUGH_CLANG)
- {
- DownstreamCompiler::Desc compatible = desc;
- compatible.type = (compatible.type == SLANG_PASS_THROUGH_CLANG) ? SLANG_PASS_THROUGH_GCC : SLANG_PASS_THROUGH_CLANG;
-
- compiler = findCompiler(compilers, MatchType::MinGreaterEqual, compatible);
- if (compiler)
- {
- return compiler;
- }
- compiler = findCompiler(compilers, MatchType::MinAbsolute, compatible);
- if (compiler)
- {
- return compiler;
- }
- }
-
- return nullptr;
-}
-
-/* static */DownstreamCompiler* DownstreamCompilerUtil::findClosestCompiler(const DownstreamCompilerSet* set, const DownstreamCompiler::Desc& desc)
-{
- DownstreamCompiler* compiler = set->getCompiler(desc);
- if (compiler)
- {
- return compiler;
- }
- List<DownstreamCompiler*> compilers;
- set->getCompilers(compilers);
- return findClosestCompiler(compilers, desc);
-}
-
-/* static */void DownstreamCompilerUtil::updateDefault(DownstreamCompilerSet* set, SlangSourceLanguage sourceLanguage)
-{
- DownstreamCompiler* compiler = nullptr;
-
- switch (sourceLanguage)
- {
- case SLANG_SOURCE_LANGUAGE_CPP:
- case SLANG_SOURCE_LANGUAGE_C:
- {
- compiler = findClosestCompiler(set, getCompiledWithDesc());
- break;
- }
- case SLANG_SOURCE_LANGUAGE_CUDA:
- {
- DownstreamCompiler::Desc desc;
- desc.type = SLANG_PASS_THROUGH_NVRTC;
- compiler = findCompiler(set, MatchType::Newest, desc);
- break;
- }
- default: break;
- }
-
- set->setDefaultCompiler(sourceLanguage, compiler);
-}
-
-/* static */void DownstreamCompilerUtil::updateDefaults(DownstreamCompilerSet* set)
-{
- for (Index i = 0; i < Index(SLANG_SOURCE_LANGUAGE_COUNT_OF); ++i)
- {
- updateDefault(set, SlangSourceLanguage(i));
- }
-}
-
-static SlangResult _locateDXCCompilers(const String& path, ISlangSharedLibraryLoader* loader, DownstreamCompilerSet* set)
-{
- // First try dxil, so it's loaded from the same path if it's there
- ComPtr<ISlangSharedLibrary> dxil;
- DefaultSharedLibraryLoader::load(loader, path, "dxil", dxil.writeRef());
-
- ComPtr<ISlangSharedLibrary> sharedLibrary;
- if (SLANG_SUCCEEDED(DefaultSharedLibraryLoader::load(loader, path, "dxcompiler", sharedLibrary.writeRef())))
- {
- // Can we determine the version?
- DownstreamCompiler::Desc desc(SLANG_PASS_THROUGH_DXC);
- RefPtr<DownstreamCompiler> compiler(new SharedLibraryDownstreamCompiler(desc, sharedLibrary));
-
- set->addCompiler(compiler);
- }
- return SLANG_OK;
-}
-
-static SlangResult _locateFXCCompilers(const String& path, ISlangSharedLibraryLoader* loader, DownstreamCompilerSet* set)
-{
- ComPtr<ISlangSharedLibrary> sharedLibrary;
- if (SLANG_SUCCEEDED(DefaultSharedLibraryLoader::load(loader, path, "d3dcompiler_47", sharedLibrary.writeRef())))
- {
- // Can we determine the version?
- DownstreamCompiler::Desc desc(SLANG_PASS_THROUGH_FXC);
- RefPtr<DownstreamCompiler> compiler(new SharedLibraryDownstreamCompiler(desc, sharedLibrary));
- set->addCompiler(compiler);
- }
- return SLANG_OK;
-}
-
-static SlangResult _locateGlslangCompilers(const String& path, ISlangSharedLibraryLoader* loader, DownstreamCompilerSet* set)
-{
-#if SLANG_UNIX_FAMILY
- // On unix systems we need to ensure pthread is loaded first.
- ComPtr<ISlangSharedLibrary> pthreadLibrary;
- DefaultSharedLibraryLoader::load(loader, path, "pthread", pthreadLibrary.writeRef());
-#endif
- ComPtr<ISlangSharedLibrary> sharedLibrary;
- if (SLANG_SUCCEEDED(DefaultSharedLibraryLoader::load(loader, path, "slang-glslang", sharedLibrary.writeRef())))
- {
- // Can we determine the version?
- DownstreamCompiler::Desc desc(SLANG_PASS_THROUGH_GLSLANG);
- RefPtr<DownstreamCompiler> compiler(new SharedLibraryDownstreamCompiler(desc, sharedLibrary));
- set->addCompiler(compiler);
- }
- return SLANG_OK;
-}
-
-/* static */void DownstreamCompilerUtil::setDefaultLocators(DownstreamCompilerLocatorFunc outFuncs[int(SLANG_PASS_THROUGH_COUNT_OF)])
-{
- outFuncs[int(SLANG_PASS_THROUGH_VISUAL_STUDIO)] = &VisualStudioCompilerUtil::locateCompilers;
- outFuncs[int(SLANG_PASS_THROUGH_CLANG)] = &GCCDownstreamCompilerUtil::locateClangCompilers;
- outFuncs[int(SLANG_PASS_THROUGH_GCC)] = &GCCDownstreamCompilerUtil::locateGCCCompilers;
- outFuncs[int(SLANG_PASS_THROUGH_NVRTC)] = &NVRTCDownstreamCompilerUtil::locateCompilers;
- outFuncs[int(SLANG_PASS_THROUGH_DXC)] = &_locateDXCCompilers;
- outFuncs[int(SLANG_PASS_THROUGH_FXC)] = &_locateFXCCompilers;
- outFuncs[int(SLANG_PASS_THROUGH_GLSLANG)] = &_locateGlslangCompilers;
-}
-
-/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DownstreamCompilerSet !!!!!!!!!!!!!!!!!!!!!!*/
-
-void DownstreamCompilerSet::getCompilerDescs(List<DownstreamCompiler::Desc>& outCompilerDescs) const
-{
- outCompilerDescs.clear();
- for (DownstreamCompiler* compiler : m_compilers)
- {
- outCompilerDescs.add(compiler->getDesc());
- }
-}
-
-Index DownstreamCompilerSet::_findIndex(const DownstreamCompiler::Desc& desc) const
-{
- const Index count = m_compilers.getCount();
- for (Index i = 0; i < count; ++i)
- {
- if (m_compilers[i]->getDesc() == desc)
- {
- return i;
- }
- }
- return -1;
-}
-
-DownstreamCompiler* DownstreamCompilerSet::getCompiler(const DownstreamCompiler::Desc& compilerDesc) const
-{
- const Index index = _findIndex(compilerDesc);
- return index >= 0 ? m_compilers[index] : nullptr;
-}
-
-void DownstreamCompilerSet::getCompilers(List<DownstreamCompiler*>& outCompilers) const
-{
- outCompilers.clear();
- outCompilers.addRange((DownstreamCompiler*const*)m_compilers.begin(), m_compilers.getCount());
-}
-
-bool DownstreamCompilerSet::hasCompiler(SlangPassThrough compilerType) const
-{
- for (DownstreamCompiler* compiler : m_compilers)
- {
- const auto& desc = compiler->getDesc();
- if (desc.type == compilerType)
- {
- return true;
- }
- }
- return false;
-}
-
-
-void DownstreamCompilerSet::remove(SlangPassThrough compilerType)
-{
- for (Index i = 0; i < m_compilers.getCount(); ++i)
- {
- DownstreamCompiler* compiler = m_compilers[i];
- if (compiler->getDesc().type == compilerType)
- {
- m_compilers.fastRemoveAt(i);
- i--;
- }
- }
-}
-
-void DownstreamCompilerSet::addCompiler(DownstreamCompiler* compiler)
-{
- const Index index = _findIndex(compiler->getDesc());
- if (index >= 0)
- {
- m_compilers[index] = compiler;
- }
- else
- {
- m_compilers.add(compiler);
- }
-}
-
-}
diff --git a/source/core/slang-downstream-compiler.h b/source/core/slang-downstream-compiler.h
deleted file mode 100644
index 9c3b9055f..000000000
--- a/source/core/slang-downstream-compiler.h
+++ /dev/null
@@ -1,501 +0,0 @@
-#ifndef SLANG_DOWNSTREAM_COMPILER_H
-#define SLANG_DOWNSTREAM_COMPILER_H
-
-#include "slang-common.h"
-#include "slang-string.h"
-
-#include "slang-process-util.h"
-
-#include "slang-platform.h"
-#include "slang-semantic-version.h"
-
-#include "slang-io.h"
-
-#include "../../slang-com-ptr.h"
-
-namespace Slang
-{
-
-struct DownstreamDiagnostic
-{
- typedef DownstreamDiagnostic ThisType;
-
- enum class Severity
- {
- Unknown,
- Info,
- Warning,
- Error,
- CountOf,
- };
- enum class Stage
- {
- Compile,
- Link,
- };
-
- void reset()
- {
- severity = Severity::Unknown;
- stage = Stage::Compile;
- fileLine = 0;
- }
-
- bool operator==(const ThisType& rhs) const
- {
- return severity == rhs.severity &&
- stage == rhs.stage &&
- text == rhs.text &&
- code == rhs.code &&
- filePath == rhs.filePath &&
- fileLine == rhs.fileLine;
- }
- bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
-
- static UnownedStringSlice getSeverityText(Severity severity);
-
- Severity severity; ///< The severity of error
- Stage stage; ///< The stage the error came from
- String text; ///< The text of the error
- String code; ///< The compiler specific error code
- String filePath; ///< The path the error originated from
- Int fileLine; ///< The line number the error came from
-};
-
-struct DownstreamDiagnostics
-{
- typedef DownstreamDiagnostic Diagnostic;
-
- /// Reset to an initial empty state
- void reset() { diagnostics.clear(); rawDiagnostics = String(); result = SLANG_OK; }
-
- /// Get the number of diagnostics by severity
- Index getCountBySeverity(Diagnostic::Severity severity) const;
- /// True if there are any diagnostics of severity
- bool has(Diagnostic::Severity severity) const { return getCountBySeverity(severity) > 0; }
-
- /// Stores in outCounts, the amount of diagnostics for the stage of each severity
- Int countByStage(Diagnostic::Stage stage, Index outCounts[Int(Diagnostic::Severity::CountOf)]) const;
-
- /// Append a summary to out
- void appendSummary(StringBuilder& out) const;
- /// Appends a summary that just identifies if there is an error of a type (not a count)
- void appendSimplifiedSummary(StringBuilder& out) const;
-
- /// Remove all diagnostics of the type
- void removeBySeverity(Diagnostic::Severity severity);
-
- String rawDiagnostics;
-
- SlangResult result;
- List<Diagnostic> diagnostics;
-};
-
-class DownstreamCompileResult : public RefObject
-{
-public:
-
- virtual SlangResult getHostCallableSharedLibrary(ComPtr<ISlangSharedLibrary>& outLibrary) = 0;
- virtual SlangResult getBinary(ComPtr<ISlangBlob>& outBlob) = 0;
-
- const DownstreamDiagnostics& getDiagnostics() const { return m_diagnostics; }
-
- /// Ctor
- DownstreamCompileResult(const DownstreamDiagnostics& diagnostics):
- m_diagnostics(diagnostics)
- {}
-
-protected:
- DownstreamDiagnostics m_diagnostics;
-};
-
-
-class BlobDownstreamCompileResult : public DownstreamCompileResult
-{
-public:
- typedef DownstreamCompileResult Super;
-
- virtual SlangResult getHostCallableSharedLibrary(ComPtr<ISlangSharedLibrary>& outLibrary) SLANG_OVERRIDE { SLANG_UNUSED(outLibrary); return SLANG_FAIL; }
- virtual SlangResult getBinary(ComPtr<ISlangBlob>& outBlob) SLANG_OVERRIDE { outBlob = m_blob; return m_blob ? SLANG_OK : SLANG_FAIL; }
-
- BlobDownstreamCompileResult(const DownstreamDiagnostics& diags, ISlangBlob* blob):
- Super(diags),
- m_blob(blob)
- {
-
- }
-protected:
- ComPtr<ISlangBlob> m_blob;
-};
-
-class DownstreamCompiler: public RefObject
-{
-public:
- typedef RefObject Super;
-
- typedef DownstreamCompileResult CompileResult;
-
- typedef uint32_t SourceLanguageFlags;
- struct SourceLanguageFlag
- {
- enum Enum : SourceLanguageFlags
- {
- Unknown = SourceLanguageFlags(1) << SLANG_SOURCE_LANGUAGE_UNKNOWN,
- Slang = SourceLanguageFlags(1) << SLANG_SOURCE_LANGUAGE_SLANG,
- HLSL = SourceLanguageFlags(1) << SLANG_SOURCE_LANGUAGE_HLSL,
- GLSL = SourceLanguageFlags(1) << SLANG_SOURCE_LANGUAGE_GLSL,
- C = SourceLanguageFlags(1) << SLANG_SOURCE_LANGUAGE_C,
- CPP = SourceLanguageFlags(1) << SLANG_SOURCE_LANGUAGE_CPP,
- CUDA = SourceLanguageFlags(1) << SLANG_SOURCE_LANGUAGE_CUDA,
- };
- };
-
- struct Info
- {
- Info():sourceLanguageFlags(0) {}
-
- Info(SourceLanguageFlags inSourceLanguageFlags):
- sourceLanguageFlags(inSourceLanguageFlags)
- {}
- SourceLanguageFlags sourceLanguageFlags;
- };
- struct Infos
- {
- Info infos[int(SLANG_PASS_THROUGH_COUNT_OF)];
- };
-
- struct Desc
- {
- typedef Desc ThisType;
-
- HashCode getHashCode() const { return combineHash(HashCode(type), combineHash(HashCode(majorVersion), HashCode(minorVersion))); }
- bool operator==(const ThisType& rhs) const { return type == rhs.type && majorVersion == rhs.majorVersion && minorVersion == rhs.minorVersion; }
- bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
-
- /// Get the version as a value
- Int getVersionValue() const { return majorVersion * 100 + minorVersion; }
-
- void appendAsText(StringBuilder& out) const;
- /// true if has a version set
- bool hasVersion() const { return majorVersion || minorVersion; }
-
- /// Ctor
- explicit Desc(SlangPassThrough inType = SLANG_PASS_THROUGH_NONE, Int inMajorVersion = 0, Int inMinorVersion = 0):type(inType), majorVersion(inMajorVersion), minorVersion(inMinorVersion) {}
-
- SlangPassThrough type; ///< The type of the compiler
- Int majorVersion; ///< Major version (interpretation is type specific)
- Int minorVersion; ///< Minor version
- };
-
- enum class OptimizationLevel
- {
- None, ///< Don't optimize at all.
- Default, ///< Default optimization level: balance code quality and compilation time.
- High, ///< Optimize aggressively.
- Maximal, ///< Include optimizations that may take a very long time, or may involve severe space-vs-speed tradeoffs
- };
-
- enum class DebugInfoType
- {
- None, ///< Don't emit debug information at all.
- Minimal, ///< Emit as little debug information as possible, while still supporting stack traces.
- Standard, ///< Emit whatever is the standard level of debug information for each target.
- Maximal, ///< Emit as much debug information as possible for each target.
- };
- enum class FloatingPointMode
- {
- Default,
- Fast,
- Precise,
- };
-
- enum TargetType
- {
- Executable, ///< Produce an executable
- SharedLibrary, ///< Produce a shared library object/dll
- Object, ///< Produce an object file
- };
-
- enum PipelineType
- {
- Unknown,
- Compute,
- Rasterization,
- RayTracing,
- };
-
- struct Define
- {
- String nameWithSig; ///< If macro takes parameters include in brackets
- String value;
- };
-
- struct CapabilityVersion
- {
- enum class Kind
- {
- CUDASM, ///< What the version is for
- };
- Kind kind;
- SemanticVersion version;
- };
-
- struct CompileOptions
- {
- typedef uint32_t Flags;
- struct Flag
- {
- enum Enum : Flags
- {
- EnableExceptionHandling = 0x01,
- Verbose = 0x02,
- EnableSecurityChecks = 0x04,
- };
- };
-
- OptimizationLevel optimizationLevel = OptimizationLevel::Default;
- DebugInfoType debugInfoType = DebugInfoType::Standard;
- TargetType targetType = TargetType::Executable;
- SlangSourceLanguage sourceLanguage = SLANG_SOURCE_LANGUAGE_CPP;
- FloatingPointMode floatingPointMode = FloatingPointMode::Default;
- PipelineType pipelineType = PipelineType::Unknown;
-
- Flags flags = Flag::EnableExceptionHandling;
-
- PlatformKind platform = PlatformKind::Unknown;
-
- /// The path/name of the output module. Should not have the extension, as that will be added for each of the target types.
- /// If not set a module path will be internally generated internally on a command line based compiler
- String modulePath;
-
- List<Define> defines;
-
- /// The contents of the source to compile. This can be empty is sourceFiles is set.
- /// If the compiler is a commandLine file this source will be written to a temporary file.
- String sourceContents;
- /// 'Path' that the contents originated from. NOTE! This is for reporting only and doesn't have to exist on file system
- String sourceContentsPath;
-
- /// The names/paths of source to compile. This can be empty if sourceContents is set.
- List<String> sourceFiles;
-
- List<String> includePaths;
- List<String> libraryPaths;
-
- List<CapabilityVersion> requiredCapabilityVersions;
- };
-
- typedef uint32_t ProductFlags;
- struct ProductFlag
- {
- enum Enum : ProductFlags
- {
- Debug = 0x1, ///< Used by debugger during execution
- Execution = 0x2, ///< Required for execution
- Compile = 0x4, ///< A product *required* for compilation
- Miscellaneous = 0x8, ///< Anything else
- };
- enum Mask : ProductFlags
- {
- All = 0xf, ///< All the flags
- };
- };
-
- enum class Product
- {
- DebugRun,
- Run,
- CompileTemporary,
- All,
- };
-
- /// Get the desc of this compiler
- const Desc& getDesc() const { return m_desc; }
- /// Compile using the specified options. The result is in resOut
- virtual SlangResult compile(const CompileOptions& options, RefPtr<DownstreamCompileResult>& outResult) = 0;
- /// Some downstream compilers are backed by a shared library. This allows access to the shared library to access internal functions.
- virtual ISlangSharedLibrary* getSharedLibrary() { return nullptr; }
-
- /// Get info for a compiler type
- static const Info& getInfo(SlangPassThrough compiler) { return s_infos.infos[int(compiler)]; }
- /// True if this compiler can compile the specified language
- static bool canCompile(SlangPassThrough compiler, SlangSourceLanguage sourceLanguage);
-
- /// Given a source language return as the equivalent compile target
- static SlangCompileTarget getCompileTarget(SlangSourceLanguage sourceLanguage);
-
-protected:
- static Infos s_infos;
-
- DownstreamCompiler(const Desc& desc) :
- m_desc(desc)
- {}
- DownstreamCompiler() {}
-
- Desc m_desc;
-};
-
-class CommandLineDownstreamCompileResult : public DownstreamCompileResult
-{
-public:
- typedef DownstreamCompileResult Super;
-
- virtual SlangResult getHostCallableSharedLibrary(ComPtr<ISlangSharedLibrary>& outLibrary) SLANG_OVERRIDE;
- virtual SlangResult getBinary(ComPtr<ISlangBlob>& outBlob) SLANG_OVERRIDE;
-
- CommandLineDownstreamCompileResult(const DownstreamDiagnostics& diagnostics, const String& moduleFilePath, TemporaryFileSet* temporaryFileSet) :
- Super(diagnostics),
- m_moduleFilePath(moduleFilePath),
- m_temporaryFiles(temporaryFileSet)
- {
- }
-
- RefPtr<TemporaryFileSet> m_temporaryFiles;
-
-protected:
-
- String m_moduleFilePath;
- DownstreamCompiler::CompileOptions m_options;
- ComPtr<ISlangBlob> m_binaryBlob;
- /// Cache of the shared library if appropriate
- ComPtr<ISlangSharedLibrary> m_hostCallableSharedLibrary;
-};
-
-class CommandLineDownstreamCompiler : public DownstreamCompiler
-{
-public:
- typedef DownstreamCompiler Super;
-
- // DownstreamCompiler
- virtual SlangResult compile(const CompileOptions& options, RefPtr<DownstreamCompileResult>& outResult) SLANG_OVERRIDE;
-
- // Functions to be implemented for a specific CommandLine
-
- /// Given the compilation options and the module name, determines the actual file name used for output
- virtual SlangResult calcModuleFilePath(const CompileOptions& options, StringBuilder& outPath) = 0;
- /// Given options determines the paths to products produced (including the 'moduleFilePath').
- /// Note that does *not* guarentee all products were or should be produced. Just aims to include all that could
- /// be produced, such that can be removed on completion.
- virtual SlangResult calcCompileProducts(const CompileOptions& options, ProductFlags flags, List<String>& outPaths) = 0;
-
- virtual SlangResult calcArgs(const CompileOptions& options, CommandLine& cmdLine) = 0;
- virtual SlangResult parseOutput(const ExecuteResult& exeResult, DownstreamDiagnostics& output) = 0;
-
- CommandLineDownstreamCompiler(const Desc& desc, const String& exeName) :
- Super(desc)
- {
- m_cmdLine.setExecutableFilename(exeName);
- }
-
- CommandLineDownstreamCompiler(const Desc& desc, const CommandLine& cmdLine) :
- Super(desc),
- m_cmdLine(cmdLine)
- {}
-
- CommandLineDownstreamCompiler(const Desc& desc):Super(desc) {}
-
- CommandLine m_cmdLine;
-};
-
-class SharedLibraryDownstreamCompiler: public DownstreamCompiler
-{
-public:
- typedef DownstreamCompiler Super;
-
- // DownstreamCompiler
- virtual SlangResult compile(const CompileOptions& options, RefPtr<DownstreamCompileResult>& outResult) SLANG_OVERRIDE { SLANG_UNUSED(options); SLANG_UNUSED(outResult); return SLANG_E_NOT_IMPLEMENTED; }
- virtual ISlangSharedLibrary* getSharedLibrary() SLANG_OVERRIDE { return m_library; }
-
- SharedLibraryDownstreamCompiler(const Desc& desc, ISlangSharedLibrary* library):
- Super(desc),
- m_library(library)
- {
- }
-protected:
- ComPtr<ISlangSharedLibrary> m_library;
-};
-
-class DownstreamCompilerSet : public RefObject
-{
-public:
- typedef RefObject Super;
-
- /// Find all the available compilers
- void getCompilerDescs(List<DownstreamCompiler::Desc>& outCompilerDescs) const;
- /// Returns list of all compilers
- void getCompilers(List<DownstreamCompiler*>& outCompilers) const;
-
- /// Get a compiler
- DownstreamCompiler* getCompiler(const DownstreamCompiler::Desc& compilerDesc) const;
-
- /// Will replace if there is one with same desc
- void addCompiler(DownstreamCompiler* compiler);
-
- /// Get a default compiler
- DownstreamCompiler* getDefaultCompiler(SlangSourceLanguage sourceLanguage) const { return m_defaultCompilers[int(sourceLanguage)]; }
- /// Set the default compiler
- void setDefaultCompiler(SlangSourceLanguage sourceLanguage, DownstreamCompiler* compiler) { m_defaultCompilers[int(sourceLanguage)] = compiler; }
-
- /// True if has a compiler of the specified type
- bool hasCompiler(SlangPassThrough compilerType) const;
-
- void remove(SlangPassThrough compilerType);
-
- void clear() { m_compilers.clear(); }
-
-protected:
-
- Index _findIndex(const DownstreamCompiler::Desc& desc) const;
-
- RefPtr<DownstreamCompiler> m_defaultCompilers[int(SLANG_SOURCE_LANGUAGE_COUNT_OF)];
- // This could be a dictionary/map - but doing a linear search is going to be fine and it makes
- // somethings easier.
- List<RefPtr<DownstreamCompiler>> m_compilers;
-};
-
-typedef SlangResult (*DownstreamCompilerLocatorFunc)(const String& path, ISlangSharedLibraryLoader* loader, DownstreamCompilerSet* set);
-
-/* Only purpose of having base-class here is to make all the DownstreamCompiler types available directly in derived Utils */
-struct DownstreamCompilerBaseUtil
-{
- typedef DownstreamCompiler::CompileOptions CompileOptions;
- typedef DownstreamCompiler::OptimizationLevel OptimizationLevel;
- typedef DownstreamCompiler::TargetType TargetType;
- typedef DownstreamCompiler::DebugInfoType DebugInfoType;
-
- typedef DownstreamDiagnostics::Diagnostic Diagnostic;
-
- typedef DownstreamCompiler::FloatingPointMode FloatingPointMode;
- typedef DownstreamCompiler::ProductFlag ProductFlag;
- typedef DownstreamCompiler::ProductFlags ProductFlags;
-};
-
-struct DownstreamCompilerUtil: public DownstreamCompilerBaseUtil
-{
- enum class MatchType
- {
- MinGreaterEqual,
- MinAbsolute,
- Newest,
- };
-
- /// Find a compiler
- 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);
-
- /// 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);
-
- /// Get the information on the compiler used to compile this source
- static const DownstreamCompiler::Desc& getCompiledWithDesc();
-
- static void updateDefault(DownstreamCompilerSet* set, SlangSourceLanguage sourceLanguage);
- static void updateDefaults(DownstreamCompilerSet* set);
-
- static void setDefaultLocators(DownstreamCompilerLocatorFunc outFuncs[int(SLANG_PASS_THROUGH_COUNT_OF)]);
-};
-
-}
-
-#endif
diff --git a/source/core/slang-file-system.cpp b/source/core/slang-file-system.cpp
new file mode 100644
index 000000000..992ae4155
--- /dev/null
+++ b/source/core/slang-file-system.cpp
@@ -0,0 +1,888 @@
+#include "slang-file-system.h"
+
+#include "../../slang-com-ptr.h"
+#include "../core/slang-io.h"
+#include "../core/slang-string-util.h"
+
+namespace Slang
+{
+
+// Allocate static const storage for the various interface IDs that the Slang API needs to expose
+static const Guid IID_ISlangUnknown = SLANG_UUID_ISlangUnknown;
+static const Guid IID_ISlangFileSystem = SLANG_UUID_ISlangFileSystem;
+static const Guid IID_ISlangFileSystemExt = SLANG_UUID_ISlangFileSystemExt;
+static const Guid IID_ISlangMutableFileSystem = SLANG_UUID_ISlangMutableFileSystem;
+
+static const Guid IID_SlangCacheFileSystem = SLANG_UUID_CacheFileSystem;
+
+SLANG_FORCE_INLINE static SlangResult _checkExt(FileSystemStyle style) { return Index(style) >= Index(FileSystemStyle::Ext) ? SLANG_OK : SLANG_E_NOT_IMPLEMENTED; }
+SLANG_FORCE_INLINE static SlangResult _checkMutable(FileSystemStyle style) { return Index(style) >= Index(FileSystemStyle::Mutable) ? SLANG_OK : SLANG_E_NOT_IMPLEMENTED; }
+
+SLANG_FORCE_INLINE static bool _canCast(FileSystemStyle style, const Guid& guid)
+{
+ if (guid == IID_ISlangUnknown || guid == IID_ISlangFileSystem)
+ {
+ return true;
+ }
+ else if (guid == IID_ISlangFileSystemExt)
+ {
+ return Index(style) >= Index(FileSystemStyle::Ext);
+ }
+ else if (guid == IID_ISlangMutableFileSystem)
+ {
+ return Index(style) >= Index(FileSystemStyle::Mutable);
+ }
+ return false;
+}
+
+static FileSystemStyle _getFileSystemStyle(ISlangFileSystem* system, ComPtr<ISlangFileSystem>& out)
+{
+ SLANG_ASSERT(system);
+
+ FileSystemStyle style = FileSystemStyle::Load;
+
+ if (SLANG_SUCCEEDED(system->queryInterface(IID_ISlangMutableFileSystem, (void**)out.writeRef())))
+ {
+ style = FileSystemStyle::Mutable;
+ }
+ else if (SLANG_SUCCEEDED(system->queryInterface(IID_ISlangFileSystemExt, (void**)out.writeRef())))
+ {
+ style = FileSystemStyle::Ext;
+ }
+ else
+ {
+ style = FileSystemStyle::Load;
+ out = system;
+ }
+
+ SLANG_ASSERT(out);
+ return style;
+}
+
+// Cacluate a combined path, just using Path:: string processing
+static SlangResult _calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut)
+{
+ String relPath;
+ switch (fromPathType)
+ {
+ case SLANG_PATH_TYPE_FILE:
+ {
+ relPath = Path::combine(Path::getParentDirectory(fromPath), path);
+ break;
+ }
+ case SLANG_PATH_TYPE_DIRECTORY:
+ {
+ relPath = Path::combine(fromPath, path);
+ break;
+ }
+ }
+
+ *pathOut = StringUtil::createStringBlob(relPath).detach();
+ return SLANG_OK;
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! OSFileSystem !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
+
+/* static */OSFileSystem OSFileSystem::g_load(FileSystemStyle::Load);
+/* static */OSFileSystem OSFileSystem::g_ext(FileSystemStyle::Ext);
+/* static */OSFileSystem OSFileSystem::g_mutable(FileSystemStyle::Mutable);
+
+ISlangUnknown* OSFileSystem::getInterface(const Guid& guid)
+{
+ return _canCast(m_style, guid) ? static_cast<ISlangFileSystem*>(this) : nullptr;
+}
+
+static String _fixPathDelimiters(const char* pathIn)
+{
+#if SLANG_WINDOWS_FAMILY
+ return pathIn;
+#else
+ // To allow windows style \ delimiters on other platforms, we convert to our standard delimiter
+ String path(pathIn);
+ return StringUtil::calcCharReplaced(pathIn, '\\', Path::kPathDelimiter);
+#endif
+}
+
+SlangResult OSFileSystem::getFileUniqueIdentity(const char* pathIn, ISlangBlob** outUniqueIdentity)
+{
+ SLANG_RETURN_ON_FAIL(_checkExt(m_style));
+
+ // By default we use the canonical path to uniquely identify a file
+ return getCanonicalPath(pathIn, outUniqueIdentity);
+}
+
+SlangResult OSFileSystem::getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath)
+{
+ SLANG_RETURN_ON_FAIL(_checkExt(m_style));
+
+ String canonicalPath;
+ SLANG_RETURN_ON_FAIL(Path::getCanonical(_fixPathDelimiters(path), canonicalPath));
+ *outCanonicalPath = StringUtil::createStringBlob(canonicalPath).detach();
+ return SLANG_OK;
+}
+
+SlangResult OSFileSystem::getSimplifiedPath(const char* pathIn, ISlangBlob** outSimplifiedPath)
+{
+ SLANG_RETURN_ON_FAIL(_checkExt(m_style));
+
+ String simplifiedPath = Path::simplify(pathIn);
+ *outSimplifiedPath = StringUtil::createStringBlob(simplifiedPath).detach();
+ return SLANG_OK;
+}
+
+SlangResult OSFileSystem::calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut)
+{
+ SLANG_RETURN_ON_FAIL(_checkExt(m_style));
+
+ // Don't need to fix delimiters - because combine path handles both path delimiter types
+ return _calcCombinedPath(fromPathType, fromPath, path, pathOut);
+}
+
+SlangResult SLANG_MCALL OSFileSystem::getPathType(const char* pathIn, SlangPathType* pathTypeOut)
+{
+ SLANG_RETURN_ON_FAIL(_checkExt(m_style));
+
+ return Path::getPathType(_fixPathDelimiters(pathIn), pathTypeOut);
+}
+
+
+SlangResult OSFileSystem::loadFile(char const* pathIn, ISlangBlob** outBlob)
+{
+ // Default implementation that uses the `core` libraries facilities for talking to the OS filesystem.
+ //
+ // TODO: we might want to conditionally compile these in, so that
+ // a user could create a build of Slang that doesn't include any OS
+ // filesystem calls.
+
+ const String path = _fixPathDelimiters(pathIn);
+ if (!File::exists(path))
+ {
+ return SLANG_E_NOT_FOUND;
+ }
+
+ ScopedAllocation alloc;
+ SLANG_RETURN_ON_FAIL(File::readAllBytes(path, alloc));
+ *outBlob = RawBlob::moveCreate(alloc).detach();
+ return SLANG_OK;
+}
+
+SlangResult OSFileSystem::enumeratePathContents(const char* path, FileSystemContentsCallBack callback, void* userData)
+{
+ SLANG_RETURN_ON_FAIL(_checkExt(m_style));
+
+ struct Visitor : Path::Visitor
+ {
+ void accept(Path::Type type, const UnownedStringSlice& filename) SLANG_OVERRIDE
+ {
+ m_buffer.Clear();
+ m_buffer.append(filename);
+
+ SlangPathType pathType;
+ switch (type)
+ {
+ case Path::Type::File: pathType = SLANG_PATH_TYPE_FILE; break;
+ case Path::Type::Directory: pathType = SLANG_PATH_TYPE_DIRECTORY; break;
+ default: return;
+ }
+
+ m_callback(pathType, m_buffer.getBuffer(), m_userData);
+ }
+
+ Visitor(FileSystemContentsCallBack callback, void* userData) :
+ m_callback(callback),
+ m_userData(userData)
+ {
+ }
+ StringBuilder m_buffer;
+ FileSystemContentsCallBack m_callback;
+ void* m_userData;
+ };
+
+ Visitor visitor(callback, userData);
+ Path::find(path, nullptr, &visitor);
+
+ return SLANG_OK;
+}
+
+SlangResult OSFileSystem::saveFile(const char* pathIn, const void* data, size_t size)
+{
+ SLANG_RETURN_ON_FAIL(_checkMutable(m_style));
+
+ const String path = _fixPathDelimiters(pathIn);
+
+ try
+ {
+ FileStream stream(pathIn, FileMode::Create, FileAccess::Write, FileShare::ReadWrite);
+
+ int64_t numWritten = stream.write(data, size);
+
+ if (numWritten != int64_t(size))
+ {
+ return SLANG_FAIL;
+ }
+
+ }
+ catch (const IOException&)
+ {
+ return SLANG_E_CANNOT_OPEN;
+ }
+
+ return SLANG_OK;
+}
+
+SlangResult OSFileSystem::remove(const char* path)
+{
+ SLANG_RETURN_ON_FAIL(_checkMutable(m_style));
+ return Path::remove(path);
+}
+
+SlangResult OSFileSystem::createDirectory(const char* path)
+{
+ SLANG_RETURN_ON_FAIL(_checkMutable(m_style));
+ return Path::createDirectory(path);
+}
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CacheFileSystem !!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+/* static */ const Result CacheFileSystem::s_compressedResultToResult[] =
+{
+ SLANG_E_UNINITIALIZED,
+ SLANG_OK, ///< Ok
+ SLANG_E_NOT_FOUND, ///< File not found
+ SLANG_E_CANNOT_OPEN, ///< CannotOpen,
+ SLANG_FAIL, ///< Fail
+};
+
+/* static */CacheFileSystem::CompressedResult CacheFileSystem::toCompressedResult(Result res)
+{
+ if (SLANG_SUCCEEDED(res))
+ {
+ return CompressedResult::Ok;
+ }
+ switch (res)
+ {
+ case SLANG_E_CANNOT_OPEN: return CompressedResult::CannotOpen;
+ case SLANG_E_NOT_FOUND: return CompressedResult::NotFound;
+ default: return CompressedResult::Fail;
+ }
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL CacheFileSystem::queryInterface(SlangUUID const& uuid, void** outObject)
+{
+ if (uuid == IID_SlangCacheFileSystem)
+ {
+ *outObject = this;
+ return SLANG_OK;
+ }
+
+ if (_canCast(FileSystemStyle::Ext, uuid))
+ {
+ addReference();
+ *outObject = static_cast<ISlangFileSystemExt*>(this);
+ return SLANG_OK;
+ }
+ return SLANG_E_NO_INTERFACE;
+}
+
+CacheFileSystem::CacheFileSystem(ISlangFileSystem* fileSystem, UniqueIdentityMode uniqueIdentityMode, PathStyle pathStyle)
+{
+ setInnerFileSystem(fileSystem, uniqueIdentityMode, pathStyle);
+}
+
+CacheFileSystem::~CacheFileSystem()
+{
+ for (const auto& pair : m_uniqueIdentityMap)
+ {
+ PathInfo* pathInfo = pair.Value;
+ delete pathInfo;
+ }
+}
+
+void CacheFileSystem::setInnerFileSystem(ISlangFileSystem* fileSystem, UniqueIdentityMode uniqueIdentityMode, PathStyle pathStyle)
+{
+ m_fileSystem = fileSystem;
+
+ m_uniqueIdentityMode = uniqueIdentityMode;
+ m_pathStyle = pathStyle;
+
+ m_fileSystemExt.setNull();
+
+ if (fileSystem)
+ {
+ // Try to get the more sophisticated interface
+ fileSystem->queryInterface(IID_ISlangFileSystemExt, (void**)m_fileSystemExt.writeRef());
+ }
+
+ switch (m_uniqueIdentityMode)
+ {
+ case UniqueIdentityMode::Default:
+ case UniqueIdentityMode::FileSystemExt:
+ {
+ // If it's not a complete file system, we will default to SimplifyAndHash style by default
+ m_uniqueIdentityMode = m_fileSystemExt ? UniqueIdentityMode::FileSystemExt : UniqueIdentityMode::SimplifyPathAndHash;
+ break;
+ }
+ default: break;
+ }
+
+ if (pathStyle == PathStyle::Default)
+ {
+ // We'll assume it's simplify-able
+ m_pathStyle = PathStyle::Simplifiable;
+ // If we have fileSystemExt, we defer to that
+ if (m_fileSystemExt)
+ {
+ // We just defer to the m_fileSystem
+ m_pathStyle = PathStyle::FileSystemExt;
+ }
+ }
+
+ // It can't be default
+ SLANG_ASSERT(m_uniqueIdentityMode != UniqueIdentityMode::Default);
+}
+
+void CacheFileSystem::clearCache()
+{
+ for (const auto& pair : m_uniqueIdentityMap)
+ {
+ PathInfo* pathInfo = pair.Value;
+ delete pathInfo;
+ }
+
+ m_uniqueIdentityMap.Clear();
+ m_pathMap.Clear();
+
+ if (m_fileSystemExt)
+ {
+ m_fileSystemExt->clearCache();
+ }
+}
+
+
+// Determines if we can simplify a path for a given mode
+static bool _canSimplifyPath(CacheFileSystem::UniqueIdentityMode mode)
+{
+ typedef CacheFileSystem::UniqueIdentityMode UniqueIdentityMode;
+ switch (mode)
+ {
+ case UniqueIdentityMode::SimplifyPath:
+ case UniqueIdentityMode::SimplifyPathAndHash:
+ {
+ return true;
+ }
+ default:
+ {
+ return false;
+ }
+ }
+}
+
+SlangResult CacheFileSystem::enumeratePathContents(const char* path, FileSystemContentsCallBack callback, void* userData)
+{
+ if (m_fileSystemExt)
+ {
+ return m_fileSystemExt->enumeratePathContents(path, callback, userData);
+ }
+
+ // Okay.. the contents of the 'cache' *is* the filesystem. So lets iterate over that
+ // This will win no prizes for efficiency, but that is unlikely to matter for typical usage
+
+ if (!_canSimplifyPath(m_uniqueIdentityMode))
+ {
+ // As it stands if we can't simplify paths, it's kind of hard to make this
+ // all work. As we use the simplified path cache
+ return SLANG_E_NOT_IMPLEMENTED;
+ }
+
+ // Simplify the path
+ String simplifiedPath = Path::simplify(path);
+
+ // If the simplified path is just a . then we don't have any prefix
+ if (simplifiedPath == ".")
+ {
+ simplifiedPath = "";
+ }
+
+ for (auto& pair : m_pathMap)
+ {
+ // NOTE! The currentPath can be a *non* simplified path (the m_pathMap is the cache of paths simplified and other to a file/directory)
+ // Also note that there will always be the simplified version of the path in cache.
+ const String& currentPath = pair.Key;
+
+ // If it doesn't start with simplified path, then it can't be a hit
+ if (!currentPath.startsWith(simplifiedPath))
+ {
+ continue;
+ }
+
+ UnownedStringSlice remaining(currentPath.getBuffer() + simplifiedPath.getLength(), currentPath.end());
+
+ // If it starts with a / delimiter strip it
+ if (remaining.getLength() > 0 && remaining[0] == '/')
+ {
+ remaining = UnownedStringSlice(remaining.begin() + 1, remaining.end());
+ }
+
+ // If it has a path separator then it's either not simplified - so we ignore (we only want to invoke on the simplified path version as there is only one
+ // of these for every PathInfo)
+ // or it is a child file/directory, and so we ignore that too.
+ if (remaining.indexOf('/') >= 0 || remaining.indexOf('\\') >= 0)
+ {
+ continue;
+ }
+
+ // We *know* that remaining comes from the end of currentPath .We also know currentPath is zero terminated.
+ // So we can just use (normally this would be a problem because UnownedStringSlice is generally *not* followed by zero termination.
+ const char* foundPath = remaining.begin();
+ // Let's check that fact...
+ SLANG_ASSERT(foundPath[remaining.getLength()] == 0);
+
+ PathInfo* pathInfo = pair.Value;
+
+ SlangPathType pathType;
+ if (SLANG_FAILED(_getPathType(pathInfo, currentPath.getBuffer(), &pathType)))
+ {
+ continue;
+ }
+
+ callback(pathType, foundPath, userData);
+ }
+
+ return SLANG_OK;
+}
+
+
+SlangResult CacheFileSystem::_calcUniqueIdentity(const String& path, String& outUniqueIdentity, ComPtr<ISlangBlob>& outFileContents)
+{
+ switch (m_uniqueIdentityMode)
+ {
+ case UniqueIdentityMode::FileSystemExt:
+ {
+ // Try getting the uniqueIdentity by asking underlying file system
+ ComPtr<ISlangBlob> uniqueIdentity;
+ SLANG_RETURN_ON_FAIL(m_fileSystemExt->getFileUniqueIdentity(path.getBuffer(), uniqueIdentity.writeRef()));
+ // Get the path as a string
+ outUniqueIdentity = StringUtil::getString(uniqueIdentity);
+ return SLANG_OK;
+ }
+ case UniqueIdentityMode::Path:
+ {
+ outUniqueIdentity = path;
+ return SLANG_OK;
+ }
+ case UniqueIdentityMode::SimplifyPath:
+ {
+ outUniqueIdentity = Path::simplify(path);
+ // If it still has relative elements can't uniquely identify, so give up
+ return Path::hasRelativeElement(outUniqueIdentity) ? SLANG_FAIL : SLANG_OK;
+ }
+ case UniqueIdentityMode::SimplifyPathAndHash:
+ case UniqueIdentityMode::Hash:
+ {
+ // If we don't have a file system -> assume cannot be found
+ if (m_fileSystem == nullptr)
+ {
+ return SLANG_E_NOT_FOUND;
+ }
+
+ // I can only see if this is the same file as already loaded by loading the file and doing a hash
+ Result res = m_fileSystem->loadFile(path.getBuffer(), outFileContents.writeRef());
+ if (SLANG_FAILED(res) || outFileContents == nullptr)
+ {
+ return SLANG_FAIL;
+ }
+
+ // Calculate the hash on the contents
+ const uint64_t hash = getHashCode64((const char*)outFileContents->getBufferPointer(), outFileContents->getBufferSize());
+
+ String hashString = Path::getFileName(path);
+ hashString = hashString.toLower();
+
+ hashString.append(':');
+
+ // The uniqueIdentity is a combination of name and hash
+ hashString.append(hash, 16);
+
+ outUniqueIdentity = hashString;
+ return SLANG_OK;
+ }
+ }
+
+ return SLANG_FAIL;
+}
+
+CacheFileSystem::PathInfo* CacheFileSystem::_resolveUniqueIdentityCacheInfo(const String& path)
+{
+ // Use the path to produce uniqueIdentity information
+ ComPtr<ISlangBlob> fileContents;
+ String uniqueIdentity;
+
+ SlangResult res = _calcUniqueIdentity(path, uniqueIdentity, fileContents);
+ if (SLANG_FAILED(res))
+ {
+ // Was not able to create a uniqueIdentity - return failure as nullptr
+ return nullptr;
+ }
+
+ // Now try looking up by uniqueIdentity path. If not found, add a new result
+ PathInfo* pathInfo = nullptr;
+ if (!m_uniqueIdentityMap.TryGetValue(uniqueIdentity, pathInfo))
+ {
+ // Create with found uniqueIdentity
+ pathInfo = new PathInfo(uniqueIdentity);
+ m_uniqueIdentityMap.Add(uniqueIdentity, pathInfo);
+ }
+
+ // At this point they must have same uniqueIdentity
+ SLANG_ASSERT(pathInfo->getUniqueIdentity() == uniqueIdentity);
+
+ // If we have the file contents (because of calc-ing uniqueIdentity), and there isn't a read file blob already
+ // store the data as if read, so doesn't get read again
+ if (fileContents && !pathInfo->m_fileBlob)
+ {
+ pathInfo->m_fileBlob = fileContents;
+ pathInfo->m_loadFileResult = CompressedResult::Ok;
+ }
+
+ return pathInfo;
+}
+
+CacheFileSystem::PathInfo* CacheFileSystem::_resolveSimplifiedPathCacheInfo(const String& path)
+{
+ // If we can simplify the path, try looking up in path cache with simplified path (as long as it's different!)
+ if (_canSimplifyPath(m_uniqueIdentityMode))
+ {
+ const String simplifiedPath = Path::simplify(path);
+ // Only lookup if the path is different - because otherwise will recurse forever...
+ if (simplifiedPath != path)
+ {
+ // This is a recursive call - and will ensure the simplified path is added to the cache
+ return _resolvePathCacheInfo(simplifiedPath);
+ }
+ }
+
+ return _resolveUniqueIdentityCacheInfo(path);
+}
+
+CacheFileSystem::PathInfo* CacheFileSystem::_resolvePathCacheInfo(const String& path)
+{
+ // Lookup in path cache
+ PathInfo* pathInfo;
+ if (m_pathMap.TryGetValue(path, pathInfo))
+ {
+ // Found so done
+ return pathInfo;
+ }
+
+ // Try getting or creating taking into account possible path simplification
+ pathInfo = _resolveSimplifiedPathCacheInfo(path);
+ // Always add the result to the path cache (even if null)
+ m_pathMap.Add(path, pathInfo);
+ return pathInfo;
+}
+
+SlangResult CacheFileSystem::loadFile(char const* pathIn, ISlangBlob** blobOut)
+{
+ *blobOut = nullptr;
+ String path(pathIn);
+ PathInfo* info = _resolvePathCacheInfo(path);
+ if (!info)
+ {
+ return SLANG_FAIL;
+ }
+
+ if (info->m_loadFileResult == CompressedResult::Uninitialized)
+ {
+ info->m_loadFileResult = toCompressedResult(m_fileSystem->loadFile(path.getBuffer(), info->m_fileBlob.writeRef()));
+ }
+
+ *blobOut = info->m_fileBlob;
+ if (*blobOut)
+ {
+ (*blobOut)->addRef();
+ }
+ return toResult(info->m_loadFileResult);
+}
+
+SlangResult CacheFileSystem::getFileUniqueIdentity(const char* path, ISlangBlob** outUniqueIdentity)
+{
+ PathInfo* info = _resolvePathCacheInfo(path);
+ if (!info)
+ {
+ return SLANG_E_NOT_FOUND;
+ }
+ info->m_uniqueIdentity->addRef();
+ *outUniqueIdentity = info->m_uniqueIdentity;
+ return SLANG_OK;
+}
+
+SlangResult CacheFileSystem::calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut)
+{
+ // Just defer to contained implementation
+ switch (m_pathStyle)
+ {
+ case PathStyle::FileSystemExt:
+ {
+ return m_fileSystemExt->calcCombinedPath(fromPathType, fromPath, path, pathOut);
+ }
+ default:
+ {
+ // Just use the default implementation
+ return _calcCombinedPath(fromPathType, fromPath, path, pathOut);
+ }
+ }
+}
+
+SlangResult CacheFileSystem::_getPathType(PathInfo* info, const char* inPath, SlangPathType* outPathType)
+{
+ if (info->m_getPathTypeResult == CompressedResult::Uninitialized)
+ {
+ if (m_fileSystemExt)
+ {
+ info->m_getPathTypeResult = toCompressedResult(m_fileSystemExt->getPathType(inPath, &info->m_pathType));
+ }
+ else
+ {
+ // Okay try to load the file
+ if (info->m_loadFileResult == CompressedResult::Uninitialized)
+ {
+ info->m_loadFileResult = toCompressedResult(m_fileSystem->loadFile(inPath, info->m_fileBlob.writeRef()));
+ }
+
+ // Make the getPathResult the same as the load result
+ info->m_getPathTypeResult = info->m_loadFileResult;
+ // Just set to file... the result is what matters in this case
+ info->m_pathType = SLANG_PATH_TYPE_FILE;
+ }
+ }
+
+ *outPathType = info->m_pathType;
+ return toResult(info->m_getPathTypeResult);
+}
+
+SlangResult CacheFileSystem::getPathType(const char* inPath, SlangPathType* outPathType)
+{
+ PathInfo* info = _resolvePathCacheInfo(inPath);
+ if (!info)
+ {
+ return SLANG_E_NOT_FOUND;
+ }
+
+ return _getPathType(info, inPath, outPathType);
+}
+
+SlangResult CacheFileSystem::getSimplifiedPath(const char* path, ISlangBlob** outSimplifiedPath)
+{
+ // If we have a ISlangFileSystemExt we can just pass on the request to it
+ switch (m_pathStyle)
+ {
+ case PathStyle::FileSystemExt:
+ {
+ return m_fileSystemExt->getSimplifiedPath(path, outSimplifiedPath);
+ }
+ case PathStyle::Simplifiable:
+ {
+ String simplifiedPath = Path::simplify(path);
+ *outSimplifiedPath = StringUtil::createStringBlob(simplifiedPath).detach();
+ return SLANG_OK;
+ }
+ default: return SLANG_E_NOT_IMPLEMENTED;
+ }
+}
+
+SlangResult CacheFileSystem::getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath)
+{
+ // A file must exist to get a canonical path...
+ PathInfo* info = _resolvePathCacheInfo(path);
+ if (!info)
+ {
+ return SLANG_E_NOT_FOUND;
+ }
+
+ // We don't have this -> so read it ...
+ if (info->m_getCanonicalPathResult == CompressedResult::Uninitialized)
+ {
+ if (!m_fileSystemExt)
+ {
+ return SLANG_E_NOT_IMPLEMENTED;
+ }
+
+ // Try getting the canonicalPath by asking underlying file system
+ ComPtr<ISlangBlob> canonicalPathBlob;
+ SlangResult res = m_fileSystemExt->getCanonicalPath(path, canonicalPathBlob.writeRef());
+
+ if (SLANG_SUCCEEDED(res))
+ {
+ // Get the path as a string
+ String canonicalPath = StringUtil::getString(canonicalPathBlob);
+ if (canonicalPath.getLength() > 0)
+ {
+ info->m_canonicalPath = new StringBlob(canonicalPath);
+ }
+ else
+ {
+ res = SLANG_FAIL;
+ }
+ }
+
+ // Save the result
+ info->m_getCanonicalPathResult = toCompressedResult(res);
+ }
+
+ if (info->m_canonicalPath)
+ {
+ info->m_canonicalPath->addRef();
+ }
+ *outCanonicalPath = info->m_canonicalPath;
+ return SLANG_OK;
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! RelativeFileSystem !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+RelativeFileSystem::RelativeFileSystem(ISlangFileSystem* fileSystem, const String& relativePath, bool stripPath) :
+ m_relativePath(relativePath),
+ m_stripPath(stripPath)
+{
+ m_style = _getFileSystemStyle(fileSystem, m_fileSystem);
+}
+
+ISlangUnknown* RelativeFileSystem::getInterface(const Guid& guid)
+{
+ return _canCast(m_style, guid) ? static_cast<ISlangMutableFileSystem*>(this) : nullptr;
+}
+
+SlangResult RelativeFileSystem::_calcCombinedPathInner(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** outPath)
+{
+ ISlangFileSystemExt* fileSystem = _getExt();
+ if (fileSystem)
+ {
+ return fileSystem->calcCombinedPath(fromPathType, fromPath, path, outPath);
+ }
+ else
+ {
+ return _calcCombinedPath(fromPathType, fromPath, path, outPath);
+ }
+}
+
+SlangResult RelativeFileSystem::_getFixedPath(const char* path, String& outPath)
+{
+ ComPtr<ISlangBlob> blob;
+ if (m_stripPath)
+ {
+ String strippedPath = Path::getFileName(path);
+ SLANG_RETURN_ON_FAIL(_calcCombinedPathInner(SLANG_PATH_TYPE_DIRECTORY, m_relativePath.getBuffer(), strippedPath.getBuffer(), blob.writeRef()));
+ }
+ else
+ {
+ SLANG_RETURN_ON_FAIL(_calcCombinedPathInner(SLANG_PATH_TYPE_DIRECTORY, m_relativePath.getBuffer(), path, blob.writeRef()));
+ }
+
+ outPath = StringUtil::getString(blob);
+ return SLANG_OK;
+}
+
+SlangResult RelativeFileSystem::loadFile(char const* path, ISlangBlob** outBlob)
+{
+ String fixedPath;
+ SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath));
+ return m_fileSystem->loadFile(fixedPath.getBuffer(), outBlob);
+}
+
+SlangResult RelativeFileSystem::getFileUniqueIdentity(const char* path, ISlangBlob** outUniqueIdentity)
+{
+ auto fileSystem = _getExt();
+ if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED;
+
+ String fixedPath;
+ SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath));
+ return fileSystem->getFileUniqueIdentity(fixedPath.getBuffer(), outUniqueIdentity);
+}
+
+SlangResult RelativeFileSystem::calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** outPath)
+{
+ auto fileSystem = _getExt();
+ if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED;
+
+ String fixedFromPath;
+ SLANG_RETURN_ON_FAIL(_getFixedPath(fromPath, fixedFromPath));
+
+ return fileSystem->calcCombinedPath(fromPathType, fixedFromPath.getBuffer(), path, outPath);
+}
+
+SlangResult RelativeFileSystem::getPathType(const char* path, SlangPathType* outPathType)
+{
+ auto fileSystem = _getExt();
+ if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED;
+
+ String fixedPath;
+ SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath));
+ return fileSystem->getPathType(fixedPath.getBuffer(), outPathType);
+}
+
+SlangResult RelativeFileSystem::getSimplifiedPath(const char* path, ISlangBlob** outSimplifiedPath)
+{
+ auto fileSystem = _getExt();
+ if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED;
+
+ return fileSystem->getSimplifiedPath(path, outSimplifiedPath);
+}
+
+SlangResult RelativeFileSystem::getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath)
+{
+ auto fileSystem = _getExt();
+ if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED;
+
+ String fixedPath;
+ SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath));
+ return fileSystem->getCanonicalPath(fixedPath.getBuffer(), outCanonicalPath);
+}
+
+void RelativeFileSystem::clearCache()
+{
+ auto fileSystem = _getExt();
+ if (!fileSystem) return;
+
+ fileSystem->clearCache();
+}
+
+SlangResult RelativeFileSystem::enumeratePathContents(const char* path, FileSystemContentsCallBack callback, void* userData)
+{
+ auto fileSystem = _getExt();
+ if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED;
+
+ String fixedPath;
+ SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath));
+ return fileSystem->enumeratePathContents(fixedPath.getBuffer(), callback, userData);
+}
+
+SlangResult RelativeFileSystem::saveFile(const char* path, const void* data, size_t size)
+{
+ auto fileSystem = _getMutable();
+ if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED;
+
+ String fixedPath;
+ SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath));
+ return fileSystem->saveFile(fixedPath.getBuffer(), data, size);
+}
+
+SlangResult RelativeFileSystem::remove(const char* path)
+{
+ auto fileSystem = _getMutable();
+ if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED;
+
+ String fixedPath;
+ SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath));
+ return fileSystem->remove(fixedPath.getBuffer());
+}
+
+SlangResult RelativeFileSystem::createDirectory(const char* path)
+{
+ auto fileSystem = _getMutable();
+ if (!fileSystem) return SLANG_E_NOT_IMPLEMENTED;
+
+ String fixedPath;
+ SLANG_RETURN_ON_FAIL(_getFixedPath(path, fixedPath));
+ return fileSystem->createDirectory(fixedPath.getBuffer());
+}
+
+}
diff --git a/source/core/slang-file-system.h b/source/core/slang-file-system.h
new file mode 100644
index 000000000..d5145404d
--- /dev/null
+++ b/source/core/slang-file-system.h
@@ -0,0 +1,253 @@
+#ifndef SLANG_FILE_SYSTEM_H_INCLUDED
+#define SLANG_FILE_SYSTEM_H_INCLUDED
+
+#include "../../slang.h"
+#include "../../slang-com-helper.h"
+#include "../../slang-com-ptr.h"
+
+#include "../core/slang-blob.h"
+
+#include "../core/slang-string-util.h"
+#include "../core/slang-dictionary.h"
+
+namespace Slang
+{
+
+enum class FileSystemStyle
+{
+ Load, ///< Equivalent to ISlangFileSystem
+ Ext, ///< Equivalent to ISlangFileSystemExt
+ Mutable, ///< Equivalent to ISlangModifyableFileSystem
+};
+
+// Can be used for all styles of file system
+class OSFileSystem : public ISlangMutableFileSystem
+{
+public:
+ // ISlangUnknown
+ // override ref counting, as DefaultFileSystem is singleton
+ SLANG_IUNKNOWN_QUERY_INTERFACE
+ SLANG_NO_THROW uint32_t SLANG_MCALL addRef() SLANG_OVERRIDE { return 1; }
+ SLANG_NO_THROW uint32_t SLANG_MCALL release() SLANG_OVERRIDE { return 1; }
+
+ // ISlangFileSystem
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadFile(char const* path, ISlangBlob** outBlob) SLANG_OVERRIDE;
+
+ // ISlangFileSystemExt
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getFileUniqueIdentity(const char* path, ISlangBlob** uniqueIdentityOut) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getPathType(const char* path, SlangPathType* pathTypeOut) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getSimplifiedPath(const char* path, ISlangBlob** outSimplifiedPath) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL clearCache() SLANG_OVERRIDE {}
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL enumeratePathContents(const char* path, FileSystemContentsCallBack callback, void* userData) SLANG_OVERRIDE;
+
+ // ISlangModifyableFileSystem
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL saveFile(const char* path, const void* data, size_t size) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL remove(const char* path) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL createDirectory(const char* path) SLANG_OVERRIDE;
+
+ /// Get a default instance
+ static ISlangFileSystem* getLoadSingleton() { return &g_load; }
+ static ISlangFileSystemExt* getExtSingleton() { return &g_ext; }
+ static ISlangMutableFileSystem* getMutableSingleton() { return &g_mutable; }
+
+private:
+
+ /// Make so not constructible
+ OSFileSystem(FileSystemStyle style):
+ m_style(style)
+ {}
+
+ virtual ~OSFileSystem() {}
+
+ ISlangUnknown* getInterface(const Guid& guid);
+
+ FileSystemStyle m_style;
+
+ static OSFileSystem g_load;
+ static OSFileSystem g_ext;
+ static OSFileSystem g_mutable;
+};
+
+ #define SLANG_UUID_CacheFileSystem { 0x2f4d1d03, 0xa0d1, 0x434b, { 0x87, 0x7a, 0x65, 0x5, 0xa4, 0xa0, 0x9a, 0x3b } };
+
+/* Wraps an underlying ISlangFileSystem or ISlangFileSystemExt and provides caching,
+as well as emulation of methods if only has ISlangFileSystem interface. Will query capabilities
+of the interface on the constructor.
+
+NOTE! That this behavior is the same as previously in that....
+1) calcRelativePath, just returns the path as processed by the Path:: methods
+2) getUniqueIdentity behavior depends on the UniqueIdentityMode.
+*/
+class CacheFileSystem: public ISlangFileSystemExt, public RefObject
+{
+ public:
+
+ enum class PathStyle
+ {
+ Default, ///< Pass to say use the default
+ Simplifiable, ///< It can be simplified by Path::Simplify
+ FileSystemExt, ///< Use file system
+ };
+
+ enum UniqueIdentityMode
+ {
+ Default, ///< If passed, will default to the others depending on what kind of ISlangFileSystem is passed in
+ Path, ///< Just use the path as is (old style slang behavior)
+ SimplifyPath, ///< Use the input path 'simplified' (ie removing . and .. aspects)
+ Hash, ///< Use hashing
+ SimplifyPathAndHash, ///< Tries simplifying path first, and if that doesn't work it hashes
+ FileSystemExt, ///< Use the file system extended interface.
+ };
+
+ /* Cannot change order/add members without changing s_compressedResultToResult */
+ enum class CompressedResult: uint8_t
+ {
+ Uninitialized, ///< Holds no value
+ Ok, ///< Ok
+ NotFound, ///< File not found
+ CannotOpen, ///< Cannot open
+ Fail, ///< Generic failure
+ CountOf,
+ };
+
+ struct PathInfo
+ {
+ PathInfo(const String& uniqueIdentity)
+ {
+ m_uniqueIdentity = new StringBlob(uniqueIdentity);
+ m_uniqueIdentity->addRef();
+
+ m_loadFileResult = CompressedResult::Uninitialized;
+ m_getPathTypeResult = CompressedResult::Uninitialized;
+ m_getCanonicalPathResult = CompressedResult::Uninitialized;
+
+ m_pathType = SLANG_PATH_TYPE_FILE;
+ }
+
+ /// Get the unique identity path as a string
+ const String& getUniqueIdentity() const { SLANG_ASSERT(m_uniqueIdentity); return m_uniqueIdentity->getString(); }
+
+ RefPtr<StringBlob> m_uniqueIdentity;
+ CompressedResult m_loadFileResult;
+ CompressedResult m_getPathTypeResult;
+ CompressedResult m_getCanonicalPathResult;
+
+ SlangPathType m_pathType;
+ ComPtr<ISlangBlob> m_fileBlob;
+ RefPtr<StringBlob> m_canonicalPath;
+ };
+
+ Dictionary<String, PathInfo*>& getPathMap() { return m_pathMap; }
+ Dictionary<String, PathInfo*>& getUniqueMap() { return m_uniqueIdentityMap; }
+
+ // ISlangUnknown
+ SLANG_NO_THROW SlangResult SLANG_MCALL queryInterface(SlangUUID const& uuid, void** outObject) SLANG_OVERRIDE;
+ SLANG_REF_OBJECT_IUNKNOWN_ADD_REF
+ SLANG_REF_OBJECT_IUNKNOWN_RELEASE
+
+ // ISlangFileSystem
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadFile(char const* path, ISlangBlob** outBlob) SLANG_OVERRIDE;
+
+ // ISlangFileSystemExt
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getFileUniqueIdentity(const char* path, ISlangBlob** outUniqueIdentity) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getPathType(const char* path, SlangPathType* outPathType) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getSimplifiedPath(const char* path, ISlangBlob** outSimplifiedPath) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL clearCache() SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL enumeratePathContents(const char* path, FileSystemContentsCallBack callback, void* userData) SLANG_OVERRIDE;
+
+ /// Get the unique identity mode
+ UniqueIdentityMode getUniqueIdentityMode() const { return m_uniqueIdentityMode; }
+ /// Get the path style
+ PathStyle getPathStyle() const { return m_pathStyle; }
+
+ /// Set the inner file system
+ void setInnerFileSystem(ISlangFileSystem* fileSystem, UniqueIdentityMode uniqueIdentityMode = UniqueIdentityMode::Default, PathStyle pathStyle = PathStyle::Default);
+
+ /// Ctor
+ CacheFileSystem(ISlangFileSystem* fileSystem, UniqueIdentityMode uniqueIdentityMode = UniqueIdentityMode::Default, PathStyle pathStyle = PathStyle::Default);
+ /// Dtor
+ virtual ~CacheFileSystem();
+
+ static CompressedResult toCompressedResult(Result res);
+ static Result toResult(CompressedResult compRes) { return s_compressedResultToResult[int(compRes)]; }
+ static const Result s_compressedResultToResult[int(CompressedResult::CountOf)];
+
+protected:
+
+ /// Given a path, works out a uniqueIdentity, based on the uniqueIdentityMode. outFileContents will be set if file had to be read to produce the uniqueIdentity (ie with Hash)
+ SlangResult _calcUniqueIdentity(const String& path, String& outUniqueIdentity, ComPtr<ISlangBlob>& outFileContents);
+
+ /// For a given path gets a PathInfo. Can return nullptr, if it is not possible to create the PathInfo for some reason
+ PathInfo* _resolvePathCacheInfo(const String& path);
+ /// Turns the path into a uniqueIdentity, and then tries to look up in the uniqueIdentityMap.
+ PathInfo* _resolveUniqueIdentityCacheInfo(const String& path);
+ /// Will simplify the path (if possible) to lookup on the pathCache else will create on uniqueIdentityMap
+ PathInfo* _resolveSimplifiedPathCacheInfo(const String& path);
+
+ SlangResult _getPathType(PathInfo* pathInfo, const char* inPath, SlangPathType* pathTypeOut);
+
+ /* TODO: This may be improved by mapping to a ISlangBlob. This makes output fast and easy, and if constructed
+ as a StringBlob, we can just static_cast to get as a string to use internally, instead of constantly converting.
+ It is probably the case we cannot do dynamic_cast on ISlangBlob if we don't know where constructed -> if outside of slang codebase
+ doing such a cast can cause an exception. So we *never* want to do dynamic cast from blobs which could be created by external code. */
+
+ Dictionary<String, PathInfo*> m_pathMap; ///< Maps a path to a PathInfo (and unique identity)
+ Dictionary<String, PathInfo*> m_uniqueIdentityMap; ///< Maps a unique identity for a file to its contents. This OWNs the PathInfo.
+
+ UniqueIdentityMode m_uniqueIdentityMode; ///< Determines how the 'uniqueIdentity' is produced. Cannot be Default in usage.
+ PathStyle m_pathStyle; ///< Style of paths
+
+ ComPtr<ISlangFileSystem> m_fileSystem; ///< Must always be set
+ ComPtr<ISlangFileSystemExt> m_fileSystemExt; ///< Optionally set -> if nullptr will fall back on the m_fileSystem and emulate all the other methods of ISlangFileSystemExt
+};
+
+class RelativeFileSystem : public ISlangMutableFileSystem, public RefObject
+{
+public:
+ SLANG_REF_OBJECT_IUNKNOWN_ALL
+
+ // ISlangFileSystem
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadFile(char const* path, ISlangBlob** outBlob) SLANG_OVERRIDE;
+
+ // ISlangFileSystemExt
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getFileUniqueIdentity(const char* path, ISlangBlob** outUniqueIdentity) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL calcCombinedPath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getPathType(const char* path, SlangPathType* outPathType) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getSimplifiedPath(const char* path, ISlangBlob** outSimplifiedPath) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getCanonicalPath(const char* path, ISlangBlob** outCanonicalPath) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL clearCache() SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL enumeratePathContents(const char* path, FileSystemContentsCallBack callback, void* userData) SLANG_OVERRIDE;
+
+ // ISlangModifyableFileSystem
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL saveFile(const char* path, const void* data, size_t size) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL remove(const char* path) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL createDirectory(const char* path) SLANG_OVERRIDE;
+
+ RelativeFileSystem(ISlangFileSystem* fileSystem, const String& relativePath, bool stripPath = false);
+
+protected:
+
+ ISlangFileSystemExt* _getExt() { return Index(m_style) >= Index(FileSystemStyle::Ext) ? reinterpret_cast<ISlangFileSystemExt*>(m_fileSystem.get()) : nullptr; }
+ ISlangMutableFileSystem* _getMutable() { return Index(m_style) >= Index(FileSystemStyle::Mutable) ? reinterpret_cast<ISlangMutableFileSystem*>(m_fileSystem.get()) : nullptr; }
+
+ SlangResult _calcCombinedPathInner(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut);
+
+ SlangResult _getFixedPath(const char* path, String& outPath);
+
+ ISlangUnknown* getInterface(const Guid& guid);
+
+ bool m_stripPath;
+
+ FileSystemStyle m_style;
+ ComPtr<ISlangFileSystem> m_fileSystem; ///< NOTE! Has to match what's in style, such style can be reached via reinterpret_cast
+
+ String m_relativePath;
+};
+
+}
+
+#endif // SLANG_FILE_SYSTEM_H_INCLUDED
diff --git a/source/core/slang-gcc-compiler-util.cpp b/source/core/slang-gcc-compiler-util.cpp
deleted file mode 100644
index 56955f1da..000000000
--- a/source/core/slang-gcc-compiler-util.cpp
+++ /dev/null
@@ -1,645 +0,0 @@
-// slang-gcc-compiler-util.cpp
-#include "slang-gcc-compiler-util.h"
-
-#include "slang-common.h"
-#include "../../slang-com-helper.h"
-#include "slang-string-util.h"
-
-#include "slang-io.h"
-#include "slang-shared-library.h"
-
-namespace Slang
-{
-
-/* static */SlangResult GCCDownstreamCompilerUtil::parseVersion(const UnownedStringSlice& text, const UnownedStringSlice& prefix, DownstreamCompiler::Desc& outDesc)
-{
- List<UnownedStringSlice> lines;
- StringUtil::calcLines(text, lines);
-
- for (auto line : lines)
- {
- if (line.startsWith(prefix))
- {
- const UnownedStringSlice remainingSlice = UnownedStringSlice(line.begin() + prefix.getLength(), line.end()).trim();
- const Index versionEndIndex = remainingSlice.indexOf(' ');
- if (versionEndIndex < 0)
- {
- return SLANG_FAIL;
- }
-
- const UnownedStringSlice versionSlice(remainingSlice.begin(), remainingSlice.begin() + versionEndIndex);
-
- // Version is in format 0.0.0
- List<UnownedStringSlice> split;
- StringUtil::split(versionSlice, '.', split);
- List<Int> digits;
-
- for (auto v : split)
- {
- Int version;
- SLANG_RETURN_ON_FAIL(StringUtil::parseInt(v, version));
- digits.add(version);
- }
-
- if (digits.getCount() < 2)
- {
- return SLANG_FAIL;
- }
-
- outDesc.majorVersion = digits[0];
- outDesc.minorVersion = digits[1];
- return SLANG_OK;
- }
- }
-
- return SLANG_FAIL;
-}
-
-SlangResult GCCDownstreamCompilerUtil::calcVersion(const String& exeName, DownstreamCompiler::Desc& outDesc)
-{
- CommandLine cmdLine;
- cmdLine.setExecutableFilename(exeName);
- cmdLine.addArg("-v");
-
- ExecuteResult exeRes;
- SLANG_RETURN_ON_FAIL(ProcessUtil::execute(cmdLine, exeRes));
-
- const UnownedStringSlice prefixes[] =
- {
- UnownedStringSlice::fromLiteral("clang version"),
- UnownedStringSlice::fromLiteral("gcc version"),
- UnownedStringSlice::fromLiteral("Apple LLVM version"),
- };
- const SlangPassThrough types[] =
- {
- SLANG_PASS_THROUGH_CLANG,
- SLANG_PASS_THROUGH_GCC,
- SLANG_PASS_THROUGH_CLANG,
- };
-
- SLANG_COMPILE_TIME_ASSERT(SLANG_COUNT_OF(prefixes) == SLANG_COUNT_OF(types));
-
- for (Index i = 0; i < SLANG_COUNT_OF(prefixes); ++i)
- {
- // Set the type
- outDesc.type = types[i];
- // Extract the version
- if (SLANG_SUCCEEDED(parseVersion(exeRes.standardError.getUnownedSlice(), prefixes[i], outDesc)))
- {
- return SLANG_OK;
- }
- }
- return SLANG_FAIL;
-}
-
-static SlangResult _parseSeverity(const UnownedStringSlice& in, DownstreamDiagnostic::Severity& outSeverity)
-{
- typedef DownstreamDiagnostic::Severity Severity;
-
- if (in == "error" || in == "fatal error")
- {
- outSeverity = Severity::Error;
- }
- else if (in == "warning")
- {
- outSeverity = Severity::Warning;
- }
- else if (in == "info" || in == "note")
- {
- outSeverity = Severity::Info;
- }
- else
- {
- return SLANG_FAIL;
- }
- return SLANG_OK;
-}
-
-namespace { // anonymous
-
-enum class LineParseResult
-{
- Single, ///< It's a single line
- Start, ///< Line was the start of a message
- Continuation, ///< Not totally clear, add to previous line if nothing else hit
- Ignore, ///< Ignore the line
-};
-
-} // anonymous
-
-static SlangResult _parseGCCFamilyLine(const UnownedStringSlice& line, LineParseResult& outLineParseResult, DownstreamDiagnostic& outDiagnostic)
-{
- typedef DownstreamDiagnostic Diagnostic;
- typedef Diagnostic::Severity Severity;
-
- // Set to default case
- outLineParseResult = LineParseResult::Ignore;
-
- /* example error output from different scenarios */
-
- /*
- tests/cpp-compiler/c-compile-error.c: In function 'int main(int, char**)':
- tests/cpp-compiler/c-compile-error.c:8:13: error: 'b' was not declared in this scope
- int a = b + c;
- ^
- tests/cpp-compiler/c-compile-error.c:8:17: error: 'c' was not declared in this scope
- int a = b + c;
- ^
- */
-
- /* /tmp/ccS0JCWe.o:c-compile-link-error.c:(.rdata$.refptr.thing[.refptr.thing]+0x0): undefined reference to `thing'
- collect2: error: ld returned 1 exit status*/
-
- /*
- clang: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated [-Wdeprecated]
- Undefined symbols for architecture x86_64:
- "_thing", referenced from:
- _main in c-compile-link-error-a83ace.o
- ld: symbol(s) not found for architecture x86_64
- clang: error: linker command failed with exit code 1 (use -v to see invocation) */
-
- /* /tmp/c-compile-link-error-ccf151.o: In function `main':
- c-compile-link-error.c:(.text+0x19): undefined reference to `thing'
- clang: error: linker command failed with exit code 1 (use -v to see invocation)
- */
-
- /* /tmp/c-compile-link-error-301c8c.o: In function `main':
- /home/travis/build/shader-slang/slang/tests/cpp-compiler/c-compile-link-error.c:10: undefined reference to `thing'
- clang-7: error: linker command failed with exit code 1 (use -v to see invocation)*/
-
- /* /path/slang-cpp-prelude.h:4:10: fatal error: ../slang.h: No such file or directory
- #include "../slang.h"
- ^~~~~~~~~~~~
- compilation terminated.*/
-
- outDiagnostic.stage = Diagnostic::Stage::Compile;
-
- List<UnownedStringSlice> split;
- StringUtil::split(line, ':', split);
-
- // On windows we can have paths that are a: etc... if we detect this we can combine 0 - 1 to be 1.
- if (split.getCount() > 1 && split[0].getLength() == 1)
- {
- const char c = split[0][0];
- if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
- {
- // We'll assume it's a path
- UnownedStringSlice path(split[0].begin(), split[1].end());
- split.removeAt(0);
- split[0] = path;
- }
- }
-
- if (split.getCount() == 2)
- {
- const auto split0 = split[0].trim();
- if (split0 == UnownedStringSlice::fromLiteral("ld"))
- {
- // We'll ignore for now
- outDiagnostic.stage = Diagnostic::Stage::Link;
- outDiagnostic.severity = Severity::Info;
- outDiagnostic.text = split[1].trim();
- outLineParseResult = LineParseResult::Start;
- return SLANG_OK;
- }
-
- if (SLANG_SUCCEEDED(_parseSeverity(split0, outDiagnostic.severity)))
- {
- // Command line errors can be just contain 'error:' etc. Can be seen on apple/clang
- outDiagnostic.stage = Diagnostic::Stage::Compile;
- outDiagnostic.text = split[1].trim();
- outLineParseResult = LineParseResult::Single;
- return SLANG_OK;
- }
-
- outLineParseResult = LineParseResult::Ignore;
- return SLANG_OK;
- }
- else if (split.getCount() == 3)
- {
- const auto split0 = split[0].trim();
- const auto split1 = split[1].trim();
- const auto text = split[2].trim();
-
- // Check for special handling for clang (Can be Clang or clang apparently)
- if (split0.startsWith(UnownedStringSlice::fromLiteral("clang")) ||
- split0.startsWith(UnownedStringSlice::fromLiteral("Clang")) )
- {
- // Extract the type
- SLANG_RETURN_ON_FAIL(_parseSeverity(split[1].trim(), outDiagnostic.severity));
-
- if (text.startsWith("linker command failed"))
- {
- outDiagnostic.stage = Diagnostic::Stage::Link;
- }
-
- outDiagnostic.text = text;
- outLineParseResult = LineParseResult::Start;
- return SLANG_OK;
- }
- else if (split1.startsWith("(.text"))
- {
- // This is a little weak... but looks like it's a link error
- outDiagnostic.filePath = split[0];
- outDiagnostic.severity = Severity::Error;
- outDiagnostic.stage = Diagnostic::Stage::Link;
- outDiagnostic.text = text;
- outLineParseResult = LineParseResult::Single;
- return SLANG_OK;
- }
- else if (text.startsWith("ld returned"))
- {
- outDiagnostic.stage = DownstreamDiagnostic::Stage::Link;
- SLANG_RETURN_ON_FAIL(_parseSeverity(split[1].trim(), outDiagnostic.severity));
- outDiagnostic.text = line;
- outLineParseResult = LineParseResult::Single;
- return SLANG_OK;
- }
- else if (text == "")
- {
- // This is probably a prelude line, we'll just ignore it
- outLineParseResult = LineParseResult::Ignore;
- return SLANG_OK;
- }
- }
- else if (split.getCount() == 4)
- {
- // Probably a link error, give the source line
- String ext = Path::getPathExt(split[0]);
-
- // Maybe a bit fragile -> but probably okay for now
- if (ext != "o" && ext != "obj")
- {
- outLineParseResult = LineParseResult::Ignore;
- return SLANG_OK;
- }
- else
- {
- outDiagnostic.filePath = split[1];
- outDiagnostic.fileLine = 0;
- outDiagnostic.severity = Diagnostic::Severity::Error;
- outDiagnostic.stage = Diagnostic::Stage::Link;
- outDiagnostic.text = split[3];
-
- outLineParseResult = LineParseResult::Start;
- return SLANG_OK;
- }
- }
- else if (split.getCount() >= 5)
- {
- // Probably a regular error line
- SLANG_RETURN_ON_FAIL(_parseSeverity(split[3].trim(), outDiagnostic.severity));
-
- outDiagnostic.filePath = split[0];
- SLANG_RETURN_ON_FAIL(StringUtil::parseInt(split[1], outDiagnostic.fileLine));
-
- // Everything from 4 to the end is the error
- outDiagnostic.text = UnownedStringSlice(split[4].begin(), split.getLast().end());
-
- outLineParseResult = LineParseResult::Start;
- return SLANG_OK;
- }
-
- // Assume it's a continuation
- outLineParseResult = LineParseResult::Continuation;
- return SLANG_OK;
-}
-
-/* static */SlangResult GCCDownstreamCompilerUtil::parseOutput(const ExecuteResult& exeRes, DownstreamDiagnostics& outOutput)
-{
- LineParseResult prevLineResult = LineParseResult::Ignore;
-
- outOutput.reset();
- outOutput.rawDiagnostics = exeRes.standardError;
-
- for (auto line : LineParser(exeRes.standardError.getUnownedSlice()))
- {
- Diagnostic diagnostic;
- diagnostic.reset();
-
- LineParseResult lineRes;
-
- SLANG_RETURN_ON_FAIL(_parseGCCFamilyLine(line, lineRes, diagnostic));
-
- switch (lineRes)
- {
- case LineParseResult::Start:
- {
- // It's start of a new message
- outOutput.diagnostics.add(diagnostic);
- prevLineResult = LineParseResult::Start;
- break;
- }
- case LineParseResult::Single:
- {
- // It's a single message, without anything following
- outOutput.diagnostics.add(diagnostic);
- prevLineResult = LineParseResult::Ignore;
- break;
- }
- case LineParseResult::Continuation:
- {
- if (prevLineResult == LineParseResult::Start || prevLineResult == LineParseResult::Continuation)
- {
- if (outOutput.diagnostics.getCount() > 0)
- {
- // We are now in a continuation, add to the last
- auto& text = outOutput.diagnostics.getLast().text;
- text.append("\n");
- text.append(line);
- }
- prevLineResult = LineParseResult::Continuation;
- }
- break;
- }
- case LineParseResult::Ignore:
- {
- prevLineResult = lineRes;
- break;
- }
- default: return SLANG_FAIL;
- }
- }
-
- if (outOutput.has(Diagnostic::Severity::Error) || exeRes.resultCode != 0)
- {
- outOutput.result = SLANG_FAIL;
- }
-
- return SLANG_OK;
-}
-
-/* static */ SlangResult GCCDownstreamCompilerUtil::calcModuleFilePath(const CompileOptions& options, StringBuilder& outPath)
-{
- SLANG_ASSERT(options.modulePath.getLength());
-
- outPath.Clear();
-
- switch (options.targetType)
- {
- case TargetType::SharedLibrary:
- {
- outPath << SharedLibrary::calcPlatformPath(options.modulePath.getUnownedSlice());
- return SLANG_OK;
- }
- case TargetType::Executable:
- {
- outPath << options.modulePath;
- outPath << ProcessUtil::getExecutableSuffix();
- return SLANG_OK;
- }
- case TargetType::Object:
- {
-#if __CYGWIN__
- outPath << options.modulePath << ".obj";
-#else
- // Will be .o for typical gcc targets
- outPath << options.modulePath << ".o";
-#endif
- return SLANG_OK;
- }
- }
-
- return SLANG_FAIL;
-}
-
-/* static */SlangResult GCCDownstreamCompilerUtil::calcCompileProducts(const CompileOptions& options, ProductFlags flags, List<String>& outPaths)
-{
- SLANG_ASSERT(options.modulePath.getLength());
-
- outPaths.clear();
-
- if (flags & ProductFlag::Execution)
- {
- StringBuilder builder;
- SLANG_RETURN_ON_FAIL(calcModuleFilePath(options, builder));
- outPaths.add(builder);
- }
-
- return SLANG_OK;
-}
-
-/* static */SlangResult GCCDownstreamCompilerUtil::calcArgs(const CompileOptions& options, CommandLine& cmdLine)
-{
- SLANG_ASSERT(options.sourceContents.getLength() == 0);
- SLANG_ASSERT(options.modulePath.getLength());
-
- PlatformKind platformKind = (options.platform == PlatformKind::Unknown) ? PlatformUtil::getPlatformKind() : options.platform;
-
- if (options.sourceLanguage == SLANG_SOURCE_LANGUAGE_CPP)
- {
- cmdLine.addArg("-fvisibility=hidden");
-
- // Need C++14 for partial specialization
- cmdLine.addArg("-std=c++14");
- }
-
- // TODO(JS): Here we always set -m32 on x86. It could be argued it is only necessary when creating a shared library
- // but if we create an object file, we don't know what to choose because we don't know what final usage is.
- // It could also be argued that the platformKind could define the actual desired target - but as it stands
- // we only have a target of 'Linux' (as opposed to Win32/64). Really it implies we need an arch enumeration too.
- //
- // For now we just make X86 binaries try and produce x86 compatible binaries as fixes the immediate problems.
-#if SLANG_PROCESSOR_X86
- /* Used to specify the processor more broadly. For a x86 binary we need to make sure we build x86 builds
- even when on an x64 system.
- -m32
- -m64*/
- cmdLine.addArg("-m32");
-#endif
-
- switch (options.optimizationLevel)
- {
- case OptimizationLevel::None:
- {
- // No optimization
- cmdLine.addArg("-O0");
- break;
- }
- case OptimizationLevel::Default:
- {
- cmdLine.addArg("-Os");
- break;
- }
- case OptimizationLevel::High:
- {
- cmdLine.addArg("-O2");
- break;
- }
- case OptimizationLevel::Maximal:
- {
- cmdLine.addArg("-O4");
- break;
- }
- default: break;
- }
-
- if (options.debugInfoType != DebugInfoType::None)
- {
- cmdLine.addArg("-g");
- }
-
- if (options.flags & CompileOptions::Flag::Verbose)
- {
- cmdLine.addArg("-v");
- }
-
- switch (options.floatingPointMode)
- {
- case FloatingPointMode::Default: break;
- case FloatingPointMode::Precise:
- {
- //cmdLine.addArg("-fno-unsafe-math-optimizations");
- break;
- }
- case FloatingPointMode::Fast:
- {
- // We could enable SSE with -mfpmath=sse
- // But that would only make sense on a x64/x86 type processor and only if that feature is present (it is on all x64)
- cmdLine.addArg("-ffast-math");
- break;
- }
- }
-
- StringBuilder moduleFilePath;
- calcModuleFilePath(options, moduleFilePath);
-
- cmdLine.addArg("-o");
- cmdLine.addArg(moduleFilePath);
-
- switch (options.targetType)
- {
- case TargetType::SharedLibrary:
- {
- // Shared library
- cmdLine.addArg("-shared");
-
- if (PlatformUtil::isFamily(PlatformFamily::Unix, platformKind))
- {
- // Position independent
- cmdLine.addArg("-fPIC");
- }
- break;
- }
- case TargetType::Executable:
- {
- break;
- }
- case TargetType::Object:
- {
- // Don't link, just produce object file
- cmdLine.addArg("-c");
- break;
- }
- default: break;
- }
-
- // Add defines
- for (const auto& define : options.defines)
- {
- StringBuilder builder;
-
- builder << "-D";
- builder << define.nameWithSig;
- if (define.value.getLength())
- {
- builder << "=" << define.value;
- }
-
- cmdLine.addArg(builder);
- }
-
- // Add includes
- for (const auto& include : options.includePaths)
- {
- cmdLine.addArg("-I");
- cmdLine.addArg(include);
- }
-
- // Link options
- if (0) // && options.targetType != TargetType::Object)
- {
- //linkOptions << "-Wl,";
- //cmdLine.addArg(linkOptions);
- }
-
- if (options.targetType == TargetType::SharedLibrary)
- {
- if (!PlatformUtil::isFamily(PlatformFamily::Apple, platformKind))
- {
- // On MacOS, this linker option is not supported. That's ok though in
- // so far as on MacOS it does report any unfound symbols without the option.
-
- // Linker flag to report any undefined symbols as a link error
- cmdLine.addArg("-Wl,--no-undefined");
- }
- }
-
- // Files to compile
- for (const auto& sourceFile : options.sourceFiles)
- {
- cmdLine.addArg(sourceFile);
- }
-
- for (const auto& libPath : options.libraryPaths)
- {
- // Note that any escaping of the path is handled in the ProcessUtil::
- cmdLine.addArg("-L");
- cmdLine.addArg(libPath);
- cmdLine.addArg("-F");
- cmdLine.addArg(libPath);
- }
-
- if (options.sourceLanguage == SLANG_SOURCE_LANGUAGE_CPP && !PlatformUtil::isFamily(PlatformFamily::Windows, platformKind))
- {
- // Make STD libs available
- cmdLine.addArg("-lstdc++");
- // Make maths lib available
- cmdLine.addArg("-lm");
- }
-
- return SLANG_OK;
-}
-
-/* static */SlangResult GCCDownstreamCompilerUtil::createCompiler(const String& path, const String& inExeName, RefPtr<DownstreamCompiler>& outCompiler)
-{
- String exeName(inExeName);
- if (path.getLength() > 0)
- {
- exeName = Path::combine(path, inExeName);
- }
-
- DownstreamCompiler::Desc desc;
- SLANG_RETURN_ON_FAIL(GCCDownstreamCompilerUtil::calcVersion(exeName, desc));
-
- RefPtr<CommandLineDownstreamCompiler> compiler(new GCCDownstreamCompiler(desc));
- compiler->m_cmdLine.setExecutableFilename(exeName);
-
- outCompiler = compiler;
- return SLANG_OK;
-}
-
-/* static */SlangResult GCCDownstreamCompilerUtil::locateGCCCompilers(const String& path, ISlangSharedLibraryLoader* loader, DownstreamCompilerSet* set)
-{
- SLANG_UNUSED(loader);
- RefPtr<DownstreamCompiler> compiler;
- if (SLANG_SUCCEEDED(createCompiler(path, "g++", compiler)))
- {
- set->addCompiler(compiler);
- }
- return SLANG_OK;
-}
-
-/* static */SlangResult GCCDownstreamCompilerUtil::locateClangCompilers(const String& path, ISlangSharedLibraryLoader* loader, DownstreamCompilerSet* set)
-{
- SLANG_UNUSED(loader);
-
- RefPtr<DownstreamCompiler> compiler;
- if (SLANG_SUCCEEDED(createCompiler(path, "clang", compiler)))
- {
- set->addCompiler(compiler);
- }
- return SLANG_OK;
-}
-
-}
diff --git a/source/core/slang-gcc-compiler-util.h b/source/core/slang-gcc-compiler-util.h
deleted file mode 100644
index b97144e35..000000000
--- a/source/core/slang-gcc-compiler-util.h
+++ /dev/null
@@ -1,56 +0,0 @@
-#ifndef SLANG_GCC_COMPILER_UTIL_H
-#define SLANG_GCC_COMPILER_UTIL_H
-
-#include "slang-downstream-compiler.h"
-
-namespace Slang
-{
-
-/* Utility for processing input and output of gcc-like compilers, including clang */
-struct GCCDownstreamCompilerUtil : public DownstreamCompilerBaseUtil
-{
- /// Extracts version number into desc from text (assumes gcc/clang -v layout with a line with version)
- static SlangResult parseVersion(const UnownedStringSlice& text, const UnownedStringSlice& prefix, DownstreamCompiler::Desc& outDesc);
-
- /// Runs the exeName, and extracts the version info into outDesc
- static SlangResult calcVersion(const String& exeName, DownstreamCompiler::Desc& outDesc);
-
- /// Calculate gcc family compilers (including clang) cmdLine arguments from options
- static SlangResult calcArgs(const CompileOptions& options, CommandLine& cmdLine);
-
- /// Parse ExecuteResult into Output
- static SlangResult parseOutput(const ExecuteResult& exeRes, DownstreamDiagnostics& outOutput);
-
- /// Calculate the output module filename
- static SlangResult calcModuleFilePath(const CompileOptions& options, StringBuilder& outPath);
-
- /// Given options, calculate paths to products produced for a compilation
- static SlangResult calcCompileProducts(const CompileOptions& options, ProductFlags flags, List<String>& outPaths);
-
- /// Given a path and an exe name, detects if compiler is present, and if so adds to compiler set.
- static SlangResult createCompiler(const String& path, const String& inExeName, RefPtr<DownstreamCompiler>& outCompiler);
-
- static SlangResult locateGCCCompilers(const String& path, ISlangSharedLibraryLoader* loader, DownstreamCompilerSet* set);
-
- static SlangResult locateClangCompilers(const String& path, ISlangSharedLibraryLoader* loader, DownstreamCompilerSet* set);
-
-};
-
-class GCCDownstreamCompiler : public CommandLineDownstreamCompiler
-{
-public:
- typedef CommandLineDownstreamCompiler Super;
- typedef GCCDownstreamCompilerUtil Util;
-
- // CommandLineCPPCompiler impl - just forwards to the Util
- virtual SlangResult calcArgs(const CompileOptions& options, CommandLine& cmdLine) SLANG_OVERRIDE { return Util::calcArgs(options, cmdLine); }
- virtual SlangResult parseOutput(const ExecuteResult& exeResult, DownstreamDiagnostics& output) SLANG_OVERRIDE { return Util::parseOutput(exeResult, output); }
- virtual SlangResult calcModuleFilePath(const CompileOptions& options, StringBuilder& outPath) SLANG_OVERRIDE { return Util::calcModuleFilePath(options, outPath); }
- virtual SlangResult calcCompileProducts(const CompileOptions& options, ProductFlags flags, List<String>& outPaths) SLANG_OVERRIDE { return Util::calcCompileProducts(options, flags, outPaths); }
-
- GCCDownstreamCompiler(const Desc& desc):Super(desc) {}
-};
-
-}
-
-#endif
diff --git a/source/core/slang-name-convention-util.cpp b/source/core/slang-name-convention-util.cpp
deleted file mode 100644
index a5acc6370..000000000
--- a/source/core/slang-name-convention-util.cpp
+++ /dev/null
@@ -1,213 +0,0 @@
-
-#include "slang-name-convention-util.h"
-
-#include "slang-char-util.h"
-#include "slang-string-util.h"
-
-namespace Slang
-{
-
-/* static */NameConvention NameConventionUtil::getConvention(const UnownedStringSlice& slice)
-{
- for (const char c : slice)
- {
- switch (c)
- {
- case '-': return NameConvention::Kabab;
- case '_': return NameConvention::Snake;
- default: break;
- }
- }
- return NameConvention::Camel;
-}
-
-/* static */void NameConventionUtil::split(NameConvention convention, const UnownedStringSlice& slice, List<UnownedStringSlice>& out)
-{
- switch (convention)
- {
- case NameConvention::Kabab:
- {
- StringUtil::split(slice, '-', out);
- break;
- }
- case NameConvention::Snake:
- {
- StringUtil::split(slice, '_', out);
- break;
- }
- case NameConvention::Camel:
- {
- typedef CharUtil::Flags CharFlags;
- typedef CharUtil::Flag CharFlag;
-
- CharFlags prevFlags = 0;
- const char*const end = slice.end();
-
- const char* start = slice.begin();
- for (const char* cur = start; cur < end; ++cur)
- {
- const char c = *cur;
- const CharUtil::Flags flags = CharUtil::getFlags(c);
-
- if (flags & CharFlag::Upper)
- {
- if (prevFlags & CharFlag::Lower)
- {
- // If we go from lower to upper, we have a transition
- out.add(UnownedStringSlice(start, cur));
- start = cur;
- }
- else if ((prevFlags & CharFlag::Upper) && cur + 1 < end)
- {
- // This works with capital or uncapitalized acronyms, but if we have two capitalized acronyms following each other - it can't split.
- //
- // For example
- // "IAABBSystem" -> "IAABB", "System"
- //
- // If it only accepted lower case acronyms the logic could be changed such that the following could be produced
- // "IAabbSystem" -> "I", "Aabb", "System"
- //
- // Since Slang source largely goes with upper case acronyms, we work with the heuristic here..
-
- if (CharUtil::isLower(cur[1]))
- {
- out.add(UnownedStringSlice(start, cur));
- start = cur;
- }
- }
- }
-
- prevFlags = flags;
- }
-
- // Add any end section
- if (start < end)
- {
- out.add(UnownedStringSlice(start, end));
- }
- break;
- }
- }
-}
-
-void NameConventionUtil::split(const UnownedStringSlice& slice, List<UnownedStringSlice>& out)
-{
- split(getConvention(slice), slice, out);
-}
-
-/* static */void NameConventionUtil::join(const UnownedStringSlice* slices, Index slicesCount, CharCase charCase, char joinChar, StringBuilder& out)
-{
- if (slicesCount <= 0)
- {
- return;
- }
-
- Index totalSize = slicesCount - 1;
- for (Index i = 0; i < slicesCount; ++i)
- {
- totalSize += slices[i].getLength();
- }
-
- char*const dstStart = out.prepareForAppend(totalSize);
- char* dst = dstStart;
-
- for (Index i = 0; i < slicesCount; ++i)
- {
- const UnownedStringSlice& slice = slices[i];
- const Index count = slice.getLength();
- const char*const src = slice.begin();
-
- if (i > 0)
- {
- *dst++ = joinChar;
- }
-
- switch (charCase)
- {
- case CharCase::Upper:
- {
- for (Index j = 0; j < count; ++j)
- {
- dst[j] = CharUtil::toUpper(src[j]);
- }
- break;
- }
- case CharCase::Lower:
- {
- for (Index j = 0; j < count; ++j)
- {
- dst[j] = CharUtil::toLower(src[j]);
- }
- break;
- }
- }
-
- dst += count;
- }
-
- SLANG_ASSERT(dstStart + totalSize == dst);
- out.appendInPlace(dstStart, totalSize);
-}
-
-/* static */void NameConventionUtil::join(const UnownedStringSlice* slices, Index slicesCount, CharCase charCase, NameConvention convention, StringBuilder& out)
-{
- switch (convention)
- {
- case NameConvention::Kabab: return join(slices, slicesCount, charCase, '-', out);
- case NameConvention::Snake: return join(slices, slicesCount, charCase, '_', out);
- case NameConvention::Camel:
- {
- Index totalSize = 0;
-
- for (Index i = 0; i < slicesCount; ++i)
- {
- totalSize += slices[i].getLength();
- }
-
- char*const dstStart = out.prepareForAppend(totalSize);
- char* dst = dstStart;
-
- for (Index i = 0; i < slicesCount; ++i)
- {
- const UnownedStringSlice& slice = slices[i];
- Index count = slice.getLength();
- const char* src = slice.begin();
-
- Int j = 0;
-
- if (count > 0 && !(i == 0 && charCase == CharCase::Lower))
- {
- // Capitalize first letter of each word, unless on first word and 'lower'
- dst[j] = CharUtil::toUpper(src[j]);
- j++;
- }
-
- for (; j < count; ++j)
- {
- dst[j] = CharUtil::toLower(src[j]);
- }
-
- dst += count;
- }
- break;
- }
- }
-}
-
-/* static */void NameConventionUtil::convert(NameConvention fromConvention, const UnownedStringSlice& slice, CharCase charCase, NameConvention toConvention, StringBuilder& out)
-{
- // Split into slices
- List<UnownedStringSlice> slices;
- split(fromConvention, slice, slices);
-
- // Join the slices in the toConvention
- join(slices.getBuffer(), slices.getCount(), charCase, toConvention, out);
-}
-
-/* static */void NameConventionUtil::convert(const UnownedStringSlice& slice, CharCase charCase, NameConvention toConvention, StringBuilder& out)
-{
- convert(getConvention(slice), slice, charCase, toConvention, out);
-}
-
-}
-
diff --git a/source/core/slang-name-convention-util.h b/source/core/slang-name-convention-util.h
deleted file mode 100644
index d4a984ca0..000000000
--- a/source/core/slang-name-convention-util.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef SLANG_CORE_NAME_CONVENTION_UTIL_H
-#define SLANG_CORE_NAME_CONVENTION_UTIL_H
-
-#include "slang-string.h"
-#include "slang-list.h"
-
-namespace Slang
-{
-
-enum class NameConvention
-{
- Kabab, /// Words are separated with -. WORDS-ARE-SEPARATED
- Snake, /// Words are separated with _. WORDS_ARE_SEPARATED
- Camel, /// Words start with a capital. (Upper will make first words character capitalized, aka PascalCase)
-};
-
-enum class CharCase
-{
- Upper,
- Lower,
-};
-
-/* This utility is to enable easy conversion and interpretation of names that use standard conventions, typically in programming
-languages. The conventions are largely how to represent multiple words together.
-
-Split is used to split up a name into it's constituent 'words' based on a convention.
-Join is used to combine words based on a convention/character case
-
-Convert uses split and join to allow easy conversion between conventions.
-*/
-struct NameConventionUtil
-{
- /// Given a slice tries to determine the convention used.
- /// If no separators are found, will assume Camel
- static NameConvention getConvention(const UnownedStringSlice& slice);
-
- /// Given a slice and a naming convention, split into it's constituent parts. If convention isn't specified, will infer from slice using getConvention.
- static void split(NameConvention convention, const UnownedStringSlice& slice, List<UnownedStringSlice>& out);
- static void split(const UnownedStringSlice& slice, List<UnownedStringSlice>& out);
-
- /// Given slices, join together with the specified convention into out
- static void join(const UnownedStringSlice* slices, Index slicesCount, CharCase charCase, NameConvention convention, StringBuilder& out);
-
- /// Join with a join char, and potentially changing case of input slices
- static void join(const UnownedStringSlice* slices, Index slicesCount, CharCase charCase, char joinChar, StringBuilder& out);
-
- /// Convert from one convention to another. If fromConvention isn't specified, will infer from slice using getConvention.
- static void convert(NameConvention fromConvention, const UnownedStringSlice& slice, CharCase charCase, NameConvention toConvention, StringBuilder& out);
- static void convert(const UnownedStringSlice& slice, CharCase charCase, NameConvention toConvention, StringBuilder& out);
-};
-
-}
-
-#endif // SLANG_CORE_NAME_CONVENTION_UTIL_H
diff --git a/source/core/slang-nvrtc-compiler.cpp b/source/core/slang-nvrtc-compiler.cpp
deleted file mode 100644
index eb117379f..000000000
--- a/source/core/slang-nvrtc-compiler.cpp
+++ /dev/null
@@ -1,773 +0,0 @@
-// slang-nvrtc-compiler.cpp
-#include "slang-nvrtc-compiler.h"
-
-#include "slang-common.h"
-#include "../../slang-com-helper.h"
-
-#include "../core/slang-blob.h"
-
-#include "slang-string-util.h"
-#include "slang-string-slice-pool.h"
-
-#include "slang-io.h"
-#include "slang-shared-library.h"
-#include "slang-semantic-version.h"
-
-
-namespace nvrtc
-{
-
-typedef enum {
- NVRTC_SUCCESS = 0,
- NVRTC_ERROR_OUT_OF_MEMORY = 1,
- NVRTC_ERROR_PROGRAM_CREATION_FAILURE = 2,
- NVRTC_ERROR_INVALID_INPUT = 3,
- NVRTC_ERROR_INVALID_PROGRAM = 4,
- NVRTC_ERROR_INVALID_OPTION = 5,
- NVRTC_ERROR_COMPILATION = 6,
- NVRTC_ERROR_BUILTIN_OPERATION_FAILURE = 7,
- NVRTC_ERROR_NO_NAME_EXPRESSIONS_AFTER_COMPILATION = 8,
- NVRTC_ERROR_NO_LOWERED_NAMES_BEFORE_COMPILATION = 9,
- NVRTC_ERROR_NAME_EXPRESSION_NOT_VALID = 10,
- NVRTC_ERROR_INTERNAL_ERROR = 11
-} nvrtcResult;
-
-typedef struct _nvrtcProgram *nvrtcProgram;
-
-#define SLANG_NVRTC_FUNCS(x) \
- x(const char*, nvrtcGetErrorString, (nvrtcResult result)) \
- x(nvrtcResult, nvrtcVersion, (int *major, int *minor)) \
- x(nvrtcResult, nvrtcCreateProgram, (nvrtcProgram *prog, const char *src, const char *name, int numHeaders, const char * const *headers, const char * const *includeNames)) \
- x(nvrtcResult, nvrtcDestroyProgram, (nvrtcProgram *prog)) \
- x(nvrtcResult, nvrtcCompileProgram, (nvrtcProgram prog, int numOptions, const char * const *options)) \
- x(nvrtcResult, nvrtcGetPTXSize, (nvrtcProgram prog, size_t *ptxSizeRet)) \
- x(nvrtcResult, nvrtcGetPTX, (nvrtcProgram prog, char *ptx)) \
- x(nvrtcResult, nvrtcGetProgramLogSize, (nvrtcProgram prog, size_t *logSizeRet)) \
- x(nvrtcResult, nvrtcGetProgramLog, (nvrtcProgram prog, char *log))\
- x(nvrtcResult, nvrtcAddNameExpression, (nvrtcProgram prog, const char * const name_expression)) \
- x(nvrtcResult, nvrtcGetLoweredName, (nvrtcProgram prog, const char *const name_expression, const char** lowered_name))
-
-} // namespace nvrtc
-
-namespace Slang
-{
-using namespace nvrtc;
-
-static SlangResult _asResult(nvrtcResult res)
-{
- switch (res)
- {
- case NVRTC_SUCCESS:
- {
- return SLANG_OK;
- }
- case NVRTC_ERROR_OUT_OF_MEMORY:
- {
- return SLANG_E_OUT_OF_MEMORY;
- }
- case NVRTC_ERROR_PROGRAM_CREATION_FAILURE:
- case NVRTC_ERROR_INVALID_INPUT:
- case NVRTC_ERROR_INVALID_PROGRAM:
- {
- return SLANG_FAIL;
- }
- case NVRTC_ERROR_INVALID_OPTION:
- {
- return SLANG_E_INVALID_ARG;
- }
- case NVRTC_ERROR_COMPILATION:
- case NVRTC_ERROR_BUILTIN_OPERATION_FAILURE:
- case NVRTC_ERROR_NO_NAME_EXPRESSIONS_AFTER_COMPILATION:
- case NVRTC_ERROR_NO_LOWERED_NAMES_BEFORE_COMPILATION:
- case NVRTC_ERROR_NAME_EXPRESSION_NOT_VALID:
- {
- return SLANG_FAIL;
- }
- case NVRTC_ERROR_INTERNAL_ERROR:
- {
- return SLANG_E_INTERNAL_FAIL;
- }
- default: return SLANG_FAIL;
- }
-}
-
-class NVRTCDownstreamCompiler : public DownstreamCompiler
-{
-public:
- typedef DownstreamCompiler Super;
-
- // DownstreamCompiler
- virtual SlangResult compile(const CompileOptions& options, RefPtr<DownstreamCompileResult>& outResult) SLANG_OVERRIDE;
- virtual ISlangSharedLibrary* getSharedLibrary() SLANG_OVERRIDE { return m_sharedLibrary; }
-
- /// Must be called before use
- SlangResult init(ISlangSharedLibrary* library);
-
- NVRTCDownstreamCompiler() {}
-
-protected:
-
- struct ScopeProgram
- {
- ScopeProgram(NVRTCDownstreamCompiler* compiler, nvrtcProgram program):
- m_compiler(compiler),
- m_program(program)
- {
- }
- ~ScopeProgram()
- {
- m_compiler->m_nvrtcDestroyProgram(&m_program);
- }
- NVRTCDownstreamCompiler* m_compiler;
- nvrtcProgram m_program;
- };
-
-
-#define SLANG_NVTRC_MEMBER_FUNCS(ret, name, params) \
- ret (*m_##name) params;
-
- SLANG_NVRTC_FUNCS(SLANG_NVTRC_MEMBER_FUNCS);
-
- ComPtr<ISlangSharedLibrary> m_sharedLibrary;
-};
-
-#define SLANG_NVRTC_RETURN_ON_FAIL(x) { nvrtcResult _res = x; if (_res != NVRTC_SUCCESS) return _asResult(_res); }
-
-SlangResult NVRTCDownstreamCompiler::init(ISlangSharedLibrary* library)
-{
-#define SLANG_NVTRC_GET_FUNC(ret, name, params) \
- m_##name = (ret (*) params)library->findFuncByName(#name); \
- if (m_##name == nullptr) return SLANG_FAIL;
-
- SLANG_NVRTC_FUNCS(SLANG_NVTRC_GET_FUNC)
-
- m_sharedLibrary = library;
-
- m_desc.type = SLANG_PASS_THROUGH_NVRTC;
-
- int major, minor;
- m_nvrtcVersion(&major, &minor);
- m_desc.majorVersion = major;
- m_desc.minorVersion = minor;
-
- return SLANG_OK;
-}
-
-static SlangResult _parseLocation(const UnownedStringSlice& in, DownstreamDiagnostic& outDiagnostic)
-{
- const Index startIndex = in.indexOf('(');
-
- if (startIndex >= 0)
- {
- outDiagnostic.filePath = UnownedStringSlice(in.begin(), in.begin() + startIndex);
- UnownedStringSlice remaining(in.begin() + startIndex + 1, in.end());
- const Int endIndex = remaining.indexOf(')');
-
- UnownedStringSlice lineText = UnownedStringSlice(remaining.begin(), remaining.begin() + endIndex);
-
- Int line;
- SLANG_RETURN_ON_FAIL(StringUtil::parseInt(lineText, line));
- outDiagnostic.fileLine = line;
- }
- else
- {
- outDiagnostic.fileLine = 0;
- outDiagnostic.filePath = in;
- }
- return SLANG_OK;
-}
-
-static bool _isDriveLetter(char c)
-{
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
-}
-
-static bool _hasDriveLetter(const UnownedStringSlice& line)
-{
- return line.getLength() > 2 && line[1] == ':' && _isDriveLetter(line[0]);
-}
-
-static SlangResult _parseNVRTCLine(const UnownedStringSlice& line, DownstreamDiagnostic& outDiagnostic)
-{
- typedef DownstreamDiagnostic Diagnostic;
- typedef Diagnostic::Severity Severity;
-
- outDiagnostic.stage = Diagnostic::Stage::Compile;
-
- List<UnownedStringSlice> split;
- if (_hasDriveLetter(line))
- {
- // The drive letter has :, which confuses things, so skip that and then fix up first entry
- UnownedStringSlice lineWithoutDrive(line.begin() + 2, line.end());
- StringUtil::split(lineWithoutDrive, ':', split);
- split[0] = UnownedStringSlice(line.begin(), split[0].end());
- }
- else
- {
- StringUtil::split(line, ':', split);
- }
-
- if (split.getCount() >= 3)
- {
- // tests/cuda/cuda-compile.cu(7): warning: variable "c" is used before its value is set
-
- const auto split1 = split[1].trim();
-
- if (split1 == "error")
- {
- outDiagnostic.severity = Severity::Error;
- }
- else if (split1 == "warning")
- {
- outDiagnostic.severity = Severity::Warning;
- }
- outDiagnostic.text = split[2].trim();
-
- SLANG_RETURN_ON_FAIL(_parseLocation(split[0], outDiagnostic));
- return SLANG_OK;
- }
-
- return SLANG_E_NOT_FOUND;
-}
-
-SlangResult NVRTCDownstreamCompiler::compile(const CompileOptions& options, RefPtr<DownstreamCompileResult>& outResult)
-{
- // This compiler doesn't read files, they should be read externally and stored in sourceContents/sourceContentsPath
- if (options.sourceFiles.getCount() > 0)
- {
- return SLANG_FAIL;
- }
-
- CommandLine cmdLine;
-
- switch (options.debugInfoType)
- {
- case DebugInfoType::None:
- {
- break;
- }
- default:
- {
- cmdLine.addArg("--device-debug");
- break;
- }
- case DebugInfoType::Maximal:
- {
- cmdLine.addArg("--device-debug");
- cmdLine.addArg("--generate-line-info");
- break;
- }
- }
-
- // Don't seem to have such a control, so ignore for now
- //switch (options.optimizationLevel)
- //{
- // default: break;
- //}
-
- switch (options.floatingPointMode)
- {
- case FloatingPointMode::Default: break;
- case FloatingPointMode::Precise:
- {
- break;
- }
- case FloatingPointMode::Fast:
- {
- cmdLine.addArg("--use_fast_math");
- break;
- }
- }
-
- // Add defines
- for (const auto& define : options.defines)
- {
- StringBuilder builder;
- builder << "-D";
- builder << define.nameWithSig;
- if (define.value.getLength())
- {
- builder << "=" << define.value;
- }
-
- cmdLine.addArg(builder);
- }
-
- // Add includes
- for (const auto& include : options.includePaths)
- {
- cmdLine.addArg("-I");
- cmdLine.addArg(include);
- }
-
- // Neither of these options are strictly required, for general use of nvrtc,
- // but are enabled to make use withing Slang work more smoothly
- {
- // Require c++14, as makes initialization construction with {} available and so simplifies code generation
- cmdLine.addArg("-std=c++14");
-
- // Disable all warnings
- // This is arguably too much - but nvrtc does not appear to have a mechanism to switch off individual warnings.
- // I tried the -Xcudafe mechanism but that does not appear to work for nvrtc
- cmdLine.addArg("-w");
- }
-
- {
- // The lowest supported CUDA architecture version supported
- // by NVRTC is `compute_30`.
- //
- SemanticVersion version(3);
-
- // Newer releases of NVRTC only support `compute_35` and up
- // (with everything before `compute_52` being deprecated).
- //
- if( m_desc.majorVersion >= 11 )
- {
- version = SemanticVersion(3, 5);
- }
-
- // If constructs used in the code to be compield require
- // a higher architecture version than the minimum, then
- // we will set the version to the highest version listed
- // among the requirements.
- //
- for (const auto& capabilityVersion : options.requiredCapabilityVersions)
- {
- if (capabilityVersion.kind == DownstreamCompiler::CapabilityVersion::Kind::CUDASM)
- {
- if (capabilityVersion.version > version)
- {
- version = capabilityVersion.version;
- }
- }
- }
-
- StringBuilder builder;
- builder << "-arch=compute_";
- builder << version.m_major;
-
- SLANG_ASSERT(version.m_minor >= 0 && version.m_minor <= 9);
- builder << char('0' + version.m_minor);
-
- cmdLine.addArg(builder);
- }
-
- List<const char*> headers;
- List<const char*> headerIncludeNames;
-
- // If compiling for OptiX, we need to add the appropriate search paths to the command line.
- //
- if(options.pipelineType == PipelineType::RayTracing)
- {
- // The device-side OptiX API is accessed through a constellation
- // of headers provided by the OptiX SDK, so we need to set an
- // include path for the compile that makes those visible.
- //
- // TODO: The OptiX SDK installer doesn't set any kind of environment
- // variable to indicate where the SDK was installed, so we seemingly
- // need to probe paths instead. The form of the path will differ
- // betwene Windows and Unix-y platforms, and we will need some kind
- // of approach to probe multiple versiosn and use the latest.
- //
- // HACK: For now I'm using the fixed path for the most recent SDK
- // release on Windows. This means that OptiX cross-compilation will
- // only "work" on a subset of platforms, but that doesn't matter
- // for now since it doesn't really "work" at all.
- //
- cmdLine.addArg("-I");
- cmdLine.addArg("C:/ProgramData/NVIDIA Corporation/OptiX SDK 7.0.0/include/");
-
- // The OptiX headers in turn `#include <stddef.h>` and expect that
- // to work. We could try to also add in an include path from the CUDA
- // SDK (which seems to provide a `stddef.h` in the most recent version),
- // but using that version doesn't seem to work (and also bakes in a
- // requirement that the user have the CUDA SDK installed in addition
- // to the OptiX SDK).
- //
- // Instead, we will rely on the NVRTC feature that lets us set up
- // memory buffers to be used as include files by the we compile.
- // We will define a dummy `stddef.h` that includes the bare minimum
- // lines required to get the OptiX headers to compile without complaint.
- //
- // TODO: Confirm that the `LP64` definition herei s actually needed.
- //
- headerIncludeNames.add("stddef.h");
- headers.add("#pragma once\n" "#define LP64\n");
-
- // Finally, we want the CUDA prelude to be able to react to whether
- // or not OptiX is required (most notably by `#include`ing the appropriate
- // header(s)), so we will insert a preprocessor define to indicate
- // the requirement.
- //
- cmdLine.addArg("-DSLANG_CUDA_ENABLE_OPTIX");
- }
-
- SLANG_ASSERT(headers.getCount() == headerIncludeNames.getCount());
-
- nvrtcProgram program = nullptr;
- nvrtcResult res = m_nvrtcCreateProgram(&program, options.sourceContents.getBuffer(), options.sourceContentsPath.getBuffer(),
- (int) headers.getCount(),
- headers.getBuffer(),
- headerIncludeNames.getBuffer());
- if (res != NVRTC_SUCCESS)
- {
- return _asResult(res);
- }
- ScopeProgram scope(this, program);
-
- List<const char*> dstOptions;
- dstOptions.setCount(cmdLine.m_args.getCount());
- for (Index i = 0; i < cmdLine.m_args.getCount(); ++i)
- {
- dstOptions[i] = cmdLine.m_args[i].value.getBuffer();
- }
-
- res = m_nvrtcCompileProgram(program, int(dstOptions.getCount()), dstOptions.getBuffer());
-
- RefPtr<ListBlob> blob;
- DownstreamDiagnostics diagnostics;
-
- diagnostics.result = _asResult(res);
-
- {
- String rawDiagnostics;
-
- size_t logSize = 0;
- SLANG_NVRTC_RETURN_ON_FAIL(m_nvrtcGetProgramLogSize(program, &logSize));
-
- if (logSize)
- {
- char* dst = rawDiagnostics.prepareForAppend(Index(logSize));
- SLANG_NVRTC_RETURN_ON_FAIL(m_nvrtcGetProgramLog(program, dst));
- rawDiagnostics.appendInPlace(dst, Index(logSize));
-
- diagnostics.rawDiagnostics = rawDiagnostics;
- }
-
- // Parse the diagnostics here
- for (auto line : LineParser(diagnostics.rawDiagnostics.getUnownedSlice()))
- {
- DownstreamDiagnostic diagnostic;
- SlangResult lineRes = _parseNVRTCLine(line, diagnostic);
-
- if (SLANG_SUCCEEDED(lineRes))
- {
- diagnostics.diagnostics.add(diagnostic);
- }
- else if (lineRes != SLANG_E_NOT_FOUND)
- {
- return lineRes;
- }
- }
-
- // if it has a compilation error.. set on output
- if (diagnostics.has(DownstreamDiagnostic::Severity::Error))
- {
- diagnostics.result = SLANG_FAIL;
- }
- }
-
- if (res == nvrtc::NVRTC_SUCCESS)
- {
- // We should parse the log to set up the diagnostics
- size_t ptxSize;
- SLANG_NVRTC_RETURN_ON_FAIL(m_nvrtcGetPTXSize(program, &ptxSize));
-
- List<uint8_t> ptx;
- ptx.setCount(Index(ptxSize));
-
- SLANG_NVRTC_RETURN_ON_FAIL(m_nvrtcGetPTX(program, (char*)ptx.getBuffer()));
-
- blob = ListBlob::moveCreate(ptx);
- }
-
- outResult = new BlobDownstreamCompileResult(diagnostics, blob);
-
- return SLANG_OK;
-}
-
-/* An implementation of Path::Visitor that can be used for finding NVRTC shared library installations. */
-struct NVRTCPathVisitor : Path::Visitor
-{
- struct Candidate
- {
- typedef Candidate ThisType;
-
- bool operator==(const ThisType& rhs) const { return path == rhs.path && version == rhs.version; }
- bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
-
- static Candidate make(const String& path, const SemanticVersion& version)
- {
- Candidate can;
- can.version = version;
- can.path = path;
- return can;
- }
- String path;
- SemanticVersion version;
- };
-
- Index findVersion(const SemanticVersion& version) const
- {
- const Index count = m_candidates.getCount();
- for (Index i = 0; i < count; ++i)
- {
- if (m_candidates[i].version == version)
- {
- return i;
- }
- }
- return -1;
- }
-
- static bool _orderCandiate(const Candidate& a, const Candidate& b) { return a.version < b.version; }
- void sortCandidates() { m_candidates.sort(_orderCandiate); }
-
- void accept(Path::Type type, const UnownedStringSlice& filename) SLANG_OVERRIDE
- {
- // Lets make sure it start's with nvrtc64, but not worry about case
- if (type == Path::Type::File)
- {
- // If there is a defined extension, make sure it has it
- if (m_postfix.getLength() && filename.getLength() >= m_postfix.getLength())
- {
- // We test without case - really for windows
- UnownedStringSlice filenamePostfix = filename.tail(filename.getLength() - m_postfix.getLength());
- if (!filenamePostfix.caseInsensitiveEquals(m_postfix.getUnownedSlice()))
- {
- return;
- }
- }
-
- if (filename.getLength() >= m_prefix.getLength() &&
- filename.subString(0, m_prefix.getLength()).caseInsensitiveEquals(m_prefix.getUnownedSlice()))
- {
- // Versions are typically (on windows) of the form
- // nvrtc64_110_2.dll
- // 11 - Major
- // 0 Minor
- // 2 Patch
- Index endIndex = filename.indexOf('.');
- endIndex = (endIndex < 0) ? filename.getLength() : endIndex;
-
- UnownedStringSlice versionSlice = UnownedStringSlice(filename.begin() + m_prefix.getLength(), filename.begin() + endIndex);
-
- Int patch = 0;
- UnownedStringSlice majorMinorSlice;
- {
- List<UnownedStringSlice> slices;
- StringUtil::split(versionSlice, '_', slices);
- if (slices.getCount() >= 2)
- {
- // We don't bother checking for error here, if it's not parsable, it will be 0
- StringUtil::parseInt(slices[1], patch);
- }
- majorMinorSlice = slices[0];
- }
-
- if (majorMinorSlice.getLength() < 2)
- {
- // Must be a major and minor
- return;
- }
-
- UnownedStringSlice majorSlice = majorMinorSlice.head(majorMinorSlice.getLength() - 1);
- UnownedStringSlice minorSlice = majorMinorSlice.subString(majorMinorSlice.getLength() - 1, 1);
-
- Int major;
- Int minor;
-
- if (SLANG_FAILED(StringUtil::parseInt(majorSlice, major)) ||
- SLANG_FAILED(StringUtil::parseInt(minorSlice, minor)))
- {
- return;
- }
-
- const SemanticVersion version = SemanticVersion(int(major), int(minor), int(patch));
-
- // We may want to add multiple versions, if they are in different locations - as there may be multiple entries
- // in the PATH, and only one works. We'll only know which works by loading
-#if 0
- // We already found this version, so let's not add it again
- if (findVersion(version) >= 0)
- {
- return;
- }
-#endif
-
- // Strip to make a shared library name
- UnownedStringSlice sharedLibraryName = filename.tail(m_prefix.getLength() - m_sharedLibraryStem.getLength());
- sharedLibraryName = filename.head(filename.getLength() - m_postfix.getLength());
-
- auto candidate = Candidate::make(Path::combine(m_basePath, sharedLibraryName), version);
-
- // If we already have this candidate, then skip
- if (m_candidates.indexOf(candidate) >= 0)
- {
- return;
- }
-
- // Add to the list of candidates
- m_candidates.add(candidate);
- }
- }
- }
-
- SlangResult findInDirectory(const String& path)
- {
- m_basePath = path;
- return Path::find(path, nullptr, this);
- }
-
- bool hasCandidates() const { return m_candidates.getCount() > 0; }
-
- NVRTCPathVisitor(const UnownedStringSlice& sharedLibraryStem):
- m_sharedLibraryStem(sharedLibraryStem)
- {
- // Work out the prefix and postfix of the shader
- StringBuilder buf;
- SharedLibrary::appendPlatformFileName(sharedLibraryStem, buf);
- const Index index = buf.indexOf(sharedLibraryStem);
- SLANG_ASSERT(index >= 0);
-
- m_prefix = buf.getUnownedSlice().head(index + sharedLibraryStem.getLength());
- m_postfix = buf.getUnownedSlice().tail(index + sharedLibraryStem.getLength());
- }
-
- String m_prefix;
- String m_postfix;
- String m_basePath;
- String m_sharedLibraryStem;
-
- List<Candidate> m_candidates;
-};
-
-static SlangResult _findAndLoadNVRTC(ISlangSharedLibraryLoader* loader, ComPtr<ISlangSharedLibrary>& outLibrary)
-{
-#if SLANG_WINDOWS_FAMILY
- // We only need to search 64 bit versions on windows
- NVRTCPathVisitor visitor(UnownedStringSlice::fromLiteral("nvrtc64_"));
-
- // First try the instance path (if supported on platform)
- {
- StringBuilder instancePath;
- if (SLANG_SUCCEEDED(PlatformUtil::getInstancePath(instancePath)))
- {
- visitor.findInDirectory(instancePath);
- }
- }
-
- // If we don't have a candidate try CUDA_PATH
- if (!visitor.hasCandidates())
- {
- StringBuilder buf;
- if (!SLANG_SUCCEEDED(PlatformUtil::getEnvironmentVariable(UnownedStringSlice::fromLiteral("CUDA_PATH"), buf)))
- {
- // Look for candidates in the directory
- visitor.findInDirectory(Path::combine(buf, "bin"));
- }
- }
-
- // If we haven't we go searching through PATH
- if (!visitor.hasCandidates())
- {
- List<UnownedStringSlice> splitPath;
-
- StringBuilder buf;
- if (SLANG_SUCCEEDED(PlatformUtil::getEnvironmentVariable(UnownedStringSlice::fromLiteral("PATH"), buf)))
- {
- // Split so we get individual paths
- List<UnownedStringSlice> paths;
- StringUtil::split(buf.getUnownedSlice(), ';', paths);
-
- // We use a pool to make sure we only check each path once
- StringSlicePool pool(StringSlicePool::Style::Empty);
-
- // We are going to search the paths in order
- for (const auto path : paths)
- {
- // PATH can have the same path multiple times. If we have already searched this path, we don't need to again
- if (!pool.has(path))
- {
- pool.add(path);
-
- Path::split(path, splitPath);
-
- // We could search every path, but here we restrict to paths that look like CUDA installations.
- // It's a path that contains a CUDA directory and has bin
- if (splitPath.indexOf("CUDA") >= 0 && splitPath[splitPath.getCount() - 1].caseInsensitiveEquals(UnownedStringSlice::fromLiteral("bin")))
- {
- // Okay lets search it
- visitor.findInDirectory(path);
- }
- }
- }
- }
- }
-
- // Put into version order with oldest first.
- visitor.sortCandidates();
-
- // We want to start with the newest version...
- for (Index i = visitor.m_candidates.getCount() - 1; i >= 0; --i)
- {
- const auto& candidate = visitor.m_candidates[i];
- if (SLANG_SUCCEEDED(loader->loadSharedLibrary(candidate.path.getBuffer(), outLibrary.writeRef())))
- {
- return SLANG_OK;
- }
- }
-#endif
- // This is an official-ish list of versions is here:
- // https://developer.nvidia.com/cuda-toolkit-archive
-
- // Filenames for NVRTC
- // https://docs.nvidia.com/cuda/nvrtc/index.html
- //
- // From this it appears on platforms other than windows the SharedLibrary name
- // should be nvrtc which is already tried, so we can give up now.
- return SLANG_E_NOT_FOUND;
-}
-
-/* static */SlangResult NVRTCDownstreamCompilerUtil::locateCompilers(const String& path, ISlangSharedLibraryLoader* loader, DownstreamCompilerSet* set)
-{
- ComPtr<ISlangSharedLibrary> library;
-
- // If the user supplies a path to their preferred version of NVRTC,
- // we just use this.
- if (path.getLength() != 0)
- {
- SLANG_RETURN_ON_FAIL(loader->loadSharedLibrary(path.getBuffer(), library.writeRef()));
- }
- else
- {
- // As a catch-all for non-Windows platforms, we search for
- // a library simply named `nvrtc` (well, `libnvrtc`) which
- // is expected to match whatever the user has installed.
- //
- // On Windows an installation could place the version of nvrtc it uses in the same directory
- // as the slang binary, such that it's loaded.
- // Using this name also allows a ISlangSharedLibraryLoader to easily identify what is required
- // and perhaps load a specific version
- if (SLANG_FAILED(loader->loadSharedLibrary("nvrtc", library.writeRef())))
- {
- // Try something more sophisticated to locate NVRTC
- SLANG_RETURN_ON_FAIL(_findAndLoadNVRTC(loader, library));
- }
- }
-
- SLANG_ASSERT(library);
- if (!library)
- {
- return SLANG_FAIL;
- }
-
- RefPtr<NVRTCDownstreamCompiler> compiler(new NVRTCDownstreamCompiler);
- SLANG_RETURN_ON_FAIL(compiler->init(library));
-
- set->addCompiler(compiler);
- return SLANG_OK;
-}
-
-
-}
diff --git a/source/core/slang-nvrtc-compiler.h b/source/core/slang-nvrtc-compiler.h
deleted file mode 100644
index 48c6d4da6..000000000
--- a/source/core/slang-nvrtc-compiler.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef SLANG_NVRTC_COMPILER_UTIL_H
-#define SLANG_NVRTC_COMPILER_UTIL_H
-
-#include "slang-downstream-compiler.h"
-
-#include "../core/slang-platform.h"
-
-namespace Slang
-{
-
-
-struct NVRTCDownstreamCompilerUtil
-{
- static SlangResult locateCompilers(const String& path, ISlangSharedLibraryLoader* loader, DownstreamCompilerSet* set);
-};
-
-}
-
-#endif
diff --git a/source/core/slang-string.cpp b/source/core/slang-string.cpp
index 62bc19754..0a5b7d260 100644
--- a/source/core/slang-string.cpp
+++ b/source/core/slang-string.cpp
@@ -5,6 +5,12 @@
namespace Slang
{
+ // HACK!
+ // JS: Many of the inlined functions of CharUtil just access a global map. That referencing this global is *NOT* enough to
+ // link correctly with CharUtil on linux for a shared library. The following call exists to try and force linkage of CharUtil
+ // for anything that uses core
+ static const auto s_charUtilLink = CharUtil::_ensureLink();
+
// TODO: this belongs in a different file:
SLANG_RETURN_NEVER void signalUnexpectedError(char const* message)
diff --git a/source/core/slang-visual-studio-compiler-util.cpp b/source/core/slang-visual-studio-compiler-util.cpp
deleted file mode 100644
index a3578483f..000000000
--- a/source/core/slang-visual-studio-compiler-util.cpp
+++ /dev/null
@@ -1,452 +0,0 @@
-// slang-visual-studio-compiler-util.cpp
-#include "slang-visual-studio-compiler-util.h"
-
-#include "slang-common.h"
-#include "../../slang-com-helper.h"
-#include "slang-string-util.h"
-
-// if Visual Studio import the visual studio platform specific header
-#if SLANG_VC
-# include "windows/slang-win-visual-studio-util.h"
-#endif
-
-#include "slang-io.h"
-
-namespace Slang
-{
-
-/* static */ SlangResult VisualStudioCompilerUtil::calcModuleFilePath(const CompileOptions& options, StringBuilder& outPath)
-{
- SLANG_ASSERT(options.modulePath.getLength());
-
- outPath.Clear();
-
- switch (options.targetType)
- {
- case TargetType::SharedLibrary:
- {
- outPath << options.modulePath << ".dll";
- return SLANG_OK;
- }
- case TargetType::Executable:
- {
- outPath << options.modulePath << ".exe";
- return SLANG_OK;
- }
- case TargetType::Object:
- {
- outPath << options.modulePath << ".obj";
- return SLANG_OK;
- }
- default: break;
- }
-
- return SLANG_FAIL;
-}
-
-/* static */SlangResult VisualStudioCompilerUtil::calcCompileProducts(const CompileOptions& options, ProductFlags flags, List<String>& outPaths)
-{
- SLANG_ASSERT(options.modulePath.getLength());
-
- outPaths.clear();
-
- if (flags & ProductFlag::Execution)
- {
- StringBuilder builder;
- SLANG_RETURN_ON_FAIL(calcModuleFilePath(options, builder));
- outPaths.add(builder);
- }
- if (flags & ProductFlag::Miscellaneous)
- {
- outPaths.add(options.modulePath + ".ilk");
-
- if (options.targetType == TargetType::SharedLibrary)
- {
- outPaths.add(options.modulePath + ".exp");
- outPaths.add(options.modulePath + ".lib");
- }
- }
- if (flags & ProductFlag::Compile)
- {
- outPaths.add(options.modulePath + ".obj");
- }
- if (flags & ProductFlag::Debug)
- {
- // TODO(JS): Could try and determine based on debug information
- outPaths.add(options.modulePath + ".pdb");
- }
-
- return SLANG_OK;
-}
-
-/* static */SlangResult VisualStudioCompilerUtil::calcArgs(const CompileOptions& options, CommandLine& cmdLine)
-{
- SLANG_ASSERT(options.sourceContents.getLength() == 0);
- SLANG_ASSERT(options.modulePath.getLength());
-
- // https://docs.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-alphabetically?view=vs-2019
-
- cmdLine.addArg("/nologo");
- // Generate complete debugging information
- cmdLine.addArg("/Zi");
- // Display full path of source files in diagnostics
- cmdLine.addArg("/FC");
-
- if (options.flags & CompileOptions::Flag::EnableExceptionHandling)
- {
- if (options.sourceLanguage == SLANG_SOURCE_LANGUAGE_CPP)
- {
- // https://docs.microsoft.com/en-us/cpp/build/reference/eh-exception-handling-model?view=vs-2019
- // Assumes c functions cannot throw
- cmdLine.addArg("/EHsc");
- }
- }
-
- if (options.flags & CompileOptions::Flag::Verbose)
- {
- // Doesn't appear to be a VS equivalent
- }
-
- if (options.flags & CompileOptions::Flag::EnableSecurityChecks)
- {
- cmdLine.addArg("/GS");
- }
- else
- {
- cmdLine.addArg("/GS-");
- }
-
- switch (options.debugInfoType)
- {
- default:
- {
- // Multithreaded statically linked runtime library
- cmdLine.addArg("/MD");
- break;
- }
- case DebugInfoType::None:
- {
- break;
- }
- case DebugInfoType::Maximal:
- {
- // Multithreaded statically linked *debug* runtime library
- cmdLine.addArg("/MDd");
- break;
- }
- }
-
- // /Fd - followed by name of the pdb file
- if (options.debugInfoType != DebugInfoType::None)
- {
- cmdLine.addPrefixPathArg("/Fd", options.modulePath, ".pdb");
- }
-
- switch (options.optimizationLevel)
- {
- case OptimizationLevel::None:
- {
- // No optimization
- cmdLine.addArg("/Od");
- break;
- }
- case OptimizationLevel::Default:
- {
- break;
- }
- case OptimizationLevel::High:
- {
- cmdLine.addArg("/O2");
- break;
- }
- case OptimizationLevel::Maximal:
- {
- cmdLine.addArg("/Ox");
- break;
- }
- default: break;
- }
-
- switch (options.floatingPointMode)
- {
- case FloatingPointMode::Default: break;
- case FloatingPointMode::Precise:
- {
- // precise is default behavior, VS also has 'strict'
- //
- // ```/fp:strict has behavior similar to /fp:precise, that is, the compiler preserves the source ordering and rounding properties of floating-point code when
- // it generates and optimizes object code for the target machine, and observes the standard when handling special values. In addition, the program may safely
- // access or modify the floating-point environment at runtime.```
-
- cmdLine.addArg("/fp:precise");
- break;
- }
- case FloatingPointMode::Fast:
- {
- cmdLine.addArg("/fp:fast");
- break;
- }
- }
-
- switch (options.targetType)
- {
- case TargetType::SharedLibrary:
- {
- // Create dynamic link library
- if (options.debugInfoType == DebugInfoType::None)
- {
- cmdLine.addArg("/LDd");
- }
- else
- {
- cmdLine.addArg("/LD");
- }
-
- cmdLine.addPrefixPathArg("/Fe", options.modulePath, ".dll");
- break;
- }
- case TargetType::Executable:
- {
- cmdLine.addPrefixPathArg("/Fe", options.modulePath, ".exe");
- break;
- }
- default: break;
- }
-
- // Object file specify it's location - needed if we are out
- cmdLine.addPrefixPathArg("/Fo", options.modulePath, ".obj");
-
- // Add defines
- for (const auto& define : options.defines)
- {
- StringBuilder builder;
- builder << "/D";
- builder << define.nameWithSig;
- if (define.value.getLength())
- {
- builder << "=" << define.value;
- }
-
- cmdLine.addArg(builder);
- }
-
- // Add includes
- for (const auto& include : options.includePaths)
- {
- cmdLine.addArg("/I");
- cmdLine.addArg(include);
- }
-
- // https://docs.microsoft.com/en-us/cpp/build/reference/eh-exception-handling-model?view=vs-2019
- // /Eha - Specifies the model of exception handling. (a, s, c, r are options)
-
- // Files to compile
- for (const auto& sourceFile : options.sourceFiles)
- {
- cmdLine.addArg(sourceFile);
- }
-
- // Link options (parameters past /link go to linker)
- cmdLine.addArg("/link");
-
- for (const auto& libPath : options.libraryPaths)
- {
- // Note that any escaping of the path is handled in the ProcessUtil::
- cmdLine.addPrefixPathArg("/LIBPATH:", libPath);
- }
-
- return SLANG_OK;
-}
-
-static SlangResult _parseSeverity(const UnownedStringSlice& in, DownstreamDiagnostics::Diagnostic::Severity& outSeverity)
-{
- typedef DownstreamDiagnostics::Diagnostic::Severity Type;
-
- if (in == "error" || in == "fatal error")
- {
- outSeverity = Type::Error;
- }
- else if (in == "warning")
- {
- outSeverity = Type::Warning;
- }
- else if (in == "info")
- {
- outSeverity = Type::Info;
- }
- else
- {
- return SLANG_FAIL;
- }
- return SLANG_OK;
-}
-
-static SlangResult _parseVisualStudioLine(const UnownedStringSlice& line, DownstreamDiagnostics::Diagnostic& outDiagnostic)
-{
- typedef DownstreamDiagnostics::Diagnostic Diagnostic;
-
- UnownedStringSlice linkPrefix = UnownedStringSlice::fromLiteral("LINK :");
- if (line.startsWith(linkPrefix))
- {
- outDiagnostic.stage = Diagnostic::Stage::Link;
- outDiagnostic.severity = Diagnostic::Severity::Info;
-
- outDiagnostic.text = UnownedStringSlice(line.begin() + linkPrefix.getLength(), line.end());
-
- return SLANG_OK;
- }
-
- outDiagnostic.stage = Diagnostic::Stage::Compile;
-
- const char*const start = line.begin();
- const char*const end = line.end();
-
- UnownedStringSlice postPath;
- // Handle the path and line no
- {
- const char* cur = start;
-
- // We have to assume it is a path up to the first : that isn't part of a drive specification
-
- if ((end - cur > 2) && Path::isDriveSpecification(UnownedStringSlice(start, start + 2)))
- {
- // Skip drive spec
- cur += 2;
- }
-
- // Find the first colon after this
- Index colonIndex = UnownedStringSlice(cur, end).indexOf(':');
- if (colonIndex < 0)
- {
- return SLANG_FAIL;
- }
-
- // Looks like we have a line number
- if (cur[colonIndex - 1] == ')')
- {
- const char* lineNoEnd = cur + colonIndex - 1;
- const char* lineNoStart = lineNoEnd;
- while (lineNoStart > start && *lineNoStart != '(')
- {
- lineNoStart--;
- }
- // Check this appears plausible
- if (*lineNoStart != '(' || *lineNoEnd != ')')
- {
- return SLANG_FAIL;
- }
- Int numDigits = 0;
- Int lineNo = 0;
- for (const char* digitCur = lineNoStart + 1; digitCur < lineNoEnd; ++digitCur)
- {
- char c = *digitCur;
- if (c >= '0' && c <= '9')
- {
- lineNo = lineNo * 10 + (c - '0');
- numDigits++;
- }
- else
- {
- return SLANG_FAIL;
- }
- }
- if (numDigits == 0)
- {
- return SLANG_FAIL;
- }
-
- outDiagnostic.filePath = UnownedStringSlice(start, lineNoStart);
- outDiagnostic.fileLine = lineNo;
- }
- else
- {
- outDiagnostic.filePath = UnownedStringSlice(start, cur + colonIndex);
- outDiagnostic.fileLine = 0;
- }
-
- // Save the remaining text in 'postPath'
- postPath = UnownedStringSlice(cur + colonIndex + 1, end);
- }
-
- // Split up the error section
- UnownedStringSlice postError;
- {
- // tests/cpp-compiler/c-compile-link-error.exe : fatal error LNK1120: 1 unresolved externals
-
- const Index errorColonIndex = postPath.indexOf(':');
- if (errorColonIndex < 0)
- {
- return SLANG_FAIL;
- }
-
- const UnownedStringSlice errorSection = UnownedStringSlice(postPath.begin(), postPath.begin() + errorColonIndex);
- Index errorCodeIndex = errorSection.lastIndexOf(' ');
- if (errorCodeIndex < 0)
- {
- return SLANG_FAIL;
- }
-
- // Extract the code
- outDiagnostic.code = UnownedStringSlice(errorSection.begin() + errorCodeIndex + 1, errorSection.end());
- if (outDiagnostic.code.startsWith(UnownedStringSlice::fromLiteral("LNK")))
- {
- outDiagnostic.stage = Diagnostic::Stage::Link;
- }
-
- // Extract the bit before the code
- SLANG_RETURN_ON_FAIL(_parseSeverity(UnownedStringSlice(errorSection.begin(), errorSection.begin() + errorCodeIndex).trim(), outDiagnostic.severity));
-
- // Link codes start with LNK prefix
- postError = UnownedStringSlice(postPath.begin() + errorColonIndex + 1, end);
- }
-
- outDiagnostic.text = postError;
-
- return SLANG_OK;
-}
-
-/* static */SlangResult VisualStudioCompilerUtil::parseOutput(const ExecuteResult& exeRes, DownstreamDiagnostics& outDiagnostics)
-{
- outDiagnostics.reset();
-
- outDiagnostics.rawDiagnostics = exeRes.standardOutput;
-
- for (auto line : LineParser(exeRes.standardOutput.getUnownedSlice()))
- {
-#if 0
- fwrite(line.begin(), 1, line.size(), stdout);
- fprintf(stdout, "\n");
-#endif
-
- Diagnostic diagnostic;
- if (SLANG_SUCCEEDED(_parseVisualStudioLine(line, diagnostic)))
- {
- outDiagnostics.diagnostics.add(diagnostic);
- }
- }
-
- // if it has a compilation error.. set on output
- if (outDiagnostics.has(Diagnostic::Severity::Error))
- {
- outDiagnostics.result = SLANG_FAIL;
- }
-
- return SLANG_OK;
-}
-
-/* static */SlangResult VisualStudioCompilerUtil::locateCompilers(const String& path, ISlangSharedLibraryLoader* loader, DownstreamCompilerSet* set)
-{
- SLANG_UNUSED(loader);
-
- // TODO(JS): We don't support fixed path for visual studio just yet
- if (path.getLength() == 0)
- {
-#if SLANG_VC
- return WinVisualStudioUtil::find(set);
-#endif
- }
-
- return SLANG_OK;
-}
-
-}
diff --git a/source/core/slang-visual-studio-compiler-util.h b/source/core/slang-visual-studio-compiler-util.h
deleted file mode 100644
index 018dde212..000000000
--- a/source/core/slang-visual-studio-compiler-util.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#ifndef SLANG_VISUAL_STUDIO_COMPILER_UTIL_H
-#define SLANG_VISUAL_STUDIO_COMPILER_UTIL_H
-
-#include "slang-downstream-compiler.h"
-
-namespace Slang
-{
-
-
-struct VisualStudioCompilerUtil : public DownstreamCompilerBaseUtil
-{
- /// Calculate Visual Studio family compilers cmdLine arguments from options
- static SlangResult calcArgs(const CompileOptions& options, CommandLine& cmdLine);
- /// Parse Visual Studio exeRes into CPPCompiler::Output
- static SlangResult parseOutput(const ExecuteResult& exeRes, DownstreamDiagnostics& outOutput);
-
- static SlangResult calcModuleFilePath(const CompileOptions& options, StringBuilder& outPath);
-
- static SlangResult calcCompileProducts(const CompileOptions& options, ProductFlags flags, List<String>& outPaths);
-
- static SlangResult locateCompilers(const String& path, ISlangSharedLibraryLoader* loader, DownstreamCompilerSet* set);
-};
-
-class VisualStudioDownstreamCompiler : public CommandLineDownstreamCompiler
-{
-public:
- typedef CommandLineDownstreamCompiler Super;
- typedef VisualStudioCompilerUtil Util;
-
- // CommandLineDownstreamCompiler impl - just forwards to the Util
- virtual SlangResult calcArgs(const CompileOptions& options, CommandLine& cmdLine) SLANG_OVERRIDE { return Util::calcArgs(options, cmdLine); }
- virtual SlangResult parseOutput(const ExecuteResult& exeResult, DownstreamDiagnostics& output) SLANG_OVERRIDE { return Util::parseOutput(exeResult, output); }
- virtual SlangResult calcModuleFilePath(const CompileOptions& options, StringBuilder& outPath) SLANG_OVERRIDE { return Util::calcModuleFilePath(options, outPath); }
- virtual SlangResult calcCompileProducts(const CompileOptions& options, ProductFlags productFlags, List<String>& outPaths) SLANG_OVERRIDE { return Util::calcCompileProducts(options, productFlags, outPaths); }
-
- VisualStudioDownstreamCompiler(const Desc& desc):Super(desc) {}
-};
-
-
-}
-
-#endif
diff --git a/source/core/windows/slang-win-visual-studio-util.cpp b/source/core/windows/slang-win-visual-studio-util.cpp
deleted file mode 100644
index 078406d99..000000000
--- a/source/core/windows/slang-win-visual-studio-util.cpp
+++ /dev/null
@@ -1,366 +0,0 @@
-#include "slang-win-visual-studio-util.h"
-
-#include "../slang-common.h"
-#include "../slang-process-util.h"
-#include "../slang-string-util.h"
-
-#include "../slang-visual-studio-compiler-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::find(DownstreamCompilerSet* set)
-{
- const int versionCount = SLANG_COUNT_OF(s_versionInfos);
-
- for (int i = versionCount - 1; i >= 0; --i)
- {
- const auto& versionInfo = s_versionInfos[i];
- auto desc = getDesc(versionInfo.version);
-
- VersionPath versionPath;
- if (!set->getCompiler(desc) && SLANG_SUCCEEDED(_find(i, versionPath)))
- {
- RefPtr<CommandLineDownstreamCompiler> compiler = new VisualStudioDownstreamCompiler(desc);
- calcExecuteCompilerArgs(versionPath, compiler->m_cmdLine);
- set->addCompiler(compiler);
- }
- }
-
- return SLANG_OK;
-}
-
-/* static */void WinVisualStudioUtil::calcExecuteCompilerArgs(const VersionPath& versionPath, CommandLine& outCmdLine)
-{
- // 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
-
- 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");
-
- outCmdLine = cmdLine;
-}
-
-/* static */SlangResult WinVisualStudioUtil::executeCompiler(const VersionPath& versionPath, const CommandLine& commandLine, ExecuteResult& outResult)
-{
- CommandLine cmdLine;
- calcExecuteCompilerArgs(versionPath, cmdLine);
- // 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
diff --git a/source/core/windows/slang-win-visual-studio-util.h b/source/core/windows/slang-win-visual-studio-util.h
deleted file mode 100644
index 530d18582..000000000
--- a/source/core/windows/slang-win-visual-studio-util.h
+++ /dev/null
@@ -1,68 +0,0 @@
-#ifndef SLANG_WIN_VISUAL_STUDIO_UTIL_H
-#define SLANG_WIN_VISUAL_STUDIO_UTIL_H
-
-#include "../slang-list.h"
-#include "../slang-string.h"
-
-#include "../slang-process-util.h"
-
-#include "../slang-downstream-compiler.h"
-
-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
- };
-
- /// 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);
-
- /// Create the cmdLine to start compiler for specified path
- static void calcExecuteCompilerArgs(const VersionPath& versionPath, CommandLine& outCmdLine);
-
- /// 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;
- }
-
-};
-
-} // namespace Slang
-
-#endif