diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2021-04-01 13:39:11 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-01 10:39:11 -0700 |
| commit | fa31d21ba92669a521a7768467246918e3947e02 (patch) | |
| tree | af98a593e24bc6309ac4d11a59562be4b22c93d7 /source/core | |
| parent | 3f1632a1450a5879f337b4bd178e48880cd583f8 (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.cpp | 11 | ||||
| -rw-r--r-- | source/core/slang-char-util.h | 9 | ||||
| -rw-r--r-- | source/core/slang-downstream-compiler.cpp | 716 | ||||
| -rw-r--r-- | source/core/slang-downstream-compiler.h | 501 | ||||
| -rw-r--r-- | source/core/slang-file-system.cpp | 888 | ||||
| -rw-r--r-- | source/core/slang-file-system.h | 253 | ||||
| -rw-r--r-- | source/core/slang-gcc-compiler-util.cpp | 645 | ||||
| -rw-r--r-- | source/core/slang-gcc-compiler-util.h | 56 | ||||
| -rw-r--r-- | source/core/slang-name-convention-util.cpp | 213 | ||||
| -rw-r--r-- | source/core/slang-name-convention-util.h | 54 | ||||
| -rw-r--r-- | source/core/slang-nvrtc-compiler.cpp | 773 | ||||
| -rw-r--r-- | source/core/slang-nvrtc-compiler.h | 19 | ||||
| -rw-r--r-- | source/core/slang-string.cpp | 6 | ||||
| -rw-r--r-- | source/core/slang-visual-studio-compiler-util.cpp | 452 | ||||
| -rw-r--r-- | source/core/slang-visual-studio-compiler-util.h | 42 | ||||
| -rw-r--r-- | source/core/windows/slang-win-visual-studio-util.cpp | 366 | ||||
| -rw-r--r-- | source/core/windows/slang-win-visual-studio-util.h | 68 |
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 |
