ShaderCompiler: Add --log-format option
This commit is contained in:
parent
106d629342
commit
e62969999a
|
|
@ -36,6 +36,9 @@ namespace Nz::ShaderLang
|
||||||
#include <Nazara/Shader/ShaderLangErrorList.hpp>
|
#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
|
class NAZARA_SHADER_API Error : public std::exception
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -47,6 +50,7 @@ namespace Nz::ShaderLang
|
||||||
inline ErrorCategory GetErrorCategory() const;
|
inline ErrorCategory GetErrorCategory() const;
|
||||||
const std::string& GetErrorMessage() const;
|
const std::string& GetErrorMessage() const;
|
||||||
inline ErrorType GetErrorType() const;
|
inline ErrorType GetErrorType() const;
|
||||||
|
const std::string& GetFullErrorMessage() const;
|
||||||
inline const SourceLocation& GetSourceLocation() const;
|
inline const SourceLocation& GetSourceLocation() const;
|
||||||
|
|
||||||
const char* what() const noexcept override;
|
const char* what() const noexcept override;
|
||||||
|
|
@ -58,6 +62,7 @@ namespace Nz::ShaderLang
|
||||||
virtual std::string BuildErrorMessage() const = 0;
|
virtual std::string BuildErrorMessage() const = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
mutable std::string m_fullErrorMessage;
|
||||||
mutable std::string m_errorMessage;
|
mutable std::string m_errorMessage;
|
||||||
ErrorCategory m_errorCategory;
|
ErrorCategory m_errorCategory;
|
||||||
SourceLocation m_sourceLocation;
|
SourceLocation m_sourceLocation;
|
||||||
|
|
|
||||||
|
|
@ -44,17 +44,7 @@ struct fmt::formatter<Nz::ShaderLang::ErrorCategory> : formatter<string_view>
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const Nz::ShaderLang::ErrorCategory& p, FormatContext& ctx) -> decltype(ctx.out())
|
auto format(const Nz::ShaderLang::ErrorCategory& p, FormatContext& ctx) -> decltype(ctx.out())
|
||||||
{
|
{
|
||||||
// TODO: Add ToString
|
return formatter<string_view>::format(ToString(p), ctx);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -64,16 +54,7 @@ struct fmt::formatter<Nz::ShaderLang::ErrorType> : formatter<string_view>
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const Nz::ShaderLang::ErrorType& p, FormatContext& ctx) -> decltype(ctx.out())
|
auto format(const Nz::ShaderLang::ErrorType& p, FormatContext& ctx) -> decltype(ctx.out())
|
||||||
{
|
{
|
||||||
// TODO: Add ToString
|
return formatter<string_view>::format(ToString(p), ctx);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -107,9 +88,42 @@ struct fmt::formatter<Nz::ShaderLang::TokenType> : formatter<string_view>
|
||||||
|
|
||||||
namespace Nz::ShaderLang
|
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
|
const std::string& Error::GetErrorMessage() const
|
||||||
{
|
{
|
||||||
if (m_errorMessage.empty())
|
if (m_errorMessage.empty())
|
||||||
|
m_errorMessage = BuildErrorMessage();
|
||||||
|
|
||||||
|
return m_errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Error::GetFullErrorMessage() const
|
||||||
|
{
|
||||||
|
if (m_fullErrorMessage.empty())
|
||||||
{
|
{
|
||||||
if (m_sourceLocation.IsValid())
|
if (m_sourceLocation.IsValid())
|
||||||
{
|
{
|
||||||
|
|
@ -118,22 +132,22 @@ namespace Nz::ShaderLang
|
||||||
sourceFile = *m_sourceLocation.file;
|
sourceFile = *m_sourceLocation.file;
|
||||||
|
|
||||||
if (m_sourceLocation.startLine != m_sourceLocation.endLine)
|
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)
|
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
|
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
|
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
|
const char* Error::what() const noexcept
|
||||||
{
|
{
|
||||||
return GetErrorMessage().c_str();
|
return GetFullErrorMessage().c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
#define NAZARA_SHADERLANG_NEWERRORTYPE(Prefix, ErrorType, ErrorName, ErrorString, ...) \
|
#define NAZARA_SHADERLANG_NEWERRORTYPE(Prefix, ErrorType, ErrorName, ErrorString, ...) \
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,12 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
enum class LogFormat
|
||||||
|
{
|
||||||
|
Classic,
|
||||||
|
VisualStudio
|
||||||
|
};
|
||||||
|
|
||||||
std::vector<Nz::UInt8> ReadFileContent(const std::filesystem::path& filePath)
|
std::vector<Nz::UInt8> ReadFileContent(const std::filesystem::path& filePath)
|
||||||
{
|
{
|
||||||
Nz::File file(filePath);
|
Nz::File file(filePath);
|
||||||
|
|
@ -53,6 +59,7 @@ int main(int argc, char* argv[])
|
||||||
("c,compile", "Compile input shader")
|
("c,compile", "Compile input shader")
|
||||||
("output-nzsl", "Output shader as NZSL to stdout")
|
("output-nzsl", "Output shader as NZSL to stdout")
|
||||||
("header-file", "Generate an includable header file")
|
("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>())
|
("i,input", "Input file(s)", cxxopts::value<std::string>())
|
||||||
("o,output", "Output path", cxxopts::value<std::string>()->default_value("."), "path")
|
("o,output", "Output path", cxxopts::value<std::string>()->default_value("."), "path")
|
||||||
("p,partial", "Allow partial compilation")
|
("p,partial", "Allow partial compilation")
|
||||||
|
|
@ -74,14 +81,27 @@ int main(int argc, char* argv[])
|
||||||
|
|
||||||
if (result.count("input") == 0)
|
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;
|
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>();
|
std::filesystem::path inputPath = result["input"].as<std::string>();
|
||||||
if (!std::filesystem::is_regular_file(inputPath))
|
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;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,62 +173,76 @@ int main(int argc, char* argv[])
|
||||||
}
|
}
|
||||||
catch (const Nz::ShaderLang::Error& error)
|
catch (const Nz::ShaderLang::Error& error)
|
||||||
{
|
{
|
||||||
fmt::print(stderr, (fmt::emphasis::bold | fg(fmt::color::red)), "{}\n", error.what());
|
const Nz::ShaderLang::SourceLocation& errorLocation = error.GetSourceLocation();
|
||||||
|
|
||||||
Nz::ShaderLang::SourceLocation errorLocation = error.GetSourceLocation();
|
|
||||||
if (errorLocation.IsValid())
|
if (errorLocation.IsValid())
|
||||||
{
|
{
|
||||||
try
|
if (logFormat == LogFormat::Classic)
|
||||||
{
|
{
|
||||||
// Retrieve line
|
fmt::print(stderr, (fmt::emphasis::bold | fg(fmt::color::red)), "{}\n", error.what());
|
||||||
std::string sourceContent = ReadSourceFileContent(*errorLocation.file);
|
|
||||||
|
|
||||||
std::size_t lineStartOffset = 0;
|
try
|
||||||
if (errorLocation.startLine > 1)
|
|
||||||
{
|
{
|
||||||
lineStartOffset = sourceContent.find('\n') + 1;
|
// Retrieve line
|
||||||
for (std::size_t i = 0; i < errorLocation.startLine - 2; ++i) //< remember startLine is 1-based
|
std::string sourceContent = ReadSourceFileContent(*errorLocation.file);
|
||||||
|
|
||||||
|
std::size_t lineStartOffset = 0;
|
||||||
|
if (errorLocation.startLine > 1)
|
||||||
{
|
{
|
||||||
lineStartOffset = sourceContent.find('\n', lineStartOffset);
|
lineStartOffset = sourceContent.find('\n') + 1;
|
||||||
if (lineStartOffset == std::string::npos)
|
for (std::size_t i = 0; i < errorLocation.startLine - 2; ++i) //< remember startLine is 1-based
|
||||||
throw std::runtime_error("file content doesn't match original source");
|
{
|
||||||
|
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);
|
catch (const std::exception& e)
|
||||||
|
|
||||||
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)
|
fmt::print(stderr, "failed to print error line: {}\n", e.what());
|
||||||
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, '^'));
|
|
||||||
}
|
}
|
||||||
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)
|
catch (const cxxopts::OptionException& e)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
-- Turns resources into includables headers
|
-- Compile shaders to includables headers
|
||||||
rule("compile_shaders")
|
rule("compile_shaders")
|
||||||
on_load(function (target)
|
on_load(function (target)
|
||||||
target:add("deps", "NazaraShaderCompiler")
|
target:add("deps", "NazaraShaderCompiler")
|
||||||
|
|
@ -22,7 +22,7 @@ rule("compile_shaders")
|
||||||
|
|
||||||
-- add commands
|
-- add commands
|
||||||
batchcmds:show_progress(opt.progress, "${color.build.object}compiling shader %s", shaderfile)
|
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 = "." })
|
batchcmds:vrunv(nzslc:targetfile(), argv, { curdir = "." })
|
||||||
|
|
||||||
local outputFile = path.join(path.directory(shaderfile), path.basename(shaderfile) .. ".nzslb.h")
|
local outputFile = path.join(path.directory(shaderfile), path.basename(shaderfile) .. ".nzslb.h")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue