aboutsummaryrefslogtreecommitdiff
path: root/src/zen/zen.cpp
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2025-05-05 18:57:15 +0200
committerGitHub Enterprise <[email protected]>2025-05-05 18:57:15 +0200
commitfd5b8fde6bd838b17e6100f01d466bcbf01f91fe (patch)
tree9fd20b6c4e7ba1da6d654eae74391c84981c354d /src/zen/zen.cpp
parentsilence Out Of Disk errors to sentry (#378) (diff)
downloadarchived-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.cpp181
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;
}
}