diff options
| author | Dan Engelbrecht <[email protected]> | 2025-05-05 18:57:15 +0200 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2025-05-05 18:57:15 +0200 |
| commit | fd5b8fde6bd838b17e6100f01d466bcbf01f91fe (patch) | |
| tree | 9fd20b6c4e7ba1da6d654eae74391c84981c354d /src/zen/zen.cpp | |
| parent | silence Out Of Disk errors to sentry (#378) (diff) | |
| download | archived-zen-fd5b8fde6bd838b17e6100f01d466bcbf01f91fe.tar.xz archived-zen-fd5b8fde6bd838b17e6100f01d466bcbf01f91fe.zip | |
UE style formatted progress output (#380)
* add UE style @progress style progress
Diffstat (limited to 'src/zen/zen.cpp')
| -rw-r--r-- | src/zen/zen.cpp | 181 |
1 files changed, 135 insertions, 46 deletions
diff --git a/src/zen/zen.cpp b/src/zen/zen.cpp index e442f8a4b..399c43990 100644 --- a/src/zen/zen.cpp +++ b/src/zen/zen.cpp @@ -269,25 +269,120 @@ ZenCmdBase::ResolveTargetHostSpec(const std::string& InHostSpec) return ResolveTargetHostSpec(InHostSpec, /* out */ Dummy); } +#if ZEN_PLATFORM_WINDOWS +static HANDLE +GetConsoleHandle() +{ + static HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + return hStdOut; +} +#endif + static bool -IsStdoutTty() +CheckStdoutTty() { #if ZEN_PLATFORM_WINDOWS - static HANDLE hStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE); - DWORD dwMode = 0; - static bool IsConsole = ::GetConsoleMode(hStdOut, &dwMode); + HANDLE hStdOut = GetConsoleHandle(); + DWORD dwMode = 0; + static bool IsConsole = ::GetConsoleMode(hStdOut, &dwMode); return IsConsole; #else return isatty(fileno(stdout)); #endif } -ProgressBar::ProgressBar(bool PlainProgress, bool ShowDetails) -: m_StdoutIsTty(IsStdoutTty()) -, m_PlainProgress(PlainProgress || !m_StdoutIsTty) -, m_ShowDetails(ShowDetails) +static bool +IsStdoutTty() +{ + static bool StdoutIsTty = CheckStdoutTty(); + return StdoutIsTty; +} + +static void +OutputToConsoleRaw(const char* String, size_t Length) +{ +#if ZEN_PLATFORM_WINDOWS + HANDLE hStdOut = GetConsoleHandle(); +#endif + +#if ZEN_PLATFORM_WINDOWS + if (IsStdoutTty()) + { + WriteConsoleA(hStdOut, String, (DWORD)Length, 0, 0); + } + else + { + ::WriteFile(hStdOut, (LPCVOID)String, (DWORD)Length, 0, 0); + } +#else + fwrite(String, 1, Length, stdout); +#endif +} + +static void +OutputToConsoleRaw(const std::string& String) +{ + OutputToConsoleRaw(String.c_str(), String.length()); +} + +static void +OutputToConsoleRaw(const StringBuilderBase& SB) +{ + OutputToConsoleRaw(SB.c_str(), SB.Size()); +} + +static uint32_t +GetConsoleColumns() +{ +#if ZEN_PLATFORM_WINDOWS + HANDLE hStdOut = GetConsoleHandle(); + CONSOLE_SCREEN_BUFFER_INFO csbi; + if (GetConsoleScreenBufferInfo(hStdOut, &csbi) == TRUE) + { + return (uint32_t)(csbi.srWindow.Right - csbi.srWindow.Left + 1); + } +#else + struct winsize w; + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) + { + return (uint32_t)w.ws_col; + } +#endif + return 1024; +} + +void +ProgressBar::SetLogOperationName(Mode InMode, std::string_view Name) +{ + if (InMode == Mode::Log) + { + std::string String = fmt::format("@progress {}\n", Name); + OutputToConsoleRaw(String); + } +} + +void +ProgressBar::SetLogOperationProgress(Mode InMode, uint32_t StepIndex, uint32_t StepCount) +{ + if (InMode == Mode::Log) + { + const size_t PercentDone = StepCount > 0u ? gsl::narrow<uint8_t>((100 * StepIndex) / StepCount) : 0u; + + std::string String = fmt::format("@progress {}%\n", PercentDone); + OutputToConsoleRaw(String); + } +} + +ProgressBar::ProgressBar(Mode InMode, std::string_view InSubTask) +: m_Mode((!IsStdoutTty() && InMode == Mode::Pretty) ? Mode::Plain : InMode) , m_LastUpdateMS(m_SW.GetElapsedTimeMs() - 10000) +, m_SubTask(InSubTask) { + if (!m_SubTask.empty() && InMode == Mode::Log) + { + std::string String = fmt::format("@progress push {}\n", m_SubTask); + OutputToConsoleRaw(String); + } } ProgressBar::~ProgressBar() @@ -295,6 +390,11 @@ ProgressBar::~ProgressBar() try { ForceLinebreak(); + if (!m_SubTask.empty() && m_Mode == Mode::Log) + { + const std::string String("@progress pop\n"); + OutputToConsoleRaw(String); + } } catch (const std::exception& Ex) { @@ -319,15 +419,16 @@ ProgressBar::UpdateState(const State& NewState, bool DoLinebreak) m_LastUpdateMS = ElapsedTimeMS; - size_t PercentDone = + const size_t PercentDone = NewState.TotalCount > 0u ? gsl::narrow<uint8_t>((100 * (NewState.TotalCount - NewState.RemainingCount)) / NewState.TotalCount) : 0u; - if (m_PlainProgress) + if (m_Mode == Mode::Plain) { - std::string Details = (m_ShowDetails && !NewState.Details.empty()) ? fmt::format(": {}", NewState.Details) : ""; - ZEN_CONSOLE("{} {}% ({}){}", NewState.Task, PercentDone, NiceTimeSpanMs(ElapsedTimeMS), Details); + const std::string Details = (!NewState.Details.empty()) ? fmt::format(": {}", NewState.Details) : ""; + const std::string Output = fmt::format("{} {}% ({}){}\n", NewState.Task, PercentDone, NiceTimeSpanMs(ElapsedTimeMS), Details); + OutputToConsoleRaw(Output); } - else + else if (m_Mode == Mode::Pretty) { size_t ProgressBarSize = 20; @@ -335,28 +436,7 @@ ProgressBar::UpdateState(const State& NewState, bool DoLinebreak) uint64_t Completed = NewState.TotalCount - NewState.RemainingCount; uint64_t ETAMS = (PercentDone > 5) ? (ElapsedTimeMS * NewState.RemainingCount) / Completed : 0; - uint32_t ConsoleColumns = 1024; - -#if ZEN_PLATFORM_WINDOWS - static HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); -#endif - - if (!m_PlainProgress) - { -#if ZEN_PLATFORM_WINDOWS - CONSOLE_SCREEN_BUFFER_INFO csbi; - if (GetConsoleScreenBufferInfo(hStdOut, &csbi) == TRUE) - { - ConsoleColumns = (uint32_t)(csbi.srWindow.Right - csbi.srWindow.Left + 1); - } -#else - struct winsize w; - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) - { - ConsoleColumns = (uint32_t)w.ws_col; - } -#endif - } + uint32_t ConsoleColumns = GetConsoleColumns(); std::string_view TaskString = NewState.Task; @@ -428,21 +508,30 @@ ProgressBar::UpdateState(const State& NewState, bool DoLinebreak) LineToPrint << "\n"; } -#if ZEN_PLATFORM_WINDOWS - if (m_StdoutIsTty) + OutputToConsoleRaw(LineToPrint); + + m_LastOutputLength = DoLinebreak ? 0 : Output.length(); + m_State = NewState; + } + else if (m_Mode == Mode::Log) + { + if (m_State.Task != NewState.Task || + m_State.Details != NewState.Details) // TODO: Should we output just because details change? Will this spam the log collector? { - WriteConsoleA(hStdOut, LineToPrint.c_str(), (DWORD)LineToPrint.Size(), 0, 0); + const std::string Details = (!NewState.Details.empty()) ? fmt::format(": {}", NewState.Details) : ""; + const std::string Message = fmt::format("@progress {} ({}){}\n", NewState.Task, NiceTimeSpanMs(ElapsedTimeMS), Details); + OutputToConsoleRaw(Message); } - else + + const size_t OldPercentDone = + m_State.TotalCount > 0u ? gsl::narrow<uint8_t>((100 * (m_State.TotalCount - m_State.RemainingCount)) / m_State.TotalCount) : 0u; + + if (OldPercentDone != PercentDone) { - ::WriteFile(hStdOut, (LPCVOID)LineToPrint.c_str(), (DWORD)LineToPrint.Size(), 0, 0); + const std::string Progress = fmt::format("@progress {}%\n", PercentDone); + OutputToConsoleRaw(Progress); } -#else - fwrite(LineToPrint.c_str(), 1, LineToPrint.Size(), stdout); -#endif - - m_LastOutputLength = DoLinebreak ? 0 : Output.length(); - m_State = NewState; + m_State = NewState; } } |