diff options
| -rw-r--r-- | src/common/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/common/logging/backend.cpp | 36 | ||||
| -rw-r--r-- | src/common/logging/backend.h | 7 | ||||
| -rw-r--r-- | src/common/logging/filter.cpp | 8 | ||||
| -rw-r--r-- | src/common/logging/filter.h | 2 | ||||
| -rw-r--r-- | src/common/logging/log.h | 33 | ||||
| -rw-r--r-- | src/common/logging/text_formatter.cpp | 41 | ||||
| -rw-r--r-- | src/common/logging/text_formatter.h | 14 | ||||
| -rw-r--r-- | src/common/string_util.cpp | 23 | ||||
| -rw-r--r-- | src/common/string_util.h | 13 |
10 files changed, 112 insertions, 67 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index d132ab969..4480c25f9 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -90,7 +90,7 @@ endif() | |||
| 90 | 90 | ||
| 91 | create_target_directory_groups(common) | 91 | create_target_directory_groups(common) |
| 92 | 92 | ||
| 93 | target_link_libraries(common PUBLIC Boost::boost microprofile) | 93 | target_link_libraries(common PUBLIC Boost::boost fmt microprofile) |
| 94 | if (ARCHITECTURE_x86_64) | 94 | if (ARCHITECTURE_x86_64) |
| 95 | target_link_libraries(common PRIVATE xbyak) | 95 | target_link_libraries(common PRIVATE xbyak) |
| 96 | endif() | 96 | endif() |
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 7f3ae1a4e..22afa1d66 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include "common/logging/filter.h" | 11 | #include "common/logging/filter.h" |
| 12 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 13 | #include "common/logging/text_formatter.h" | 13 | #include "common/logging/text_formatter.h" |
| 14 | #include "common/string_util.h" | ||
| 14 | 15 | ||
| 15 | namespace Log { | 16 | namespace Log { |
| 16 | 17 | ||
| @@ -102,25 +103,20 @@ const char* GetLevelName(Level log_level) { | |||
| 102 | } | 103 | } |
| 103 | 104 | ||
| 104 | Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, | 105 | Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, |
| 105 | const char* function, const char* format, va_list args) { | 106 | const char* function, std::string message) { |
| 106 | using std::chrono::duration_cast; | 107 | using std::chrono::duration_cast; |
| 107 | using std::chrono::steady_clock; | 108 | using std::chrono::steady_clock; |
| 108 | 109 | ||
| 109 | static steady_clock::time_point time_origin = steady_clock::now(); | 110 | static steady_clock::time_point time_origin = steady_clock::now(); |
| 110 | 111 | ||
| 111 | std::array<char, 4 * 1024> formatting_buffer; | ||
| 112 | |||
| 113 | Entry entry; | 112 | Entry entry; |
| 114 | entry.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin); | 113 | entry.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin); |
| 115 | entry.log_class = log_class; | 114 | entry.log_class = log_class; |
| 116 | entry.log_level = log_level; | 115 | entry.log_level = log_level; |
| 117 | 116 | entry.filename = Common::TrimSourcePath(filename); | |
| 118 | snprintf(formatting_buffer.data(), formatting_buffer.size(), "%s:%s:%u", filename, function, | 117 | entry.line_num = line_nr; |
| 119 | line_nr); | 118 | entry.function = function; |
| 120 | entry.location = std::string(formatting_buffer.data()); | 119 | entry.message = std::move(message); |
| 121 | |||
| 122 | vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args); | ||
| 123 | entry.message = std::string(formatting_buffer.data()); | ||
| 124 | 120 | ||
| 125 | return entry; | 121 | return entry; |
| 126 | } | 122 | } |
| @@ -131,15 +127,27 @@ void SetFilter(Filter* new_filter) { | |||
| 131 | filter = new_filter; | 127 | filter = new_filter; |
| 132 | } | 128 | } |
| 133 | 129 | ||
| 134 | void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_nr, | 130 | void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num, |
| 135 | const char* function, const char* format, ...) { | 131 | const char* function, const char* format, ...) { |
| 136 | if (filter != nullptr && !filter->CheckMessage(log_class, log_level)) | 132 | if (filter && !filter->CheckMessage(log_class, log_level)) |
| 137 | return; | 133 | return; |
| 138 | 134 | std::array<char, 4 * 1024> formatting_buffer; | |
| 139 | va_list args; | 135 | va_list args; |
| 140 | va_start(args, format); | 136 | va_start(args, format); |
| 141 | Entry entry = CreateEntry(log_class, log_level, filename, line_nr, function, format, args); | 137 | vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args); |
| 142 | va_end(args); | 138 | va_end(args); |
| 139 | Entry entry = CreateEntry(log_class, log_level, filename, line_num, function, | ||
| 140 | std::string(formatting_buffer.data())); | ||
| 141 | |||
| 142 | PrintColoredMessage(entry); | ||
| 143 | } | ||
| 144 | |||
| 145 | void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num, | ||
| 146 | const char* function, const char* format, const fmt::ArgList& args) { | ||
| 147 | if (filter && !filter->CheckMessage(log_class, log_level)) | ||
| 148 | return; | ||
| 149 | Entry entry = | ||
| 150 | CreateEntry(log_class, log_level, filename, line_num, function, fmt::format(format, args)); | ||
| 143 | 151 | ||
| 144 | PrintColoredMessage(entry); | 152 | PrintColoredMessage(entry); |
| 145 | } | 153 | } |
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index 70744e3e5..7e81efb23 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h | |||
| @@ -22,13 +22,16 @@ struct Entry { | |||
| 22 | std::chrono::microseconds timestamp; | 22 | std::chrono::microseconds timestamp; |
| 23 | Class log_class; | 23 | Class log_class; |
| 24 | Level log_level; | 24 | Level log_level; |
| 25 | std::string location; | 25 | std::string filename; |
| 26 | unsigned int line_num; | ||
| 27 | std::string function; | ||
| 26 | std::string message; | 28 | std::string message; |
| 27 | 29 | ||
| 28 | Entry() = default; | 30 | Entry() = default; |
| 29 | Entry(Entry&& o) = default; | 31 | Entry(Entry&& o) = default; |
| 30 | 32 | ||
| 31 | Entry& operator=(Entry&& o) = default; | 33 | Entry& operator=(Entry&& o) = default; |
| 34 | Entry& operator=(const Entry& o) = default; | ||
| 32 | }; | 35 | }; |
| 33 | 36 | ||
| 34 | /** | 37 | /** |
| @@ -44,7 +47,7 @@ const char* GetLevelName(Level log_level); | |||
| 44 | 47 | ||
| 45 | /// Creates a log entry by formatting the given source location, and message. | 48 | /// Creates a log entry by formatting the given source location, and message. |
| 46 | Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, | 49 | Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, |
| 47 | const char* function, const char* format, va_list args); | 50 | const char* function, std::string message); |
| 48 | 51 | ||
| 49 | void SetFilter(Filter* filter); | 52 | void SetFilter(Filter* filter); |
| 50 | } // namespace Log | 53 | } // namespace Log |
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 733247b51..428723dce 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp | |||
| @@ -65,14 +65,14 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin, | |||
| 65 | const std::string::const_iterator end) { | 65 | const std::string::const_iterator end) { |
| 66 | auto level_separator = std::find(begin, end, ':'); | 66 | auto level_separator = std::find(begin, end, ':'); |
| 67 | if (level_separator == end) { | 67 | if (level_separator == end) { |
| 68 | LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s", | 68 | NGLOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s", |
| 69 | std::string(begin, end).c_str()); | 69 | std::string(begin, end).c_str()); |
| 70 | return false; | 70 | return false; |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | const Level level = GetLevelByName(level_separator + 1, end); | 73 | const Level level = GetLevelByName(level_separator + 1, end); |
| 74 | if (level == Level::Count) { | 74 | if (level == Level::Count) { |
| 75 | LOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str()); | 75 | NGLOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str()); |
| 76 | return false; | 76 | return false; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| @@ -83,7 +83,7 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin, | |||
| 83 | 83 | ||
| 84 | const Class log_class = GetClassByName(begin, level_separator); | 84 | const Class log_class = GetClassByName(begin, level_separator); |
| 85 | if (log_class == Class::Count) { | 85 | if (log_class == Class::Count) { |
| 86 | LOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str()); | 86 | NGLOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str()); |
| 87 | return false; | 87 | return false; |
| 88 | } | 88 | } |
| 89 | 89 | ||
diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h index 16fa72642..ccca289bd 100644 --- a/src/common/logging/filter.h +++ b/src/common/logging/filter.h | |||
| @@ -19,7 +19,7 @@ namespace Log { | |||
| 19 | class Filter { | 19 | class Filter { |
| 20 | public: | 20 | public: |
| 21 | /// Initializes the filter with all classes having `default_level` as the minimum level. | 21 | /// Initializes the filter with all classes having `default_level` as the minimum level. |
| 22 | Filter(Level default_level); | 22 | Filter(Level default_level = Level::Info); |
| 23 | 23 | ||
| 24 | /// Resets the filter so that all classes have `level` as the minimum displayed level. | 24 | /// Resets the filter so that all classes have `level` as the minimum displayed level. |
| 25 | void ResetAll(Level level); | 25 | void ResetAll(Level level); |
diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 3cf13fcb0..31fa932fb 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <chrono> | ||
| 8 | #include <fmt/format.h> | ||
| 7 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 8 | 10 | ||
| 9 | namespace Log { | 11 | namespace Log { |
| @@ -87,7 +89,7 @@ enum class Class : ClassType { | |||
| 87 | }; | 89 | }; |
| 88 | 90 | ||
| 89 | /// Logs a message to the global logger. | 91 | /// Logs a message to the global logger. |
| 90 | void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_nr, | 92 | void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num, |
| 91 | const char* function, | 93 | const char* function, |
| 92 | #ifdef _MSC_VER | 94 | #ifdef _MSC_VER |
| 93 | _Printf_format_string_ | 95 | _Printf_format_string_ |
| @@ -99,6 +101,10 @@ void LogMessage(Class log_class, Level log_level, const char* filename, unsigned | |||
| 99 | #endif | 101 | #endif |
| 100 | ; | 102 | ; |
| 101 | 103 | ||
| 104 | void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num, | ||
| 105 | const char* function, const char* format, const fmt::ArgList& args); | ||
| 106 | FMT_VARIADIC(void, FmtLogMessage, Class, Level, const char*, unsigned int, const char*, const char*) | ||
| 107 | |||
| 102 | } // namespace Log | 108 | } // namespace Log |
| 103 | 109 | ||
| 104 | #define LOG_GENERIC(log_class, log_level, ...) \ | 110 | #define LOG_GENERIC(log_class, log_level, ...) \ |
| @@ -121,3 +127,28 @@ void LogMessage(Class log_class, Level log_level, const char* filename, unsigned | |||
| 121 | LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Error, __VA_ARGS__) | 127 | LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Error, __VA_ARGS__) |
| 122 | #define LOG_CRITICAL(log_class, ...) \ | 128 | #define LOG_CRITICAL(log_class, ...) \ |
| 123 | LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Critical, __VA_ARGS__) | 129 | LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Critical, __VA_ARGS__) |
| 130 | |||
| 131 | // Define the fmt lib macros | ||
| 132 | #ifdef _DEBUG | ||
| 133 | #define NGLOG_TRACE(log_class, ...) \ | ||
| 134 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace, __FILE__, __LINE__, \ | ||
| 135 | __func__, __VA_ARGS__) | ||
| 136 | #else | ||
| 137 | #define NGLOG_TRACE(log_class, fmt, ...) (void(0)) | ||
| 138 | #endif | ||
| 139 | |||
| 140 | #define NGLOG_DEBUG(log_class, ...) \ | ||
| 141 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug, __FILE__, __LINE__, \ | ||
| 142 | __func__, __VA_ARGS__) | ||
| 143 | #define NGLOG_INFO(log_class, ...) \ | ||
| 144 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info, __FILE__, __LINE__, \ | ||
| 145 | __func__, __VA_ARGS__) | ||
| 146 | #define NGLOG_WARNING(log_class, ...) \ | ||
| 147 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning, __FILE__, __LINE__, \ | ||
| 148 | __func__, __VA_ARGS__) | ||
| 149 | #define NGLOG_ERROR(log_class, ...) \ | ||
| 150 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error, __FILE__, __LINE__, \ | ||
| 151 | __func__, __VA_ARGS__) | ||
| 152 | #define NGLOG_CRITICAL(log_class, ...) \ | ||
| 153 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical, __FILE__, __LINE__, \ | ||
| 154 | __func__, __VA_ARGS__) | ||
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp index e7e46c76b..8583916a8 100644 --- a/src/common/logging/text_formatter.cpp +++ b/src/common/logging/text_formatter.cpp | |||
| @@ -18,50 +18,29 @@ | |||
| 18 | 18 | ||
| 19 | namespace Log { | 19 | namespace Log { |
| 20 | 20 | ||
| 21 | // TODO(bunnei): This should be moved to a generic path manipulation library | 21 | std::string FormatLogMessage(const Entry& entry) { |
| 22 | const char* TrimSourcePath(const char* path, const char* root) { | ||
| 23 | const char* p = path; | ||
| 24 | |||
| 25 | while (*p != '\0') { | ||
| 26 | const char* next_slash = p; | ||
| 27 | while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') { | ||
| 28 | ++next_slash; | ||
| 29 | } | ||
| 30 | |||
| 31 | bool is_src = Common::ComparePartialString(p, next_slash, root); | ||
| 32 | p = next_slash; | ||
| 33 | |||
| 34 | if (*p != '\0') { | ||
| 35 | ++p; | ||
| 36 | } | ||
| 37 | if (is_src) { | ||
| 38 | path = p; | ||
| 39 | } | ||
| 40 | } | ||
| 41 | return path; | ||
| 42 | } | ||
| 43 | |||
| 44 | void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len) { | ||
| 45 | unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000); | 22 | unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000); |
| 46 | unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000); | 23 | unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000); |
| 47 | 24 | ||
| 48 | const char* class_name = GetLogClassName(entry.log_class); | 25 | const char* class_name = GetLogClassName(entry.log_class); |
| 49 | const char* level_name = GetLevelName(entry.log_level); | 26 | const char* level_name = GetLevelName(entry.log_level); |
| 50 | 27 | ||
| 51 | snprintf(out_text, text_len, "[%4u.%06u] %s <%s> %s: %s", time_seconds, time_fractional, | 28 | return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}", time_seconds, time_fractional, |
| 52 | class_name, level_name, TrimSourcePath(entry.location.c_str()), entry.message.c_str()); | 29 | class_name, level_name, entry.filename, entry.function, entry.line_num, |
| 30 | entry.message); | ||
| 53 | } | 31 | } |
| 54 | 32 | ||
| 55 | void PrintMessage(const Entry& entry) { | 33 | void PrintMessage(const Entry& entry) { |
| 56 | std::array<char, 4 * 1024> format_buffer; | 34 | auto str = FormatLogMessage(entry) + '\n'; |
| 57 | FormatLogMessage(entry, format_buffer.data(), format_buffer.size()); | 35 | fputs(str.c_str(), stderr); |
| 58 | fputs(format_buffer.data(), stderr); | ||
| 59 | fputc('\n', stderr); | ||
| 60 | } | 36 | } |
| 61 | 37 | ||
| 62 | void PrintColoredMessage(const Entry& entry) { | 38 | void PrintColoredMessage(const Entry& entry) { |
| 63 | #ifdef _WIN32 | 39 | #ifdef _WIN32 |
| 64 | static HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE); | 40 | HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE); |
| 41 | if (console_handle == INVALID_HANDLE_VALUE) { | ||
| 42 | return; | ||
| 43 | } | ||
| 65 | 44 | ||
| 66 | CONSOLE_SCREEN_BUFFER_INFO original_info = {0}; | 45 | CONSOLE_SCREEN_BUFFER_INFO original_info = {0}; |
| 67 | GetConsoleScreenBufferInfo(console_handle, &original_info); | 46 | GetConsoleScreenBufferInfo(console_handle, &original_info); |
diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h index 66e86d9ec..c587faefb 100644 --- a/src/common/logging/text_formatter.h +++ b/src/common/logging/text_formatter.h | |||
| @@ -10,20 +10,8 @@ namespace Log { | |||
| 10 | 10 | ||
| 11 | struct Entry; | 11 | struct Entry; |
| 12 | 12 | ||
| 13 | /** | ||
| 14 | * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's | ||
| 15 | * intended to be used to strip a system-specific build directory from the `__FILE__` macro, | ||
| 16 | * leaving only the path relative to the sources root. | ||
| 17 | * | ||
| 18 | * @param path The input file path as a null-terminated string | ||
| 19 | * @param root The name of the root source directory as a null-terminated string. Path up to and | ||
| 20 | * including the last occurrence of this name will be stripped | ||
| 21 | * @return A pointer to the same string passed as `path`, but starting at the trimmed portion | ||
| 22 | */ | ||
| 23 | const char* TrimSourcePath(const char* path, const char* root = "src"); | ||
| 24 | |||
| 25 | /// Formats a log entry into the provided text buffer. | 13 | /// Formats a log entry into the provided text buffer. |
| 26 | void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len); | 14 | std::string FormatLogMessage(const Entry& entry); |
| 27 | /// Formats and prints a log entry to stderr. | 15 | /// Formats and prints a log entry to stderr. |
| 28 | void PrintMessage(const Entry& entry); | 16 | void PrintMessage(const Entry& entry); |
| 29 | /// Prints the same message as `PrintMessage`, but colored acoording to the severity level. | 17 | /// Prints the same message as `PrintMessage`, but colored acoording to the severity level. |
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index e9a2a6b00..124a8937f 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp | |||
| @@ -462,4 +462,27 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_l | |||
| 462 | 462 | ||
| 463 | return std::string(buffer, len); | 463 | return std::string(buffer, len); |
| 464 | } | 464 | } |
| 465 | |||
| 466 | const char* TrimSourcePath(const char* path, const char* root) { | ||
| 467 | const char* p = path; | ||
| 468 | |||
| 469 | while (*p != '\0') { | ||
| 470 | const char* next_slash = p; | ||
| 471 | while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') { | ||
| 472 | ++next_slash; | ||
| 473 | } | ||
| 474 | |||
| 475 | bool is_src = Common::ComparePartialString(p, next_slash, root); | ||
| 476 | p = next_slash; | ||
| 477 | |||
| 478 | if (*p != '\0') { | ||
| 479 | ++p; | ||
| 480 | } | ||
| 481 | if (is_src) { | ||
| 482 | path = p; | ||
| 483 | } | ||
| 484 | } | ||
| 485 | return path; | ||
| 486 | } | ||
| 487 | |||
| 465 | } // namespace Common | 488 | } // namespace Common |
diff --git a/src/common/string_util.h b/src/common/string_util.h index ceb8df48e..ec0c31a24 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h | |||
| @@ -134,4 +134,17 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) { | |||
| 134 | * NUL-terminated then the string ends at max_len characters. | 134 | * NUL-terminated then the string ends at max_len characters. |
| 135 | */ | 135 | */ |
| 136 | std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len); | 136 | std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len); |
| 137 | |||
| 138 | /** | ||
| 139 | * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's | ||
| 140 | * intended to be used to strip a system-specific build directory from the `__FILE__` macro, | ||
| 141 | * leaving only the path relative to the sources root. | ||
| 142 | * | ||
| 143 | * @param path The input file path as a null-terminated string | ||
| 144 | * @param root The name of the root source directory as a null-terminated string. Path up to and | ||
| 145 | * including the last occurrence of this name will be stripped | ||
| 146 | * @return A pointer to the same string passed as `path`, but starting at the trimmed portion | ||
| 147 | */ | ||
| 148 | const char* TrimSourcePath(const char* path, const char* root = "src"); | ||
| 149 | |||
| 137 | } // namespace Common | 150 | } // namespace Common |