diff --git a/include/Nazara/Shader/ShaderLangErrors.hpp b/include/Nazara/Shader/ShaderLangErrors.hpp index 6020534ee..4ff6e7306 100644 --- a/include/Nazara/Shader/ShaderLangErrors.hpp +++ b/include/Nazara/Shader/ShaderLangErrors.hpp @@ -36,6 +36,9 @@ namespace Nz::ShaderLang #include }; + NAZARA_SHADER_API std::string_view ToString(ErrorCategory errorCategory); + NAZARA_SHADER_API std::string_view ToString(ErrorType errorType); + class NAZARA_SHADER_API Error : public std::exception { public: @@ -47,6 +50,7 @@ namespace Nz::ShaderLang inline ErrorCategory GetErrorCategory() const; const std::string& GetErrorMessage() const; inline ErrorType GetErrorType() const; + const std::string& GetFullErrorMessage() const; inline const SourceLocation& GetSourceLocation() const; const char* what() const noexcept override; @@ -58,6 +62,7 @@ namespace Nz::ShaderLang virtual std::string BuildErrorMessage() const = 0; private: + mutable std::string m_fullErrorMessage; mutable std::string m_errorMessage; ErrorCategory m_errorCategory; SourceLocation m_sourceLocation; diff --git a/src/Nazara/Shader/ShaderLangErrors.cpp b/src/Nazara/Shader/ShaderLangErrors.cpp index 905755bf9..d4cd756f9 100644 --- a/src/Nazara/Shader/ShaderLangErrors.cpp +++ b/src/Nazara/Shader/ShaderLangErrors.cpp @@ -44,17 +44,7 @@ struct fmt::formatter : formatter template auto format(const Nz::ShaderLang::ErrorCategory& p, FormatContext& ctx) -> decltype(ctx.out()) { - // TODO: Add ToString - std::string_view name = ""; - switch (p) - { - case Nz::ShaderLang::ErrorCategory::Ast: name = "Ast"; break; - case Nz::ShaderLang::ErrorCategory::Compilation: name = "Compilation"; break; - case Nz::ShaderLang::ErrorCategory::Lexing: name = "Lexing"; break; - case Nz::ShaderLang::ErrorCategory::Parsing: name = "Parsing"; break; - } - - return formatter::format(name, ctx); + return formatter::format(ToString(p), ctx); } }; @@ -64,16 +54,7 @@ struct fmt::formatter : formatter template auto format(const Nz::ShaderLang::ErrorType& p, FormatContext& ctx) -> decltype(ctx.out()) { - // TODO: Add ToString - std::string_view name = ""; - switch (p) - { -#define NAZARA_SHADERLANG_ERROR(ErrorPrefix, ErrorName, ...) case Nz::ShaderLang::ErrorType:: ErrorPrefix ## ErrorName: name = #ErrorPrefix #ErrorName; break; - -#include - } - - return formatter::format(name, ctx); + return formatter::format(ToString(p), ctx); } }; @@ -107,9 +88,42 @@ struct fmt::formatter : formatter namespace Nz::ShaderLang { + std::string_view ToString(ErrorCategory errorCategory) + { + switch (errorCategory) + { + case ErrorCategory::Ast: return "Ast"; + case ErrorCategory::Compilation: return "Compilation"; + case ErrorCategory::Lexing: return "Lexing"; + case ErrorCategory::Parsing: return "Parsing"; + } + + return ""; + } + + std::string_view ToString(ErrorType errorType) + { + switch (errorType) + { +#define NAZARA_SHADERLANG_ERROR(ErrorPrefix, ErrorName, ...) case ErrorType:: ErrorPrefix ## ErrorName: return #ErrorPrefix #ErrorName; + +#include + } + + return ""; + } + const std::string& Error::GetErrorMessage() const { if (m_errorMessage.empty()) + m_errorMessage = BuildErrorMessage(); + + return m_errorMessage; + } + + const std::string& Error::GetFullErrorMessage() const + { + if (m_fullErrorMessage.empty()) { if (m_sourceLocation.IsValid()) { @@ -118,22 +132,22 @@ namespace Nz::ShaderLang sourceFile = *m_sourceLocation.file; if (m_sourceLocation.startLine != m_sourceLocation.endLine) - m_errorMessage = fmt::format("{}({} -> {},{} -> {}): {} error: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.endLine, m_sourceLocation.startColumn, m_sourceLocation.endColumn, m_errorType, BuildErrorMessage()); + m_fullErrorMessage = fmt::format("{}({} -> {},{} -> {}): {} error: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.endLine, m_sourceLocation.startColumn, m_sourceLocation.endColumn, m_errorType, GetErrorMessage()); else if (m_sourceLocation.startColumn != m_sourceLocation.endColumn) - m_errorMessage = fmt::format("{}({},{} -> {}): {} error: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.startColumn, m_sourceLocation.endColumn, m_errorType, BuildErrorMessage()); + m_fullErrorMessage = fmt::format("{}({},{} -> {}): {} error: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.startColumn, m_sourceLocation.endColumn, m_errorType, GetErrorMessage()); else - m_errorMessage = fmt::format("{}({}, {}): {} error: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.startColumn, m_errorType, BuildErrorMessage()); + m_fullErrorMessage = fmt::format("{}({}, {}): {} error: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.startColumn, m_errorType, GetErrorMessage()); } else - m_errorMessage = fmt::format("?: {} error: {}", m_errorType, BuildErrorMessage()); + m_fullErrorMessage = fmt::format("?: {} error: {}", m_errorType, GetErrorMessage()); } - return m_errorMessage; + return m_fullErrorMessage; } const char* Error::what() const noexcept { - return GetErrorMessage().c_str(); + return GetFullErrorMessage().c_str(); } #define NAZARA_SHADERLANG_NEWERRORTYPE(Prefix, ErrorType, ErrorName, ErrorString, ...) \ diff --git a/src/ShaderCompiler/main.cpp b/src/ShaderCompiler/main.cpp index 1ad81d835..5b3693041 100644 --- a/src/ShaderCompiler/main.cpp +++ b/src/ShaderCompiler/main.cpp @@ -13,6 +13,12 @@ #include #include +enum class LogFormat +{ + Classic, + VisualStudio +}; + std::vector ReadFileContent(const std::filesystem::path& filePath) { Nz::File file(filePath); @@ -53,6 +59,7 @@ int main(int argc, char* argv[]) ("c,compile", "Compile input shader") ("output-nzsl", "Output shader as NZSL to stdout") ("header-file", "Generate an includable header file") + ("log-format", "Set log format (classic, vs)", cxxopts::value()) ("i,input", "Input file(s)", cxxopts::value()) ("o,output", "Output path", cxxopts::value()->default_value("."), "path") ("p,partial", "Allow partial compilation") @@ -74,14 +81,27 @@ int main(int argc, char* argv[]) if (result.count("input") == 0) { - fmt::print("no input file\n{}\n", options.help()); + fmt::print(stderr, "no input file\n{}\n", options.help()); return EXIT_SUCCESS; } + LogFormat logFormat = LogFormat::Classic; + if (result.count("log-format") != 0) + { + const std::string& formatStr = result["log-format"].as(); + if (formatStr == "vs") + logFormat = LogFormat::VisualStudio; + else if (formatStr != "classic") + { + fmt::print(stderr, "{} is not a file\n", formatStr); + return EXIT_FAILURE; + } + } + std::filesystem::path inputPath = result["input"].as(); if (!std::filesystem::is_regular_file(inputPath)) { - fmt::print("{} is not a file\n", inputPath.generic_u8string()); + fmt::print(stderr, "{} is not a file\n", inputPath.generic_u8string()); return EXIT_FAILURE; } @@ -153,62 +173,76 @@ int main(int argc, char* argv[]) } catch (const Nz::ShaderLang::Error& error) { - fmt::print(stderr, (fmt::emphasis::bold | fg(fmt::color::red)), "{}\n", error.what()); - - Nz::ShaderLang::SourceLocation errorLocation = error.GetSourceLocation(); + const Nz::ShaderLang::SourceLocation& errorLocation = error.GetSourceLocation(); if (errorLocation.IsValid()) { - try + if (logFormat == LogFormat::Classic) { - // Retrieve line - std::string sourceContent = ReadSourceFileContent(*errorLocation.file); + fmt::print(stderr, (fmt::emphasis::bold | fg(fmt::color::red)), "{}\n", error.what()); - std::size_t lineStartOffset = 0; - if (errorLocation.startLine > 1) + try { - lineStartOffset = sourceContent.find('\n') + 1; - for (std::size_t i = 0; i < errorLocation.startLine - 2; ++i) //< remember startLine is 1-based + // Retrieve line + std::string sourceContent = ReadSourceFileContent(*errorLocation.file); + + std::size_t lineStartOffset = 0; + if (errorLocation.startLine > 1) { - lineStartOffset = sourceContent.find('\n', lineStartOffset); - if (lineStartOffset == std::string::npos) - throw std::runtime_error("file content doesn't match original source"); + lineStartOffset = sourceContent.find('\n') + 1; + for (std::size_t i = 0; i < errorLocation.startLine - 2; ++i) //< remember startLine is 1-based + { + lineStartOffset = sourceContent.find('\n', lineStartOffset); + if (lineStartOffset == std::string::npos) + throw std::runtime_error("file content doesn't match original source"); - ++lineStartOffset; + ++lineStartOffset; + } } + std::size_t lineEndOffset = sourceContent.find('\n', lineStartOffset); + + std::string errorLine = sourceContent.substr(lineStartOffset, lineEndOffset - lineStartOffset); + + // handle tabs + Nz::UInt32 startColumn = errorLocation.startColumn - 1; + std::size_t startPos = 0; + while ((startPos = errorLine.find('\t', startPos)) != std::string::npos) + { + if (startPos < startColumn) + startColumn += 3; + + errorLine.replace(startPos, 1, " "); + startPos += 4; + } + + std::size_t columnSize; + if (errorLocation.startLine == errorLocation.endLine) + columnSize = errorLocation.endColumn - errorLocation.startColumn + 1; + else + columnSize = 1; + + std::string lineStr = std::to_string(errorLocation.startLine); + + fmt::print(stderr, " {} | {}\n", lineStr, errorLine); + fmt::print(stderr, " {} | {}", std::string(lineStr.size(), ' '), std::string(startColumn, ' ')); + fmt::print(stderr, fg(fmt::color::green), "{}\n", std::string(columnSize, '^')); } - std::size_t lineEndOffset = sourceContent.find('\n', lineStartOffset); - - std::string errorLine = sourceContent.substr(lineStartOffset, lineEndOffset - lineStartOffset); - - // handle tabs - Nz::UInt32 startColumn = errorLocation.startColumn - 1; - std::size_t startPos = 0; - while ((startPos = errorLine.find("\t", startPos)) != std::string::npos) + catch (const std::exception& e) { - if (startPos < startColumn) - startColumn += 3; - - errorLine.replace(startPos, 1, " "); - startPos += 4; + fmt::print(stderr, "failed to print error line: {}\n", e.what()); } - - std::size_t columnSize; - if (errorLocation.startLine == errorLocation.endLine) - columnSize = errorLocation.endColumn - errorLocation.startColumn + 1; - else - columnSize = 1; - - std::string lineStr = std::to_string(errorLocation.startLine); - - fmt::print(stderr, " {} | {}\n", lineStr, errorLine); - fmt::print(stderr, " {} | {}", std::string(lineStr.size(), ' '), std::string(startColumn, ' ')); - fmt::print(stderr, fg(fmt::color::green), "{}\n", std::string(columnSize, '^')); } - catch (const std::exception& e) + else if (logFormat == LogFormat::VisualStudio) { - fmt::print(stderr, "failed to print error line: {}\n", e.what()); + // VS requires absolute path + std::filesystem::path fullPath; + if (errorLocation.file) + fullPath = std::filesystem::absolute(*errorLocation.file); + + fmt::print(stderr, "{}({},{}): error {}: {}\n", fullPath.generic_u8string(), errorLocation.startLine, errorLocation.startColumn, ToString(error.GetErrorType()), error.GetErrorMessage()); } } + else + fmt::print(stderr, (fmt::emphasis::bold | fg(fmt::color::red)), "{}\n", error.what()); } } catch (const cxxopts::OptionException& e) diff --git a/xmake/rules/compile_shaders.lua b/xmake/rules/compile_shaders.lua index a255a3718..85588ad1f 100644 --- a/xmake/rules/compile_shaders.lua +++ b/xmake/rules/compile_shaders.lua @@ -1,4 +1,4 @@ --- Turns resources into includables headers +-- Compile shaders to includables headers rule("compile_shaders") on_load(function (target) target:add("deps", "NazaraShaderCompiler") @@ -22,7 +22,7 @@ rule("compile_shaders") -- add commands batchcmds:show_progress(opt.progress, "${color.build.object}compiling shader %s", shaderfile) - local argv = {"--compile", "--partial", "--header-file", shaderfile} + local argv = {"--compile", "--partial", "--header-file", "--log-format=vs", shaderfile} batchcmds:vrunv(nzslc:targetfile(), argv, { curdir = "." }) local outputFile = path.join(path.directory(shaderfile), path.basename(shaderfile) .. ".nzslb.h")