diff options
Diffstat (limited to 'source/core/slang-visual-studio-compiler-util.cpp')
| -rw-r--r-- | source/core/slang-visual-studio-compiler-util.cpp | 300 |
1 files changed, 300 insertions, 0 deletions
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; +} + +} |
