aboutsummaryrefslogtreecommitdiff
path: root/src
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
parentsilence Out Of Disk errors to sentry (#378) (diff)
downloadzen-fd5b8fde6bd838b17e6100f01d466bcbf01f91fe.tar.xz
zen-fd5b8fde6bd838b17e6100f01d466bcbf01f91fe.zip
UE style formatted progress output (#380)
* add UE style @progress style progress
Diffstat (limited to 'src')
-rw-r--r--src/zen/cmds/builds_cmd.cpp206
-rw-r--r--src/zen/cmds/builds_cmd.h1
-rw-r--r--src/zen/cmds/projectstore_cmd.cpp6
-rw-r--r--src/zen/cmds/wipe_cmd.cpp26
-rw-r--r--src/zen/zen.cpp181
-rw-r--r--src/zen/zen.h25
6 files changed, 350 insertions, 95 deletions
diff --git a/src/zen/cmds/builds_cmd.cpp b/src/zen/cmds/builds_cmd.cpp
index 15c635594..134cde0f8 100644
--- a/src/zen/cmds/builds_cmd.cpp
+++ b/src/zen/cmds/builds_cmd.cpp
@@ -145,8 +145,24 @@ namespace {
const std::vector<std::string_view> DefaultExcludeFolders({UnsyncFolderName, ZenFolderName, UGSFolderName, LegacyZenTempFolderName});
const std::vector<std::string_view> DefaultExcludeExtensions({});
- static bool IsVerbose = false;
- static bool UsePlainProgress = false;
+ static bool IsVerbose = false;
+ static ProgressBar::Mode ProgressMode = ProgressBar::Mode::Pretty;
+
+ uint32_t GetUpdateDelayMS(ProgressBar::Mode InMode)
+ {
+ switch (InMode)
+ {
+ case ProgressBar::Mode::Plain:
+ return 5000;
+ case ProgressBar::Mode::Pretty:
+ return 200;
+ case ProgressBar::Mode::Log:
+ return 2000;
+ default:
+ ZEN_ASSERT(false);
+ return 0;
+ }
+ }
#define ZEN_CONSOLE_VERBOSE(fmtstr, ...) \
if (IsVerbose) \
@@ -333,7 +349,7 @@ namespace {
ZEN_TRACE_CPU("CleanDirectory");
Stopwatch Timer;
- ProgressBar Progress(UsePlainProgress);
+ ProgressBar Progress(ProgressMode, "Clean Folder");
std::atomic<bool> CleanWipe = true;
std::atomic<uint64_t> DiscoveredItemCount = 0;
@@ -453,7 +469,7 @@ namespace {
uint64_t LastUpdateTimeMs = Timer.GetElapsedTimeMs();
- Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, ptrdiff_t PendingWork) {
+ Work.Wait(GetUpdateDelayMS(ProgressMode), [&](bool IsAborted, ptrdiff_t PendingWork) {
ZEN_UNUSED(IsAborted, PendingWork);
LastUpdateTimeMs = Timer.GetElapsedTimeMs();
@@ -492,7 +508,7 @@ namespace {
}
uint64_t NowMs = Timer.GetElapsedTimeMs();
- if ((NowMs - LastUpdateTimeMs) >= (UsePlainProgress ? 5000 : 200))
+ if ((NowMs - LastUpdateTimeMs) >= GetUpdateDelayMS(ProgressMode))
{
LastUpdateTimeMs = NowMs;
@@ -1814,15 +1830,34 @@ namespace {
ValidateStatistics& ValidateStats,
DownloadStatistics& DownloadStats)
{
+ ZEN_TRACE_CPU("ValidateBuildPart");
+
+ ProgressBar::SetLogOperationName(ProgressMode, "Validate Part");
+
+ enum TaskSteps : uint32_t
+ {
+ FetchBuild,
+ FetchBuildPart,
+ ValidateBlobs,
+ Cleanup,
+ StepCount
+ };
+
+ auto EndProgress =
+ MakeGuard([&]() { ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::StepCount, TaskSteps::StepCount); });
+
Stopwatch Timer;
- auto _ = MakeGuard([&]() {
+ auto _ = MakeGuard([&]() {
ZEN_CONSOLE("Validated build part {}/{} ('{}') in {}",
BuildId,
BuildPartId,
BuildPartName,
NiceTimeSpanMs(Timer.GetElapsedTimeMs()));
});
- CbObject Build = Storage.GetBuild(BuildId);
+
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::FetchBuild, TaskSteps::StepCount);
+
+ CbObject Build = Storage.GetBuild(BuildId);
if (!BuildPartName.empty())
{
BuildPartId = Build["parts"sv].AsObjectView()[BuildPartName].AsObjectId();
@@ -1837,6 +1872,9 @@ namespace {
{
PreferredMultipartChunkSize = ChunkSize;
}
+
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::FetchBuildPart, TaskSteps::StepCount);
+
CbObject BuildPart = Storage.GetBuildPart(BuildId, BuildPartId);
ValidateStats.BuildPartSize = BuildPart.GetSize();
ZEN_CONSOLE("Validating build part {}/{} ({})", BuildId, BuildPartId, NiceBytes(BuildPart.GetSize()));
@@ -1876,7 +1914,9 @@ namespace {
}
});
- ProgressBar ProgressBar(UsePlainProgress);
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::ValidateBlobs, TaskSteps::StepCount);
+
+ ProgressBar ProgressBar(ProgressMode, "Validate Blobs");
uint64_t AttachmentsToVerifyCount = ChunkAttachments.size() + BlockAttachments.size();
FilteredRate FilteredDownloadedBytesPerSecond;
@@ -1990,7 +2030,7 @@ namespace {
Work.DefaultErrorFunction());
}
- Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, std::ptrdiff_t PendingWork) {
+ Work.Wait(GetUpdateDelayMS(ProgressMode), [&](bool IsAborted, std::ptrdiff_t PendingWork) {
ZEN_UNUSED(IsAborted, PendingWork);
const uint64_t DownloadedAttachmentCount = DownloadStats.DownloadedChunkCount + DownloadStats.DownloadedBlockCount;
@@ -2020,6 +2060,8 @@ namespace {
ProgressBar.Finish();
ValidateStats.ElapsedWallTimeUS = Timer.GetElapsedTimeUs();
+
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Cleanup, TaskSteps::StepCount);
}
void ArrangeChunksIntoBlocks(const ChunkedFolderContent& Content,
@@ -2226,7 +2268,7 @@ namespace {
const std::size_t NewBlockCount = NewBlockChunks.size();
if (NewBlockCount > 0)
{
- ProgressBar ProgressBar(UsePlainProgress);
+ ProgressBar ProgressBar(ProgressMode, "Generate Blocks");
OutBlocks.BlockDescriptions.resize(NewBlockCount);
OutBlocks.BlockSizes.resize(NewBlockCount);
@@ -2389,7 +2431,7 @@ namespace {
Work.DefaultErrorFunction());
}
- Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, std::ptrdiff_t PendingWork) {
+ Work.Wait(GetUpdateDelayMS(ProgressMode), [&](bool IsAborted, std::ptrdiff_t PendingWork) {
ZEN_UNUSED(IsAborted, PendingWork);
FilteredGeneratedBytesPerSecond.Update(GenerateBlocksStats.GeneratedBlockByteCount.load());
@@ -2439,7 +2481,7 @@ namespace {
{
ZEN_TRACE_CPU("UploadPartBlobs");
{
- ProgressBar ProgressBar(UsePlainProgress);
+ ProgressBar ProgressBar(ProgressMode, "Upload Blobs");
WorkerThreadPool& ReadChunkPool = GetIOWorkerPool();
WorkerThreadPool& UploadChunkPool = GetNetworkPool();
@@ -2752,7 +2794,7 @@ namespace {
Work.DefaultErrorFunction());
}
- Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, std::ptrdiff_t PendingWork) {
+ Work.Wait(GetUpdateDelayMS(ProgressMode), [&](bool IsAborted, std::ptrdiff_t PendingWork) {
ZEN_UNUSED(IsAborted, PendingWork);
FilteredCompressedBytesPerSecond.Update(LooseChunksStats.CompressedChunkRawBytes.load());
FilteredGenerateBlockBytesPerSecond.Update(GeneratedBlockByteCount.load());
@@ -2971,6 +3013,23 @@ namespace {
bool IgnoreExistingBlocks,
bool PostUploadVerify)
{
+ ZEN_TRACE_CPU("UploadFolder");
+
+ ProgressBar::SetLogOperationName(ProgressMode, "Upload Folder");
+
+ enum TaskSteps : uint32_t
+ {
+ PrepareBuild,
+ CalculateDelta,
+ Upload,
+ Validate,
+ Cleanup,
+ StepCount
+ };
+
+ auto EndProgress =
+ MakeGuard([&]() { ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::StepCount, TaskSteps::StepCount); });
+
Stopwatch ProcessTimer;
const std::filesystem::path ZenTempFolder = ZenTempFolderPath(ZenFolderPath);
@@ -2986,6 +3045,8 @@ namespace {
CreateDirectories(ZenTempBlockFolderPath(ZenFolderPath));
CreateDirectories(ZenTempChunkFolderPath(ZenFolderPath));
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::PrepareBuild, TaskSteps::StepCount);
+
std::uint64_t TotalRawSize = 0;
CbObject ChunkerParameters;
@@ -3157,7 +3218,7 @@ namespace {
return true;
},
GetIOWorkerPool(),
- UsePlainProgress ? 5000 : 200,
+ GetUpdateDelayMS(ProgressMode),
[&](bool, std::ptrdiff_t) {
ZEN_CONSOLE_VERBOSE("Found {} files in '{}'...", LocalFolderScanStats.AcceptedFileCount.load(), Path);
},
@@ -3212,7 +3273,7 @@ namespace {
TotalRawSize = std::accumulate(Content.RawSizes.begin(), Content.RawSizes.end(), std::uint64_t(0));
{
- ProgressBar ProgressBar(UsePlainProgress);
+ ProgressBar ProgressBar(ProgressMode, "Scan Files");
FilteredRate FilteredBytesHashed;
FilteredBytesHashed.Start();
LocalContent = ChunkFolderContent(
@@ -3221,7 +3282,7 @@ namespace {
Path,
Content,
*ChunkController,
- UsePlainProgress ? 5000 : 200,
+ GetUpdateDelayMS(ProgressMode),
[&](bool, std::ptrdiff_t) {
FilteredBytesHashed.Update(ChunkingStats.BytesHashed.load());
std::string Details = fmt::format("{}/{} ({}/{}, {}B/s) scanned, {} ({}) chunks found",
@@ -3277,6 +3338,8 @@ namespace {
PrepBuildResult.KnownBlocks.size(),
NiceTimeSpanMs(PrepBuildResult.FindBlocksTimeMs)));
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::CalculateDelta, TaskSteps::StepCount);
+
const std::uint64_t LargeAttachmentSize = AllowMultiparts ? PrepBuildResult.PreferredMultipartChunkSize * 4u : (std::uint64_t)-1;
Stopwatch BlockArrangeTimer;
@@ -3386,6 +3449,8 @@ namespace {
UploadStatistics UploadStats;
GeneratedBlocks NewBlocks;
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Upload, TaskSteps::StepCount);
+
if (!NewBlockChunks.empty())
{
Stopwatch GenerateBuildBlocksTimer;
@@ -3710,6 +3775,7 @@ namespace {
DownloadStatistics ValidateDownloadStats;
if (PostUploadVerify && !AbortFlag)
{
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Validate, TaskSteps::StepCount);
ValidateBuildPart(*Storage.BuildStorage, BuildId, BuildPartId, BuildPartName, ValidateStats, ValidateDownloadStats);
}
@@ -3961,6 +4027,8 @@ namespace {
{"uploadedBytesPerSec",
double(GetBytesPerSecond(UploadStats.ElapsedWallTimeUS, UploadStats.ChunksBytes + UploadStats.BlocksBytes))},
{"elapsedTimeSec", double(ProcessTimer.GetElapsedTimeMs() / 1000.0)}});
+
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Cleanup, TaskSteps::StepCount);
}
void VerifyFolder(const ChunkedFolderContent& Content,
@@ -3972,7 +4040,7 @@ namespace {
Stopwatch Timer;
- ProgressBar ProgressBar(UsePlainProgress);
+ ProgressBar ProgressBar(ProgressMode, "Verify Files");
WorkerThreadPool& VerifyPool = GetIOWorkerPool();
@@ -4114,7 +4182,7 @@ namespace {
});
}
- Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, std::ptrdiff_t PendingWork) {
+ Work.Wait(GetUpdateDelayMS(ProgressMode), [&](bool IsAborted, std::ptrdiff_t PendingWork) {
ZEN_UNUSED(IsAborted, PendingWork);
std::string Details = fmt::format("Verified {}/{} ({}). Failed files: {}",
VerifyFolderStats.FilesVerified.load(),
@@ -5085,7 +5153,7 @@ namespace {
Stopwatch Timer;
auto _ = MakeGuard([&LocalFolderScanStats, &Timer]() { LocalFolderScanStats.ElapsedWallTimeUS = Timer.GetElapsedTimeUs(); });
- ProgressBar ProgressBar(UsePlainProgress);
+ ProgressBar ProgressBar(ProgressMode, "Check Files");
ParallellWork Work(AbortFlag);
std::atomic<uint64_t> CompletedPathCount = 0;
@@ -5751,7 +5819,7 @@ namespace {
WorkerThreadPool& NetworkPool = GetNetworkPool();
WorkerThreadPool& WritePool = GetIOWorkerPool();
- ProgressBar WriteProgressBar(UsePlainProgress);
+ ProgressBar WriteProgressBar(ProgressMode, PrimeCacheOnly ? "Downloading" : "Writing");
ParallellWork Work(AbortFlag);
struct LooseChunkHashWorkData
@@ -7054,7 +7122,7 @@ namespace {
{
ZEN_TRACE_CPU("WriteChunks_Wait");
- Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, std::ptrdiff_t PendingWork) {
+ Work.Wait(GetUpdateDelayMS(ProgressMode), [&](bool IsAborted, std::ptrdiff_t PendingWork) {
ZEN_UNUSED(IsAborted, PendingWork);
uint64_t DownloadedBytes = DownloadStats.DownloadedChunkByteCount.load() +
DownloadStats.DownloadedBlockByteCount.load() +
@@ -7241,7 +7309,7 @@ namespace {
WorkerThreadPool& WritePool = GetIOWorkerPool();
- ProgressBar CacheLocalProgressBar(UsePlainProgress);
+ ProgressBar CacheLocalProgressBar(ProgressMode, "Cache Local Data");
ParallellWork Work(AbortFlag);
for (uint32_t LocalPathIndex : FilesToCache)
@@ -7272,7 +7340,7 @@ namespace {
{
ZEN_TRACE_CPU("CacheLocal_Wait");
- Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, std::ptrdiff_t PendingWork) {
+ Work.Wait(GetUpdateDelayMS(ProgressMode), [&](bool IsAborted, std::ptrdiff_t PendingWork) {
ZEN_UNUSED(IsAborted, PendingWork);
const uint64_t WorkTotal = FilesToCache.size();
const uint64_t WorkComplete = CachedCount.load();
@@ -7329,7 +7397,7 @@ namespace {
WorkerThreadPool& WritePool = GetIOWorkerPool();
- ProgressBar RebuildProgressBar(UsePlainProgress);
+ ProgressBar RebuildProgressBar(ProgressMode, "Rebuild State");
ParallellWork Work(AbortFlag);
OutLocalFolderState.Paths.resize(RemoteContent.Paths.size());
@@ -7566,7 +7634,7 @@ namespace {
{
ZEN_TRACE_CPU("FinalizeTree_Wait");
- Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, std::ptrdiff_t PendingWork) {
+ Work.Wait(GetUpdateDelayMS(ProgressMode), [&](bool IsAborted, std::ptrdiff_t PendingWork) {
ZEN_UNUSED(IsAborted, PendingWork);
const uint64_t WorkTotal = Targets.size() + RemoveLocalPathIndexes.size();
const uint64_t WorkComplete = TargetsComplete.load() + DeletedCount.load();
@@ -8164,7 +8232,7 @@ namespace {
{
ByteCountToScan += RawSize;
}
- ProgressBar ProgressBar(false);
+ ProgressBar ProgressBar(ProgressMode, "Scan Files");
FilteredRate FilteredBytesHashed;
FilteredBytesHashed.Start();
ChunkedFolderContent UpdatedLocalContent = ChunkFolderContent(
@@ -8173,7 +8241,7 @@ namespace {
Path,
UpdatedContent,
ChunkController,
- UsePlainProgress ? 5000 : 200,
+ GetUpdateDelayMS(ProgressMode),
[&](bool, std::ptrdiff_t) {
FilteredBytesHashed.Update(ChunkingStats.BytesHashed.load());
std::string Details = fmt::format("{}/{} ({}/{}, {}B/s) scanned, {} ({}) chunks found",
@@ -8232,7 +8300,7 @@ namespace {
{
ByteCountToScan += RawSize;
}
- ProgressBar ProgressBar(false);
+ ProgressBar ProgressBar(ProgressMode, "Scan Files");
FilteredRate FilteredBytesHashed;
FilteredBytesHashed.Start();
LocalContent = ChunkFolderContent(
@@ -8241,7 +8309,7 @@ namespace {
Path,
OutLocalFolderContent,
ChunkController,
- UsePlainProgress ? 5000 : 200,
+ GetUpdateDelayMS(ProgressMode),
[&](bool, std::ptrdiff_t) {
FilteredBytesHashed.Update(ChunkingStats.BytesHashed.load());
std::string Details = fmt::format("{}/{} ({}/{}, {}B/s) scanned, {} ({}) chunks found",
@@ -8289,7 +8357,7 @@ namespace {
std::move(IsAcceptedFolder),
std::move(IsAcceptedFile),
GetIOWorkerPool(),
- UsePlainProgress ? 5000 : 200,
+ GetUpdateDelayMS(ProgressMode),
[](bool, std::ptrdiff_t) {},
AbortFlag);
if (AbortFlag)
@@ -8337,10 +8405,27 @@ namespace {
{
ZEN_TRACE_CPU("DownloadFolder");
+ ProgressBar::SetLogOperationName(ProgressMode, "Download Folder");
+
+ enum TaskSteps : uint32_t
+ {
+ CheckState,
+ CompareState,
+ Download,
+ Verify,
+ Cleanup,
+ StepCount
+ };
+
+ auto EndProgress =
+ MakeGuard([&]() { ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::StepCount, TaskSteps::StepCount); });
+
ZEN_ASSERT((!PrimeCacheOnly) || (PrimeCacheOnly && (!AllowPartialBlockRequests)));
Stopwatch DownloadTimer;
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::CheckState, TaskSteps::StepCount);
+
const std::filesystem::path ZenTempFolder = ZenTempFolderPath(ZenFolderPath);
CreateDirectories(ZenTempFolder);
@@ -8360,6 +8445,8 @@ namespace {
std::vector<ChunkBlockDescription> BlockDescriptions;
std::vector<IoHash> LooseChunkHashes;
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::CompareState, TaskSteps::StepCount);
+
ChunkedFolderContent RemoteContent =
GetRemoteContent(Storage, BuildId, AllBuildParts, ChunkController, PartContents, BlockDescriptions, LooseChunkHashes);
@@ -8471,6 +8558,8 @@ namespace {
RebuildFolderStateStatistics RebuildFolderStateStats;
VerifyFolderStatistics VerifyFolderStats;
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Download, TaskSteps::StepCount);
+
UpdateFolder(SystemRootDir,
Storage,
BuildId,
@@ -8497,6 +8586,8 @@ namespace {
{
if (!PrimeCacheOnly)
{
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Verify, TaskSteps::StepCount);
+
VerifyFolder(RemoteContent, Path, PostDownloadVerify, VerifyFolderStats);
Stopwatch WriteStateTimer;
@@ -8564,6 +8655,9 @@ namespace {
});
}
}
+
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Cleanup, TaskSteps::StepCount);
+
if (CleanDirectory(ZenTempFolder, {}))
{
RemoveDirWithRetry(ZenTempFolder);
@@ -8572,6 +8666,22 @@ namespace {
void DiffFolders(const std::filesystem::path& BasePath, const std::filesystem::path& ComparePath, bool OnlyChunked)
{
+ ZEN_TRACE_CPU("DiffFolders");
+
+ ProgressBar::SetLogOperationName(ProgressMode, "Diff Folders");
+
+ enum TaskSteps : uint32_t
+ {
+ CheckBase,
+ CheckCompare,
+ Diff,
+ Cleanup,
+ StepCount
+ };
+
+ auto EndProgress =
+ MakeGuard([&]() { ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::StepCount, TaskSteps::StepCount); });
+
ChunkedFolderContent BaseFolderContent;
ChunkedFolderContent CompareFolderContent;
@@ -8614,6 +8724,8 @@ namespace {
return true;
};
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::CheckBase, TaskSteps::StepCount);
+
GetFolderContentStatistics BaseGetFolderContentStats;
ChunkingStatistics BaseChunkingStats;
BaseFolderContent = ScanAndChunkFolder(BaseGetFolderContentStats,
@@ -8627,6 +8739,8 @@ namespace {
return;
}
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::CheckCompare, TaskSteps::StepCount);
+
GetFolderContentStatistics CompareGetFolderContentStats;
ChunkingStatistics CompareChunkingStats;
CompareFolderContent = ScanAndChunkFolder(CompareGetFolderContentStats,
@@ -8642,6 +8756,8 @@ namespace {
}
}
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Diff, TaskSteps::StepCount);
+
std::vector<IoHash> AddedHashes;
std::vector<IoHash> RemovedHashes;
uint64_t RemovedSize = 0;
@@ -8728,6 +8844,8 @@ namespace {
NewChunkCount,
NiceBytes(NewChunkSize),
NewPercent);
+
+ ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Cleanup, TaskSteps::StepCount);
}
} // namespace
@@ -8852,7 +8970,18 @@ BuildsCommand::BuildsCommand()
};
auto AddOutputOptions = [this](cxxopts::Options& Ops) {
- Ops.add_option("output", "", "plain-progress", "Show progress using plain output", cxxopts::value(m_PlainProgress), "<progress>");
+ Ops.add_option("output",
+ "",
+ "plain-progress",
+ "Show progress using plain output",
+ cxxopts::value(m_PlainProgress),
+ "<plainprogress>");
+ Ops.add_option("output",
+ "",
+ "log-progress",
+ "Write @progress style progress to output",
+ cxxopts::value(m_LogProgress),
+ "<logprogress>");
Ops.add_option("output", "", "verbose", "Enable verbose console output", cxxopts::value(m_Verbose), "<verbose>");
};
@@ -9335,8 +9464,19 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
};
auto ParseOutputOptions = [&]() {
- IsVerbose = m_Verbose;
- UsePlainProgress = IsVerbose || m_PlainProgress;
+ IsVerbose = m_Verbose;
+ if (m_LogProgress)
+ {
+ ProgressMode = ProgressBar::Mode::Log;
+ }
+ else if (m_Verbose || m_PlainProgress)
+ {
+ ProgressMode = ProgressBar::Mode::Plain;
+ }
+ else
+ {
+ ProgressMode = ProgressBar::Mode::Pretty;
+ }
};
ParseOutputOptions();
diff --git a/src/zen/cmds/builds_cmd.h b/src/zen/cmds/builds_cmd.h
index 01d510e1b..9f6c1f6ec 100644
--- a/src/zen/cmds/builds_cmd.h
+++ b/src/zen/cmds/builds_cmd.h
@@ -28,6 +28,7 @@ private:
std::filesystem::path m_SystemRootDir;
bool m_PlainProgress = false;
+ bool m_LogProgress = false;
bool m_Verbose = false;
bool m_BoostWorkerThreads = false;
diff --git a/src/zen/cmds/projectstore_cmd.cpp b/src/zen/cmds/projectstore_cmd.cpp
index c73842b89..540c4c9ac 100644
--- a/src/zen/cmds/projectstore_cmd.cpp
+++ b/src/zen/cmds/projectstore_cmd.cpp
@@ -168,7 +168,7 @@ namespace {
throw std::runtime_error(fmt::format("invalid job id returned, received '{}'", JobIdText));
}
- ProgressBar ProgressBar(PlainProgress);
+ ProgressBar ProgressBar(PlainProgress ? ProgressBar::Mode::Plain : ProgressBar::Mode::Pretty, ""sv);
auto OuputMessages = [&](CbObjectView StatusObject) {
CbArrayView Messages = StatusObject["Messages"sv].AsArrayView();
@@ -2091,7 +2091,7 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
{
std::unique_ptr<ProgressBar> EmitProgressBar;
{
- ProgressBar ParseProgressBar(false);
+ ProgressBar ParseProgressBar(ProgressBar::Mode::Pretty, "");
CbArrayView Entries = ResponseObject["entries"sv].AsArrayView();
uint64_t Remaining = Entries.Num();
for (auto EntryIter : Entries)
@@ -2110,7 +2110,7 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
}
if (!EmitProgressBar)
{
- EmitProgressBar = std::make_unique<ProgressBar>(false);
+ EmitProgressBar = std::make_unique<ProgressBar>(ProgressBar::Mode::Pretty, ""sv);
WriteStopWatch.Reset();
}
diff --git a/src/zen/cmds/wipe_cmd.cpp b/src/zen/cmds/wipe_cmd.cpp
index 2b4e9ab3c..9bf0c6f9e 100644
--- a/src/zen/cmds/wipe_cmd.cpp
+++ b/src/zen/cmds/wipe_cmd.cpp
@@ -35,10 +35,26 @@ namespace {
static std::atomic<bool> AbortFlag = false;
static bool IsVerbose = false;
static bool Quiet = false;
- static bool UsePlainProgress = false;
+ static ProgressBar::Mode ProgressMode = ProgressBar::Mode::Pretty;
const bool SingleThreaded = false;
bool BoostWorkerThreads = true;
+ uint32_t GetUpdateDelayMS(ProgressBar::Mode InMode)
+ {
+ switch (InMode)
+ {
+ case ProgressBar::Mode::Plain:
+ return 5000;
+ case ProgressBar::Mode::Pretty:
+ return 200;
+ case ProgressBar::Mode::Log:
+ return 2000;
+ default:
+ ZEN_ASSERT(false);
+ return 0;
+ }
+ }
+
WorkerThreadPool& GetIOWorkerPool()
{
return SingleThreaded ? GetSyncWorkerPool()
@@ -161,7 +177,7 @@ namespace {
ZEN_TRACE_CPU("CleanDirectory");
Stopwatch Timer;
- ProgressBar Progress(UsePlainProgress);
+ ProgressBar Progress(ProgressMode, "Clean Folder");
std::atomic<bool> CleanWipe = true;
std::atomic<uint64_t> DiscoveredItemCount = 0;
@@ -396,7 +412,7 @@ namespace {
GetIOWorkerPool(),
Work.PendingWork());
- Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, ptrdiff_t PendingWork) {
+ Work.Wait(ProgressMode == ProgressBar::Mode::Pretty ? 200 : 5000, [&](bool IsAborted, ptrdiff_t PendingWork) {
if (Quiet)
{
return;
@@ -455,7 +471,7 @@ namespace {
}
uint64_t NowMs = Timer.GetElapsedTimeMs();
- if ((NowMs - LastUpdateTimeMs) >= (UsePlainProgress ? 5000 : 200))
+ if ((NowMs - LastUpdateTimeMs) >= GetUpdateDelayMS(ProgressMode))
{
LastUpdateTimeMs = NowMs;
@@ -529,7 +545,7 @@ WipeCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
Quiet = m_Quiet;
IsVerbose = m_Verbose;
- UsePlainProgress = IsVerbose || m_PlainProgress;
+ ProgressMode = (IsVerbose || m_PlainProgress) ? ProgressBar::Mode::Plain : ProgressBar::Mode::Pretty;
BoostWorkerThreads = m_BoostWorkerThreads;
MakeSafeAbsolutePathÍnPlace(m_Directory);
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;
}
}
diff --git a/src/zen/zen.h b/src/zen/zen.h
index dd0fd44b3..9ca228e37 100644
--- a/src/zen/zen.h
+++ b/src/zen/zen.h
@@ -81,7 +81,17 @@ public:
uint64_t RemainingCount = 0;
};
- explicit ProgressBar(bool PlainProgress, bool ShowDetails = true);
+ enum class Mode
+ {
+ Plain,
+ Pretty,
+ Log
+ };
+
+ static void SetLogOperationName(Mode InMode, std::string_view Name);
+ static void SetLogOperationProgress(Mode InMode, uint32_t StepIndex, uint32_t StepCount);
+
+ explicit ProgressBar(Mode InMode, std::string_view InSubTask);
~ProgressBar();
void UpdateState(const State& NewState, bool DoLinebreak);
@@ -91,13 +101,12 @@ public:
bool HasActiveTask() const;
private:
- const bool m_StdoutIsTty = true;
- const bool m_PlainProgress;
- const bool m_ShowDetails;
- Stopwatch m_SW;
- uint64_t m_LastUpdateMS;
- State m_State;
- size_t m_LastOutputLength = 0;
+ const Mode m_Mode;
+ Stopwatch m_SW;
+ uint64_t m_LastUpdateMS;
+ State m_State;
+ const std::string m_SubTask;
+ size_t m_LastOutputLength = 0;
};
} // namespace zen