ShaderCompiler: Add --log-format option

This commit is contained in:
SirLynix 2022-05-05 20:18:34 +02:00
parent 106d629342
commit e62969999a
4 changed files with 125 additions and 72 deletions

View File

@ -36,6 +36,9 @@ namespace Nz::ShaderLang
#include <Nazara/Shader/ShaderLangErrorList.hpp>
};
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;

View File

@ -44,17 +44,7 @@ struct fmt::formatter<Nz::ShaderLang::ErrorCategory> : formatter<string_view>
template <typename FormatContext>
auto format(const Nz::ShaderLang::ErrorCategory& p, FormatContext& ctx) -> decltype(ctx.out())
{
// TODO: Add ToString
std::string_view name = "<unhandled error category>";
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<string_view>::format(name, ctx);
return formatter<string_view>::format(ToString(p), ctx);
}
};
@ -64,16 +54,7 @@ struct fmt::formatter<Nz::ShaderLang::ErrorType> : formatter<string_view>
template <typename FormatContext>
auto format(const Nz::ShaderLang::ErrorType& p, FormatContext& ctx) -> decltype(ctx.out())
{
// TODO: Add ToString
std::string_view name = "<unhandled error type>";
switch (p)
{
#define NAZARA_SHADERLANG_ERROR(ErrorPrefix, ErrorName, ...) case Nz::ShaderLang::ErrorType:: ErrorPrefix ## ErrorName: name = #ErrorPrefix #ErrorName; break;
#include <Nazara/Shader/ShaderLangErrorList.hpp>
}
return formatter<string_view>::format(name, ctx);
return formatter<string_view>::format(ToString(p), ctx);
}
};
@ -107,9 +88,42 @@ struct fmt::formatter<Nz::ShaderLang::TokenType> : formatter<string_view>
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 "<unhandled error category>";
}
std::string_view ToString(ErrorType errorType)
{
switch (errorType)
{
#define NAZARA_SHADERLANG_ERROR(ErrorPrefix, ErrorName, ...) case ErrorType:: ErrorPrefix ## ErrorName: return #ErrorPrefix #ErrorName;
#include <Nazara/Shader/ShaderLangErrorList.hpp>
}
return "<unhandled error type>";
}
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, ...) \

View File

@ -13,6 +13,12 @@
#include <sstream>
#include <stdexcept>
enum class LogFormat
{
Classic,
VisualStudio
};
std::vector<Nz::UInt8> 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<std::string>())
("i,input", "Input file(s)", cxxopts::value<std::string>())
("o,output", "Output path", cxxopts::value<std::string>()->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<std::string>();
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<std::string>();
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)

View File

@ -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")