summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2019-06-21 17:39:32 -0400
committerGitHub <noreply@github.com>2019-06-21 17:39:32 -0400
commit714b0881974965e8fcfbc57b452ef648290d14a1 (patch)
treef283fb216113a9517686acf49ca436512500b4cb /source
parent64eec046f5a77ebad9564a935c4fad8df08ea6eb (diff)
Parsing CPP Compiler output (#994)
* Added extractLine line parsing to StringUtil. Use for matching lines instead of calcLines. calcLines uses extractLine to extract lines. Fixed problems found in output of some tests- due to how a how final line is handled. Now a final line has a \r or \n\r combination, but nothing else after it, it is considered the last line (not the line after it). * Use StringUtil::extractLine in slang-generate. * Improved comment on extractLine * Remove test code from StringUtil::extractLine * Made StringUtil::extractLine act as if line terminators are 'separators'. Added unit-test-string.cpp - to check behavior. * Adding LineParser - not entirely necessary, but slightly easier to use. * Hack to output start of tests. * WIP parsing CPPCompiler output. * Make extractLine return a bool. * First attempt at Visual Studio output parsing. * Add handling for checking error returning from CPPCompiler. * First pass parsing output of Gcc/Clang. * Split out VisualStudioCompilerUtil and GCCCompilerUtil. Simplified parsing of versions. * Simplify CPPCompiler::Output interface. * Fix problem with cpp-compiler on linux targets. * Add shared library link error. * Improving GCC/Clang parsing output. * Make cpp compiler parsing function able to return a SlangResult. * Handling for 'info' on clang * Add expected result for c-compile-shared-library-error.c * * Add flags such that link errors on shared libraries are supported. * Added StringUtil::join * Turn off the link shared library unfound symbol option on MacOS because it causes an error (and it's not needed on that target). * Add natvis inclusion back to visual studio projects. * Display message to try and determine crash problem on travisbuild. * Fix bug in handling continuations for clang. Disabled output of exception text. * WIP: See what clang is outputting that is parsing incorrectly on travis. * More handling for travis clang parsing issue. * Restore natvis to core.vcxproj * Fix visual studio project such that it still as natvis.
Diffstat (limited to 'source')
-rw-r--r--source/core/core.vcxproj4
-rw-r--r--source/core/core.vcxproj.filters12
-rw-r--r--source/core/slang-cpp-compiler.cpp409
-rw-r--r--source/core/slang-cpp-compiler.h94
-rw-r--r--source/core/slang-gcc-compiler-util.cpp460
-rw-r--r--source/core/slang-gcc-compiler-util.h33
-rw-r--r--source/core/slang-string-util.cpp80
-rw-r--r--source/core/slang-string-util.h10
-rw-r--r--source/core/slang-string.cpp17
-rw-r--r--source/core/slang-string.h21
-rw-r--r--source/core/slang-visual-studio-compiler-util.cpp300
-rw-r--r--source/core/slang-visual-studio-compiler-util.h26
-rw-r--r--source/core/windows/slang-win-visual-studio-util.cpp4
-rw-r--r--source/core/windows/slang-win-visual-studio-util.h5
14 files changed, 1137 insertions, 338 deletions
diff --git a/source/core/core.vcxproj b/source/core/core.vcxproj
index dd2f3c144..9238cf058 100644
--- a/source/core/core.vcxproj
+++ b/source/core/core.vcxproj
@@ -180,6 +180,7 @@
<ClInclude Include="slang-dictionary.h" />
<ClInclude Include="slang-exception.h" />
<ClInclude Include="slang-free-list.h" />
+ <ClInclude Include="slang-gcc-compiler-util.h" />
<ClInclude Include="slang-hash.h" />
<ClInclude Include="slang-io.h" />
<ClInclude Include="slang-list.h" />
@@ -203,6 +204,7 @@
<ClInclude Include="slang-token-reader.h" />
<ClInclude Include="slang-type-traits.h" />
<ClInclude Include="slang-uint-set.h" />
+ <ClInclude Include="slang-visual-studio-compiler-util.h" />
<ClInclude Include="slang-writer.h" />
<ClInclude Include="windows\slang-win-visual-studio-util.h" />
</ItemGroup>
@@ -210,6 +212,7 @@
<ClCompile Include="slang-byte-encode-util.cpp" />
<ClCompile Include="slang-cpp-compiler.cpp" />
<ClCompile Include="slang-free-list.cpp" />
+ <ClCompile Include="slang-gcc-compiler-util.cpp" />
<ClCompile Include="slang-io.cpp" />
<ClCompile Include="slang-memory-arena.cpp" />
<ClCompile Include="slang-object-scope-manager.cpp" />
@@ -226,6 +229,7 @@
<ClCompile Include="slang-text-io.cpp" />
<ClCompile Include="slang-token-reader.cpp" />
<ClCompile Include="slang-uint-set.cpp" />
+ <ClCompile Include="slang-visual-studio-compiler-util.cpp" />
<ClCompile Include="slang-writer.cpp" />
<ClCompile Include="windows\slang-win-process-util.cpp" />
<ClCompile Include="windows\slang-win-visual-studio-util.cpp" />
diff --git a/source/core/core.vcxproj.filters b/source/core/core.vcxproj.filters
index 8d7add8ea..3b3e429ba 100644
--- a/source/core/core.vcxproj.filters
+++ b/source/core/core.vcxproj.filters
@@ -39,6 +39,9 @@
<ClInclude Include="slang-free-list.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="slang-gcc-compiler-util.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="slang-hash.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -108,6 +111,9 @@
<ClInclude Include="slang-uint-set.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="slang-visual-studio-compiler-util.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="slang-writer.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -125,6 +131,9 @@
<ClCompile Include="slang-free-list.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="slang-gcc-compiler-util.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="slang-io.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -173,6 +182,9 @@
<ClCompile Include="slang-uint-set.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="slang-visual-studio-compiler-util.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="slang-writer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/source/core/slang-cpp-compiler.cpp b/source/core/slang-cpp-compiler.cpp
index 18cb9cdae..0ed0a1ba8 100644
--- a/source/core/slang-cpp-compiler.cpp
+++ b/source/core/slang-cpp-compiler.cpp
@@ -8,374 +8,167 @@
#include "slang-io.h"
#include "slang-shared-library.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-visual-studio-compiler-util.h"
+#include "slang-gcc-compiler-util.h"
+
namespace Slang
{
-/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! GenericCPPCompiler !!!!!!!!!!!!!!!!!!!!!!*/
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CPPCompiler::OutputMessage !!!!!!!!!!!!!!!!!!!!!!*/
-SlangResult GenericCPPCompiler::compile(const CompileOptions& options, ExecuteResult& outResult)
+/* static */UnownedStringSlice CPPCompiler::OutputMessage::getTypeText(OutputMessage::Type type)
{
- // Copy the command line options
- CommandLine cmdLine(m_cmdLine);
-
- // Append command line args to the end of cmdLine using the target specific function for the specified options
- m_func(options, cmdLine);
-
-#if 0
- // Test
- {
- String line = ProcessUtil::getCommandLineString(cmdLine);
- printf("%s", line.getBuffer());
- }
-#endif
-
- SlangResult res = ProcessUtil::execute(cmdLine, outResult);
-
-#if 0
+ typedef OutputMessage::Type Type;
+ switch (type)
{
- printf("stdout=\"%s\"\nstderr=\"%s\"\nret=%d\n", outResult.standardOutput.getBuffer(), outResult.standardError.getBuffer(), int(outResult.resultCode));
+ default: return UnownedStringSlice::fromLiteral("Unknown");
+ case Type::Info: return UnownedStringSlice::fromLiteral("Info");
+ case Type::Warning: return UnownedStringSlice::fromLiteral("Warning");
+ case Type::Error: return UnownedStringSlice::fromLiteral("Error");
}
-#endif
-
- return res;
}
-/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CPPCompilerUtil !!!!!!!!!!!!!!!!!!!!!!*/
-static bool _isDigit(char c)
-{
- return c >= '0' && c <= '9';
-}
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CPPCompiler::Output !!!!!!!!!!!!!!!!!!!!!!*/
-static bool _isWhiteSpace(char c)
+Index CPPCompiler::Output::getCountByType(OutputMessage::Type type) const
{
- return c == ' ' || c == '\t' || c == '\n' || c == '\r';
-}
-
-/* static */SlangResult CPPCompilerUtil::parseGCCFamilyVersion(const UnownedStringSlice& text, const UnownedStringSlice& prefix, CPPCompiler::Desc& outDesc)
-{
- List<UnownedStringSlice> lines;
- StringUtil::calcLines(text, lines);
-
- for (auto line : lines)
+ Index count = 0;
+ for (const auto& msg : messages)
{
- // TODO(JS): Ugh - having to turn into a string to do this test isn't great.
- if (String(line).startsWith(prefix))
- {
- UnownedStringSlice versionSlice(line.begin() + prefix.size(), line.end());
-
- List<Int> digits;
-
- const char* cur = versionSlice.begin();
- const char* end = versionSlice.end();
-
- // Consume white space
- while (cur < end && _isWhiteSpace(*cur)) cur++;
-
- // Version is in format 0.0.0
- while (true)
- {
- Int value = 0;
- const char* start = cur;
- while (cur < end && _isDigit(*cur))
- {
- value = value * 10 + (*cur - '0');
- cur++;
- }
-
- if (cur <= start)
- {
- break;
- }
-
- digits.add(value);
-
- if (cur < end && *cur == '.')
- {
- cur++;
- }
- }
-
- if (digits.getCount() < 2)
- {
- return SLANG_FAIL;
- }
-
- outDesc.majorVersion = digits[0];
- outDesc.minorVersion = digits[1];
- return SLANG_OK;
- }
+ count += Index(msg.type == type);
}
-
- return SLANG_FAIL;
+ return count;
}
-SlangResult CPPCompilerUtil::calcGCCFamilyVersion(const String& exeName, CPPCompiler::Desc& outDesc)
+Int CPPCompiler::Output::countByStage(OutputMessage::Stage stage, Index counts[Int(OutputMessage::Type::CountOf)]) const
{
- 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 CPPCompiler::Type types[] =
+ Int count = 0;
+ ::memset(counts, 0, sizeof(Index) * Int(OutputMessage::Type::CountOf));
+ for (const auto& msg : messages)
{
- CPPCompiler::Type::Clang,
- CPPCompiler::Type::GCC,
- CPPCompiler::Type::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(parseGCCFamilyVersion(exeRes.standardError.getUnownedSlice(), prefixes[i], outDesc)))
+ if (msg.stage == stage)
{
- return SLANG_OK;
+ count++;
+ counts[Index(msg.type)]++;
}
}
- return SLANG_FAIL;
+ return count++;
}
-/* static */void CPPCompilerUtil::calcVisualStudioArgs(const CompileOptions& options, CommandLine& cmdLine)
+static void _appendCounts(const Index counts[Int(CPPCompiler::OutputMessage::Type::CountOf)], StringBuilder& out)
{
- // 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");
+ typedef CPPCompiler::OutputMessage::Type Type;
- if (options.flags & CompileOptions::Flag::EnableExceptionHandling)
+ for (Index i = 0; i < Int(Type::CountOf); i++)
{
- if (options.sourceType == SourceType::CPP)
+ if (counts[i] > 0)
{
- // https://docs.microsoft.com/en-us/cpp/build/reference/eh-exception-handling-model?view=vs-2019
- // Assumes c functions cannot throw
- cmdLine.addArg("/EHsc");
+ out << CPPCompiler::OutputMessage::getTypeText(Type(i)) << "(" << counts[i] << ") ";
}
}
+}
- switch (options.optimizationLevel)
- {
- case OptimizationLevel::Debug:
- {
- // No optimization
- cmdLine.addArg("/Od");
-
- cmdLine.addArg("/MDd");
- break;
- }
- case OptimizationLevel::Normal:
- {
- cmdLine.addArg("/O2");
- // Multithreaded DLL
- cmdLine.addArg("/MD");
- break;
- }
- default: break;
- }
-
- // /Fd - followed by name of the pdb file
- if (options.debugInfoType != DebugInfoType::None)
- {
- cmdLine.addPrefixPathArg("/Fd", options.modulePath, ".pdb");
- }
-
- switch (options.targetType)
+static void _appendSimplified(const Index counts[Int(CPPCompiler::OutputMessage::Type::CountOf)], StringBuilder& out)
+{
+ typedef CPPCompiler::OutputMessage::Type Type;
+ for (Index i = 0; i < Int(Type::CountOf); i++)
{
- case TargetType::SharedLibrary:
+ if (counts[i] > 0)
{
- // Create dynamic link library
- if (options.optimizationLevel == OptimizationLevel::Debug)
- {
- cmdLine.addArg("/LDd");
- }
- else
- {
- cmdLine.addArg("/LD");
- }
-
- cmdLine.addPrefixPathArg("/Fe", options.modulePath, ".dll");
- break;
+ out << CPPCompiler::OutputMessage::getTypeText(Type(i)) << " ";
}
- 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)
+void CPPCompiler::Output::appendSummary(StringBuilder& out) const
+{
+ Index counts[Int(OutputMessage::Type::CountOf)];
+ if (countByStage(OutputMessage::Stage::Compile, counts) > 0)
{
- StringBuilder builder;
- builder << define.nameWithSig;
- if (define.value.getLength())
- {
- builder << "=" << define.value;
- }
-
- cmdLine.addArg(builder);
+ out << "Compile: ";
+ _appendCounts(counts, out);
+ out << "\n";
}
-
- // Add includes
- for (const auto& include : options.includePaths)
+ if (countByStage(OutputMessage::Stage::Link, counts) > 0)
{
- cmdLine.addArg("/I");
- cmdLine.addArg(include);
+ out << "Link: ";
+ _appendCounts(counts, out);
+ out << "\n";
}
+}
- // 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)
+void CPPCompiler::Output::appendSimplifiedSummary(StringBuilder& out) const
+{
+ Index counts[Int(OutputMessage::Type::CountOf)];
+ if (countByStage(OutputMessage::Stage::Compile, counts) > 0)
{
- cmdLine.addArg(sourceFile);
+ out << "Compile: ";
+ _appendSimplified(counts, out);
+ out << "\n";
}
-
- // Link options (parameters past /link go to linker)
- cmdLine.addArg("/link");
-
- for (const auto& libPath : options.libraryPaths)
+ if (countByStage(OutputMessage::Stage::Link, counts) > 0)
{
- // Note that any escaping of the path is handled in the ProcessUtil::
- cmdLine.addPrefixPathArg("/LIBPATH:", libPath);
+ out << "Link: ";
+ _appendSimplified(counts, out);
+ out << "\n";
}
}
-/* static */void CPPCompilerUtil::calcGCCFamilyArgs(const CompileOptions& options, CommandLine& cmdLine)
+void CPPCompiler::Output::removeByType(OutputMessage::Type type)
{
- cmdLine.addArg("-fvisibility=hidden");
- // Use shared libraries
- //cmdLine.addArg("-shared");
-
- switch (options.optimizationLevel)
+ Index count = messages.getCount();
+ for (Index i = 0; i < count; ++i)
{
- case OptimizationLevel::Debug:
- {
- // No optimization
- cmdLine.addArg("-O0");
- break;
- }
- case OptimizationLevel::Normal:
+ if (messages[i].type == type)
{
- cmdLine.addArg("-Os");
- break;
+ messages.removeAt(i);
+ i--;
+ count--;
}
- default: break;
}
+}
- if (options.debugInfoType != DebugInfoType::None)
- {
- cmdLine.addArg("-g");
- }
-
- switch (options.targetType)
- {
- case TargetType::SharedLibrary:
- {
- // Shared library
- cmdLine.addArg("-shared");
- // Position independent
- cmdLine.addArg("-fPIC");
-
- String sharedLibraryPath = SharedLibrary::calcPlatformPath(options.modulePath.getUnownedSlice());
-
- cmdLine.addArg("-o");
- cmdLine.addArg(sharedLibraryPath);
- break;
- }
- case TargetType::Executable:
- {
- cmdLine.addArg("-o");
-
- StringBuilder builder;
- builder << options.modulePath;
- builder << ProcessUtil::getExecutableSuffix();
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! GenericCPPCompiler !!!!!!!!!!!!!!!!!!!!!!*/
- cmdLine.addArg(options.modulePath);
- break;
- }
- case TargetType::Object:
- {
- // Don't link, just produce object file
- cmdLine.addArg("-c");
- break;
- }
- default: break;
- }
+SlangResult GenericCPPCompiler::compile(const CompileOptions& options, Output& outOutput)
+{
+ outOutput.reset();
- // Add defines
- for (const auto& define : options.defines)
- {
- StringBuilder builder;
- builder << define.nameWithSig;
- if (define.value.getLength())
- {
- builder << "=" << define.value;
- }
+ // Copy the command line options
+ CommandLine cmdLine(m_cmdLine);
- cmdLine.addArg(builder);
- }
+ // Append command line args to the end of cmdLine using the target specific function for the specified options
+ m_calcArgsFunc(options, cmdLine);
- // Add includes
- for (const auto& include : options.includePaths)
- {
- cmdLine.addArg("-I");
- cmdLine.addArg(include);
- }
+ ExecuteResult exeRes;
- // Link options
- if (0)
+#if 0
+ // Test
{
- StringBuilder linkOptions;
- linkOptions << "Wl,";
- cmdLine.addArg(linkOptions);
+ String line = ProcessUtil::getCommandLineString(cmdLine);
+ printf("%s", line.getBuffer());
}
+#endif
- // Files to compile
- for (const auto& sourceFile : options.sourceFiles)
- {
- cmdLine.addArg(sourceFile);
- }
+ SLANG_RETURN_ON_FAIL(ProcessUtil::execute(cmdLine, exeRes));
- for (const auto& libPath : options.libraryPaths)
+#if 0
{
- // Note that any escaping of the path is handled in the ProcessUtil::
- cmdLine.addArg("-L");
- cmdLine.addArg(libPath);
- cmdLine.addArg("-F");
- cmdLine.addArg(libPath);
+ printf("stdout=\"%s\"\nstderr=\"%s\"\nret=%d\n", exeRes.standardOutput.getBuffer(), exeRes.standardError.getBuffer(), int(exeRes.resultCode));
}
+#endif
- if (options.sourceType == SourceType::CPP)
- {
- // Make STD libs available
- cmdLine.addArg("-lstdc++");
- }
+ return m_parseOutputFunc(exeRes, outOutput);
}
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CPPCompilerUtil !!!!!!!!!!!!!!!!!!!!!!*/
+
static CPPCompiler::Desc _calcCompiledWithDesc()
{
CPPCompiler::Desc desc = {};
@@ -383,19 +176,19 @@ static CPPCompiler::Desc _calcCompiledWithDesc()
#if SLANG_VC
desc = WinVisualStudioUtil::getDesc(WinVisualStudioUtil::getCompiledVersion());
#elif SLANG_CLANG
- desc.type = CPPCompiler::Type::Clang;
+ desc.type = CPPCompiler::CompilerType::Clang;
desc.majorVersion = Int(__clang_major__);
desc.minorVersion = Int(__clang_minor__);
#elif SLANG_SNC
- desc.type = CPPCompiler::Type::SNC;
+ desc.type = CPPCompiler::CompilerType::SNC;
#elif SLANG_GHS
- desc.type = CPPCompiler::Type::GHS;
+ desc.type = CPPCompiler::CompilerType::GHS;
#elif SLANG_GCC
- desc.type = CPPCompiler::Type::GCC;
+ desc.type = CPPCompiler::CompilerType::GCC;
desc.majorVersion = Int(__GNUC__);
desc.minorVersion = Int(__GNUC_MINOR__);
#else
- desc.type = CPPCompiler::Type::Unknown;
+ desc.type = CPPCompiler::CompilerType::Unknown;
#endif
return desc;
@@ -418,7 +211,7 @@ const CPPCompiler::Desc& CPPCompilerUtil::getCompiledWithDesc()
{
Int bestIndex = -1;
- const CPPCompiler::Type type = desc.type;
+ const CPPCompiler::CompilerType type = desc.type;
Int maxVersionValue = 0;
Int minVersionDiff = 0x7fffffff;
@@ -488,10 +281,10 @@ const CPPCompiler::Desc& CPPCompilerUtil::getCompiledWithDesc()
}
// If we are gcc, we can try clang and vice versa
- if (desc.type == CPPCompiler::Type::GCC || desc.type == CPPCompiler::Type::Clang)
+ if (desc.type == CPPCompiler::CompilerType::GCC || desc.type == CPPCompiler::CompilerType::Clang)
{
CPPCompiler::Desc compatible = desc;
- compatible.type = (compatible.type == CPPCompiler::Type::Clang) ? CPPCompiler::Type::GCC : CPPCompiler::Type::Clang;
+ compatible.type = (compatible.type == CPPCompiler::CompilerType::Clang) ? CPPCompiler::CompilerType::GCC : CPPCompiler::CompilerType::Clang;
compiler = findCompiler(compilers, MatchType::MinGreaterEqual, compatible);
if (compiler)
@@ -513,9 +306,9 @@ const CPPCompiler::Desc& CPPCompilerUtil::getCompiledWithDesc()
static void _addGCCFamilyCompiler(const String& exeName, CPPCompilerSet* compilerSet)
{
CPPCompiler::Desc desc;
- if (SLANG_SUCCEEDED(CPPCompilerUtil::calcGCCFamilyVersion(exeName, desc)))
+ if (SLANG_SUCCEEDED(GCCCompilerUtil::calcVersion(exeName, desc)))
{
- RefPtr<CPPCompiler> compiler(new GenericCPPCompiler(desc, exeName, &CPPCompilerUtil::calcGCCFamilyArgs));
+ RefPtr<CPPCompiler> compiler(new GenericCPPCompiler(desc, exeName, &GCCCompilerUtil::calcArgs, &GCCCompilerUtil::parseOutput));
compilerSet->addCompiler(compiler);
}
}
diff --git a/source/core/slang-cpp-compiler.h b/source/core/slang-cpp-compiler.h
index 83c18bca2..60b534556 100644
--- a/source/core/slang-cpp-compiler.h
+++ b/source/core/slang-cpp-compiler.h
@@ -13,7 +13,7 @@ class CPPCompiler: public RefObject
{
public:
typedef RefObject Super;
- enum class Type
+ enum class CompilerType
{
Unknown,
VisualStudio,
@@ -41,9 +41,9 @@ public:
Int getVersionValue() const { return majorVersion * 100 + minorVersion; }
/// Ctor
- Desc(Type inType = Type::Unknown, Int inMajorVersion = 0, Int inMinorVersion = 0):type(inType), majorVersion(inMajorVersion), minorVersion(inMinorVersion) {}
+ Desc(CompilerType inType = CompilerType::Unknown, Int inMajorVersion = 0, Int inMinorVersion = 0):type(inType), majorVersion(inMajorVersion), minorVersion(inMinorVersion) {}
- Type type; ///< The type of the compiler
+ CompilerType type; ///< The type of the compiler
Int majorVersion; ///< Major version (interpretation is type specific)
Int minorVersion; ///< Minor version
};
@@ -101,10 +101,68 @@ public:
List<String> libraryPaths;
};
+ struct OutputMessage
+ {
+ enum class Type
+ {
+ Unknown,
+ Info,
+ Warning,
+ Error,
+ CountOf,
+ };
+ enum class Stage
+ {
+ Compile,
+ Link,
+ };
+
+ void reset()
+ {
+ type = Type::Unknown;
+ stage = Stage::Compile;
+ fileLine = 0;
+ }
+ static UnownedStringSlice getTypeText(OutputMessage::Type type);
+
+
+ Type type; ///< The type 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 Output
+ {
+ /// Reset to an initial empty state
+ void reset() { messages.clear(); result = SLANG_OK; }
+
+ /// Get the number of messages by type
+ Index getCountByType(OutputMessage::Type type) const;
+ /// True if there are any messages of the type
+ bool has(OutputMessage::Type type) const { return getCountByType(type) > 0; }
+
+ /// Stores in outCounts, the amount of messages for the stage of each type
+ Int countByStage(OutputMessage::Stage stage, Index outCounts[Int(OutputMessage::Type::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 messages of the type
+ void removeByType(OutputMessage::Type type);
+
+ SlangResult result;
+ List<OutputMessage> messages;
+ };
+
/// 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, ExecuteResult& outResult) = 0;
+ virtual SlangResult compile(const CompileOptions& options, Output& outOutput) = 0;
protected:
@@ -121,23 +179,27 @@ public:
typedef CPPCompiler Super;
typedef void(*CalcArgsFunc)(const CPPCompiler::CompileOptions& options, CommandLine& cmdLine);
+ typedef SlangResult(*ParseOutputFunc)(const ExecuteResult& exeResult, Output& output);
- virtual SlangResult compile(const CompileOptions& options, ExecuteResult& outResult) SLANG_OVERRIDE;
+ virtual SlangResult compile(const CompileOptions& options, Output& outOutput) SLANG_OVERRIDE;
- GenericCPPCompiler(const Desc& desc, const String& exeName, CalcArgsFunc func) :
+ GenericCPPCompiler(const Desc& desc, const String& exeName, CalcArgsFunc calcArgsFunc, ParseOutputFunc parseOutputFunc) :
Super(desc),
- m_func(func)
+ m_calcArgsFunc(calcArgsFunc),
+ m_parseOutputFunc(parseOutputFunc)
{
m_cmdLine.setExecutableFilename(exeName);
}
- GenericCPPCompiler(const Desc& desc, const CommandLine& cmdLine, CalcArgsFunc func) :
+ GenericCPPCompiler(const Desc& desc, const CommandLine& cmdLine, CalcArgsFunc calcArgsFunc, ParseOutputFunc parseOutputFunc) :
Super(desc),
m_cmdLine(cmdLine),
- m_func(func)
+ m_calcArgsFunc(calcArgsFunc),
+ m_parseOutputFunc(parseOutputFunc)
{}
- CalcArgsFunc m_func;
+ CalcArgsFunc m_calcArgsFunc;
+ ParseOutputFunc m_parseOutputFunc;
CommandLine m_cmdLine;
};
@@ -188,18 +250,6 @@ struct CPPCompilerUtil
Newest,
};
- /// Extracts version number into desc from text (assumes gcc/clang -v layout with a line with version)
- static SlangResult parseGCCFamilyVersion(const UnownedStringSlice& text, const UnownedStringSlice& prefix, CPPCompiler::Desc& outDesc);
-
- /// Runs the exeName, and extracts the version info into outDesc
- static SlangResult calcGCCFamilyVersion(const String& exeName, CPPCompiler::Desc& outDesc);
-
- /// Calculate gcc family compilers (including clang) cmdLine arguments from options
- static void calcGCCFamilyArgs(const CompileOptions& options, CommandLine& cmdLine);
-
- /// Calculate Visual Studio family compilers cmdLine arguments from options
- static void calcVisualStudioArgs(const CompileOptions& options, CommandLine& cmdLine);
-
/// Find a compiler
static CPPCompiler* findCompiler(const CPPCompilerSet* set, MatchType matchType, const CPPCompiler::Desc& desc);
static CPPCompiler* findCompiler(const List<CPPCompiler*>& compilers, MatchType matchType, const CPPCompiler::Desc& desc);
diff --git a/source/core/slang-gcc-compiler-util.cpp b/source/core/slang-gcc-compiler-util.cpp
new file mode 100644
index 000000000..9f22526a1
--- /dev/null
+++ b/source/core/slang-gcc-compiler-util.cpp
@@ -0,0 +1,460 @@
+// 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 GCCCompilerUtil::parseVersion(const UnownedStringSlice& text, const UnownedStringSlice& prefix, CPPCompiler::Desc& outDesc)
+{
+ List<UnownedStringSlice> lines;
+ StringUtil::calcLines(text, lines);
+
+ for (auto line : lines)
+ {
+ if (line.startsWith(prefix))
+ {
+ const UnownedStringSlice remainingSlice = UnownedStringSlice(line.begin() + prefix.size(), 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 GCCCompilerUtil::calcVersion(const String& exeName, CPPCompiler::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 CPPCompiler::CompilerType types[] =
+ {
+ CPPCompiler::CompilerType::Clang,
+ CPPCompiler::CompilerType::GCC,
+ CPPCompiler::CompilerType::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 _parseErrorType(const UnownedStringSlice& in, CPPCompiler::OutputMessage::Type& outType)
+{
+ typedef CPPCompiler::OutputMessage::Type Type;
+
+ if (in == "error" || in == "fatal error")
+ {
+ outType = Type::Error;
+ }
+ else if (in == "warning")
+ {
+ outType = Type::Warning;
+ }
+ else if (in == "info" || in == "note")
+ {
+ outType = Type::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, CPPCompiler::OutputMessage& outMsg)
+{
+ typedef CPPCompiler::OutputMessage OutputMessage;
+ typedef OutputMessage::Type Type;
+
+ // 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)*/
+
+ outMsg.stage = OutputMessage::Stage::Compile;
+
+ List<UnownedStringSlice> split;
+ StringUtil::split(line, ':', split);
+
+ if (split.getCount() == 2)
+ {
+ const auto split0 = split[0].trim();
+ if (split0 == UnownedStringSlice::fromLiteral("ld"))
+ {
+ // We'll ignore for now
+ outMsg.stage = OutputMessage::Stage::Link;
+ outMsg.type = Type::Info;
+ outMsg.text = split[1].trim();
+ outLineParseResult = LineParseResult::Start;
+ 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
+ if (split0.startsWith(UnownedStringSlice::fromLiteral("clang")))
+ {
+ // Extract the type
+ SLANG_RETURN_ON_FAIL(_parseErrorType(split[1].trim(), outMsg.type));
+
+ if (text.startsWith("linker command failed"))
+ {
+ outMsg.stage = OutputMessage::Stage::Link;
+ }
+
+ outMsg.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
+ outMsg.filePath = split[0];
+ outMsg.type = Type::Error;
+ outMsg.stage = OutputMessage::Stage::Link;
+ outMsg.text = text;
+ outLineParseResult = LineParseResult::Single;
+ return SLANG_OK;
+ }
+ else if (text.startsWith("ld returned"))
+ {
+ outMsg.stage = CPPCompiler::OutputMessage::Stage::Link;
+ SLANG_RETURN_ON_FAIL(_parseErrorType(split[1].trim(), outMsg.type));
+ outMsg.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::getFileExt(split[0]);
+
+ // Maybe a bit fragile -> but probably okay for now
+ if (ext != "o" && ext != "obj")
+ {
+ outLineParseResult = LineParseResult::Ignore;
+ return SLANG_OK;
+ }
+ else
+ {
+ outMsg.filePath = split[1];
+ outMsg.fileLine = 0;
+ outMsg.type = OutputMessage::Type::Error;
+ outMsg.stage = OutputMessage::Stage::Link;
+ outMsg.text = split[3];
+
+ outLineParseResult = LineParseResult::Start;
+ return SLANG_OK;
+ }
+ }
+ else if (split.getCount() == 5)
+ {
+ // Probably a regular error line
+ SLANG_RETURN_ON_FAIL(_parseErrorType(split[3].trim(), outMsg.type));
+
+ outMsg.filePath = split[0];
+ SLANG_RETURN_ON_FAIL(StringUtil::parseInt(split[1], outMsg.fileLine));
+ outMsg.text = split[4];
+
+ outLineParseResult = LineParseResult::Start;
+ return SLANG_OK;
+ }
+
+ // Assume it's a continuation
+ outLineParseResult = LineParseResult::Continuation;
+ return SLANG_OK;
+}
+
+/* static */SlangResult GCCCompilerUtil::parseOutput(const ExecuteResult& exeRes, CPPCompiler::Output& outOutput)
+{
+ LineParseResult prevLineResult = LineParseResult::Ignore;
+
+ outOutput.reset();
+
+ for (auto line : LineParser(exeRes.standardError.getUnownedSlice()))
+ {
+ CPPCompiler::OutputMessage msg;
+ LineParseResult lineRes;
+
+ SLANG_RETURN_ON_FAIL(_parseGCCFamilyLine(line, lineRes, msg));
+
+ switch (lineRes)
+ {
+ case LineParseResult::Start:
+ {
+ // It's start of a new message
+ outOutput.messages.add(msg);
+ prevLineResult = LineParseResult::Start;
+ break;
+ }
+ case LineParseResult::Single:
+ {
+ // It's a single message, without anything following
+ outOutput.messages.add(msg);
+ prevLineResult = LineParseResult::Ignore;
+ break;
+ }
+ case LineParseResult::Continuation:
+ {
+ if (prevLineResult == LineParseResult::Start || prevLineResult == LineParseResult::Continuation)
+ {
+ if (outOutput.messages.getCount() > 0)
+ {
+ // We are now in a continuation, add to the last
+ auto& text = outOutput.messages.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(CPPCompiler::OutputMessage::Type::Error) || exeRes.resultCode != 0)
+ {
+ outOutput.result = SLANG_FAIL;
+ }
+
+ return SLANG_OK;
+}
+
+/* static */void GCCCompilerUtil::calcArgs(const CompileOptions& options, CommandLine& cmdLine)
+{
+ cmdLine.addArg("-fvisibility=hidden");
+ // Use shared libraries
+ //cmdLine.addArg("-shared");
+
+ switch (options.optimizationLevel)
+ {
+ case OptimizationLevel::Debug:
+ {
+ // No optimization
+ cmdLine.addArg("-O0");
+ break;
+ }
+ case OptimizationLevel::Normal:
+ {
+ cmdLine.addArg("-Os");
+ break;
+ }
+ default: break;
+ }
+
+ if (options.debugInfoType != DebugInfoType::None)
+ {
+ cmdLine.addArg("-g");
+ }
+
+ switch (options.targetType)
+ {
+ case TargetType::SharedLibrary:
+ {
+ // Shared library
+ cmdLine.addArg("-shared");
+ // Position independent
+ cmdLine.addArg("-fPIC");
+
+ String sharedLibraryPath = SharedLibrary::calcPlatformPath(options.modulePath.getUnownedSlice());
+
+ cmdLine.addArg("-o");
+ cmdLine.addArg(sharedLibraryPath);
+ break;
+ }
+ case TargetType::Executable:
+ {
+ cmdLine.addArg("-o");
+
+ StringBuilder builder;
+ builder << options.modulePath;
+ builder << ProcessUtil::getExecutableSuffix();
+
+ cmdLine.addArg(options.modulePath);
+ 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 << 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 !SLANG_APPLE_FAMILY
+ // 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");
+#endif
+ }
+
+ // 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.sourceType == SourceType::CPP)
+ {
+ // Make STD libs available
+ cmdLine.addArg("-lstdc++");
+ }
+}
+
+}
diff --git a/source/core/slang-gcc-compiler-util.h b/source/core/slang-gcc-compiler-util.h
new file mode 100644
index 000000000..29070fc61
--- /dev/null
+++ b/source/core/slang-gcc-compiler-util.h
@@ -0,0 +1,33 @@
+#ifndef SLANG_GCC_COMPILER_UTIL_H
+#define SLANG_GCC_COMPILER_UTIL_H
+
+#include "slang-cpp-compiler.h"
+
+namespace Slang
+{
+
+/* Utility for processing input and output of gcc-like compilers, including clang */
+struct GCCCompilerUtil
+{
+ typedef CPPCompiler::CompileOptions CompileOptions;
+ typedef CPPCompiler::OptimizationLevel OptimizationLevel;
+ typedef CPPCompiler::TargetType TargetType;
+ typedef CPPCompiler::DebugInfoType DebugInfoType;
+ typedef CPPCompiler::SourceType SourceType;
+
+ /// 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, CPPCompiler::Desc& outDesc);
+
+ /// Runs the exeName, and extracts the version info into outDesc
+ static SlangResult calcVersion(const String& exeName, CPPCompiler::Desc& outDesc);
+
+ /// Calculate gcc family compilers (including clang) cmdLine arguments from options
+ static void calcArgs(const CompileOptions& options, CommandLine& cmdLine);
+
+ /// Parse ExecuteResult into Output
+ static SlangResult parseOutput(const ExecuteResult& exeRes, CPPCompiler::Output& outOutput);
+};
+
+}
+
+#endif
diff --git a/source/core/slang-string-util.cpp b/source/core/slang-string-util.cpp
index 95caf8319..4295a2c52 100644
--- a/source/core/slang-string-util.cpp
+++ b/source/core/slang-string-util.cpp
@@ -39,6 +39,45 @@ static const Guid IID_ISlangBlob = SLANG_UUID_ISlangBlob;
}
}
+/* static */void StringUtil::join(const List<String>& values, char separator, StringBuilder& out)
+{
+ join(values, UnownedStringSlice(&separator, 1), out);
+}
+
+/* static */void StringUtil::join(const List<String>& values, const UnownedStringSlice& separator, StringBuilder& out)
+{
+ const Index count = values.getCount();
+ if (count <= 0)
+ {
+ return;
+ }
+ out.append(values[0]);
+ for (Index i = 1; i < count; i++)
+ {
+ out.append(separator);
+ out.append(values[i]);
+ }
+}
+
+/* static */void StringUtil::join(const UnownedStringSlice* values, Index valueCount, char separator, StringBuilder& out)
+{
+ join(values, valueCount, UnownedStringSlice(&separator, 1), out);
+}
+
+/* static */void StringUtil::join(const UnownedStringSlice* values, Index valueCount, const UnownedStringSlice& separator, StringBuilder& out)
+{
+ if (valueCount <= 0)
+ {
+ return;
+ }
+ out.append(values[0]);
+ for (Index i = 1; i < valueCount; i++)
+ {
+ out.append(separator);
+ out.append(values[i]);
+ }
+}
+
/* static */int StringUtil::indexOfInSplit(const UnownedStringSlice& in, char splitChar, const UnownedStringSlice& find)
{
const char* start = in.begin();
@@ -285,4 +324,45 @@ ComPtr<ISlangBlob> StringUtil::createStringBlob(const String& string)
}
}
+SLANG_FORCE_INLINE static bool _isDigit(char c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+/* static */SlangResult StringUtil::parseInt(const UnownedStringSlice& in, Int& outValue)
+{
+ const char* cur = in.begin();
+ const char* end = in.end();
+
+ bool negate = false;
+ if (cur < end && *cur == '-')
+ {
+ negate = true;
+ cur++;
+ }
+
+ // We need at least one digit
+ if (cur >= end || !_isDigit(*cur))
+ {
+ return SLANG_FAIL;
+ }
+
+ Int value = *cur++ - '0';
+ // Do the remaining digits
+ for (; cur < end; ++cur)
+ {
+ const char c = *cur;
+ if (!_isDigit(c))
+ {
+ return SLANG_FAIL;
+ }
+ value = value * 10 + (c - '0');
+ }
+
+ value = negate ? -value : value;
+
+ outValue = value;
+ return SLANG_OK;
+}
+
} // namespace Slang
diff --git a/source/core/slang-string-util.h b/source/core/slang-string-util.h
index 74ec75dc6..77b7ca2a3 100644
--- a/source/core/slang-string-util.h
+++ b/source/core/slang-string-util.h
@@ -41,6 +41,13 @@ struct StringUtil
/// Slices contents will directly address into in, so contents will only stay valid as long as in does.
static void split(const UnownedStringSlice& in, char splitChar, List<UnownedStringSlice>& slicesOut);
+ /// Append the joining of in items, separated by 'separator' onto out
+ static void join(const List<String>& in, char separator, StringBuilder& out);
+ static void join(const List<String>& in, const UnownedStringSlice& separator, StringBuilder& out);
+
+ static void join(const UnownedStringSlice* values, Index valueCount, char separator, StringBuilder& out);
+ static void join(const UnownedStringSlice* values, Index valueCount, const UnownedStringSlice& separator, StringBuilder& out);
+
/// Equivalent to doing a split and then finding the index of 'find' on the array
/// Returns -1 if not found
static int indexOfInSplit(const UnownedStringSlice& in, char splitChar, const UnownedStringSlice& find);
@@ -94,6 +101,9 @@ struct StringUtil
/// Equal if the lines are equal (in effect a way to ignore differences in line breaks)
static bool areLinesEqual(const UnownedStringSlice& a, const UnownedStringSlice& b);
+
+ /// Convert in to int. Returns SLANG_FAIL on error
+ static SlangResult parseInt(const UnownedStringSlice& in, Int& outValue);
};
/* A helper class that allows parsing of lines from text with iteration. Uses StringUtil::extractLine for the actual underlying implementation. */
diff --git a/source/core/slang-string.cpp b/source/core/slang-string.cpp
index 54703def1..8b2a35b3f 100644
--- a/source/core/slang-string.cpp
+++ b/source/core/slang-string.cpp
@@ -7,9 +7,15 @@ namespace Slang
SLANG_RETURN_NEVER void signalUnexpectedError(char const* message)
{
+ // Can be useful to uncomment during debug when problem is on CI
+ // printf("Unexpected: %s\n", message);
throw InternalError(message);
}
+ SLANG_FORCE_INLINE static bool _isWhiteSpace(char c)
+ {
+ return c == ' ' || c == '\t';
+ }
// OSString
@@ -100,6 +106,17 @@ namespace Slang
return endsWith(UnownedTerminatedStringSlice(str));
}
+
+ UnownedStringSlice UnownedStringSlice::trim() const
+ {
+ const char* start = m_begin;
+ const char* end = m_end;
+
+ while (start < end && _isWhiteSpace(*start)) start++;
+ while (end > start && _isWhiteSpace(end[-1])) end--;
+ return UnownedStringSlice(start, end);
+ }
+
// StringSlice
diff --git a/source/core/slang-string.h b/source/core/slang-string.h
index 560d137db..cb2731cee 100644
--- a/source/core/slang-string.h
+++ b/source/core/slang-string.h
@@ -98,10 +98,22 @@ namespace Slang
return Index(m_end - m_begin);
}
- int indexOf(char c) const
+ Index indexOf(char c) const
{
- const int size = int(m_end - m_begin);
- for (int i = 0; i < size; ++i)
+ const Index size = int(m_end - m_begin);
+ for (Index i = 0; i < size; ++i)
+ {
+ if (m_begin[i] == c)
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+ Index lastIndexOf(char c) const
+ {
+ const Index size = Index(m_end - m_begin);
+ for (Index i = size - 1; i >= 0; --i)
{
if (m_begin[i] == c)
{
@@ -156,6 +168,9 @@ namespace Slang
bool endsWith(UnownedStringSlice const& other) const;
bool endsWith(char const* str) const;
+
+ UnownedStringSlice trim() const;
+
int GetHashCode() const
{
return Slang::GetHashCode(m_begin, size_t(m_end - m_begin));
diff --git a/source/core/slang-visual-studio-compiler-util.cpp b/source/core/slang-visual-studio-compiler-util.cpp
new file mode 100644
index 000000000..20d655e78
--- /dev/null
+++ b/source/core/slang-visual-studio-compiler-util.cpp
@@ -0,0 +1,300 @@
+// 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"
+
+#include "slang-io.h"
+
+namespace Slang
+{
+
+/* static */void VisualStudioCompilerUtil::calcArgs(const CompileOptions& options, CommandLine& cmdLine)
+{
+ // 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.sourceType == SourceType::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");
+ }
+ }
+
+ switch (options.optimizationLevel)
+ {
+ case OptimizationLevel::Debug:
+ {
+ // No optimization
+ cmdLine.addArg("/Od");
+
+ cmdLine.addArg("/MDd");
+ break;
+ }
+ case OptimizationLevel::Normal:
+ {
+ cmdLine.addArg("/O2");
+ // Multithreaded DLL
+ cmdLine.addArg("/MD");
+ break;
+ }
+ default: break;
+ }
+
+ // /Fd - followed by name of the pdb file
+ if (options.debugInfoType != DebugInfoType::None)
+ {
+ cmdLine.addPrefixPathArg("/Fd", options.modulePath, ".pdb");
+ }
+
+ switch (options.targetType)
+ {
+ case TargetType::SharedLibrary:
+ {
+ // Create dynamic link library
+ if (options.optimizationLevel == OptimizationLevel::Debug)
+ {
+ 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 << 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);
+ }
+}
+
+static SlangResult _parseErrorType(const UnownedStringSlice& in, CPPCompiler::OutputMessage::Type& outType)
+{
+ typedef CPPCompiler::OutputMessage::Type Type;
+
+ if (in == "error" || in == "fatal error")
+ {
+ outType = Type::Error;
+ }
+ else if (in == "warning")
+ {
+ outType = Type::Warning;
+ }
+ else if (in == "info")
+ {
+ outType = Type::Info;
+ }
+ else
+ {
+ return SLANG_FAIL;
+ }
+ return SLANG_OK;
+}
+
+static SlangResult _parseVisualStudioLine(const UnownedStringSlice& line, CPPCompiler::OutputMessage& outMsg)
+{
+ typedef CPPCompiler::OutputMessage OutputMessage;
+
+ UnownedStringSlice linkPrefix = UnownedStringSlice::fromLiteral("LINK :");
+ if (line.startsWith(linkPrefix))
+ {
+ outMsg.stage = OutputMessage::Stage::Link;
+ outMsg.type = OutputMessage::Type::Info;
+
+ outMsg.text = UnownedStringSlice(line.begin() + linkPrefix.size(), line.end());
+
+ return SLANG_OK;
+ }
+
+ outMsg.stage = OutputMessage::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;
+ }
+
+ outMsg.filePath = UnownedStringSlice(start, lineNoStart);
+ outMsg.fileLine = lineNo;
+ }
+ else
+ {
+ outMsg.filePath = UnownedStringSlice(start, cur + colonIndex);
+ outMsg.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
+ outMsg.code = UnownedStringSlice(errorSection.begin() + errorCodeIndex + 1, errorSection.end());
+ if (outMsg.code.startsWith(UnownedStringSlice::fromLiteral("LNK")))
+ {
+ outMsg.stage = OutputMessage::Stage::Link;
+ }
+
+ // Extract the bit before the code
+ SLANG_RETURN_ON_FAIL(_parseErrorType(UnownedStringSlice(errorSection.begin(), errorSection.begin() + errorCodeIndex).trim(), outMsg.type));
+
+ // Link codes start with LNK prefix
+ postError = UnownedStringSlice(postPath.begin() + errorColonIndex + 1, end);
+ }
+
+ outMsg.text = postError;
+
+ return SLANG_OK;
+}
+
+/* static */SlangResult VisualStudioCompilerUtil::parseOutput(const ExecuteResult& exeRes, CPPCompiler::Output& outOutput)
+{
+ outOutput.reset();
+
+ for (auto line : LineParser(exeRes.standardOutput.getUnownedSlice()))
+ {
+#if 0
+ fwrite(line.begin(), 1, line.size(), stdout);
+ fprintf(stdout, "\n");
+#endif
+
+ OutputMessage msg;
+ if (SLANG_SUCCEEDED(_parseVisualStudioLine(line, msg)))
+ {
+ outOutput.messages.add(msg);
+ }
+ }
+
+ // if it has a compilation error.. set on output
+ if (outOutput.has(OutputMessage::Type::Error))
+ {
+ outOutput.result = SLANG_FAIL;
+ }
+
+ return SLANG_OK;
+}
+
+}
diff --git a/source/core/slang-visual-studio-compiler-util.h b/source/core/slang-visual-studio-compiler-util.h
new file mode 100644
index 000000000..86845b5af
--- /dev/null
+++ b/source/core/slang-visual-studio-compiler-util.h
@@ -0,0 +1,26 @@
+#ifndef SLANG_VISUAL_STUDIO_COMPILER_UTIL_H
+#define SLANG_VISUAL_STUDIO_COMPILER_UTIL_H
+
+#include "slang-cpp-compiler.h"
+
+namespace Slang
+{
+
+struct VisualStudioCompilerUtil
+{
+ typedef CPPCompiler::CompileOptions CompileOptions;
+ typedef CPPCompiler::OptimizationLevel OptimizationLevel;
+ typedef CPPCompiler::TargetType TargetType;
+ typedef CPPCompiler::DebugInfoType DebugInfoType;
+ typedef CPPCompiler::SourceType SourceType;
+ typedef CPPCompiler::OutputMessage OutputMessage;
+
+ /// Calculate Visual Studio family compilers cmdLine arguments from options
+ static void calcArgs(const CompileOptions& options, CommandLine& cmdLine);
+ /// Parse Visual Studio exeRes into CPPCompiler::Output
+ static SlangResult parseOutput(const ExecuteResult& exeRes, CPPCompiler::Output& outOutput);
+};
+
+}
+
+#endif
diff --git a/source/core/windows/slang-win-visual-studio-util.cpp b/source/core/windows/slang-win-visual-studio-util.cpp
index 81d77c710..7e683ee55 100644
--- a/source/core/windows/slang-win-visual-studio-util.cpp
+++ b/source/core/windows/slang-win-visual-studio-util.cpp
@@ -4,6 +4,8 @@
#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
@@ -291,7 +293,7 @@ static SlangResult _find(int versionIndex, WinVisualStudioUtil::VersionPath& out
CommandLine cmdLine;
calcExecuteCompilerArgs(versionPath, cmdLine);
- RefPtr<GenericCPPCompiler> compiler = new GenericCPPCompiler(desc, cmdLine, &CPPCompilerUtil::calcVisualStudioArgs);
+ RefPtr<GenericCPPCompiler> compiler = new GenericCPPCompiler(desc, cmdLine, &VisualStudioCompilerUtil::calcArgs, &VisualStudioCompilerUtil::parseOutput);
set->addCompiler(compiler);
}
}
diff --git a/source/core/windows/slang-win-visual-studio-util.h b/source/core/windows/slang-win-visual-studio-util.h
index 76e1cc710..34be8473d 100644
--- a/source/core/windows/slang-win-visual-studio-util.h
+++ b/source/core/windows/slang-win-visual-studio-util.h
@@ -51,14 +51,11 @@ struct WinVisualStudioUtil
/// Convert a version number into a string
static void append(Version version, StringBuilder& outBuilder);
- /// Calculate the command line args
- static void calcArgs(const CPPCompiler::CompileOptions& options, CommandLine& cmdLine);
-
/// Get version as desc
static CPPCompiler::Desc getDesc(Version version)
{
CPPCompiler::Desc desc;
- desc.type = CPPCompiler::Type::VisualStudio;
+ desc.type = CPPCompiler::CompilerType::VisualStudio;
desc.majorVersion = Int(version) / 10;
desc.minorVersion = Int(version) % 10;
return desc;