aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2025-04-22 16:28:08 +0200
committerGitHub Enterprise <[email protected]>2025-04-22 16:28:08 +0200
commit732a1cb1e78abbabaa0d926e9b1e58a36538dc1b (patch)
tree08d47d333f68e2f99ec7ec922fa72dea4ee96dbc /src
parentxmake updatefrontend (diff)
downloadzen-732a1cb1e78abbabaa0d926e9b1e58a36538dc1b.tar.xz
zen-732a1cb1e78abbabaa0d926e9b1e58a36538dc1b.zip
add cxxopts overload for parsing file paths from command line (#362)
Diffstat (limited to 'src')
-rw-r--r--src/zen/cmds/admin_cmd.cpp19
-rw-r--r--src/zen/cmds/admin_cmd.h8
-rw-r--r--src/zen/cmds/builds_cmd.cpp603
-rw-r--r--src/zen/cmds/builds_cmd.h50
-rw-r--r--src/zen/cmds/cache_cmd.cpp22
-rw-r--r--src/zen/cmds/cache_cmd.h18
-rw-r--r--src/zen/cmds/copy_cmd.cpp7
-rw-r--r--src/zen/cmds/copy_cmd.h10
-rw-r--r--src/zen/cmds/dedup_cmd.h4
-rw-r--r--src/zen/cmds/status_cmd.cpp9
-rw-r--r--src/zen/cmds/status_cmd.h6
-rw-r--r--src/zen/cmds/up_cmd.cpp41
-rw-r--r--src/zen/cmds/up_cmd.h28
-rw-r--r--src/zen/cmds/workspaces_cmd.cpp55
-rw-r--r--src/zen/cmds/workspaces_cmd.h28
-rw-r--r--src/zen/zen.h5
-rw-r--r--src/zencore/filesystem.cpp16
-rw-r--r--src/zencore/include/zencore/filesystem.h2
-rw-r--r--src/zencore/include/zencore/process.h3
-rw-r--r--src/zencore/process.cpp142
-rw-r--r--src/zenserver/config.cpp38
-rw-r--r--src/zenserver/config/luaconfig.cpp23
-rw-r--r--src/zenserver/config/luaconfig.h5
-rw-r--r--src/zenutil/commandlineoptions.cpp213
-rw-r--r--src/zenutil/include/zenutil/commandlineoptions.h28
-rw-r--r--src/zenutil/zenutil.cpp2
26 files changed, 682 insertions, 703 deletions
diff --git a/src/zen/cmds/admin_cmd.cpp b/src/zen/cmds/admin_cmd.cpp
index 573639c2d..a7cfa6a4e 100644
--- a/src/zen/cmds/admin_cmd.cpp
+++ b/src/zen/cmds/admin_cmd.cpp
@@ -714,29 +714,26 @@ CopyStateCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
throw OptionParseException("data path must be given");
}
- std::filesystem::path DataPath = StringToPath(m_DataPath);
- std::filesystem::path TargetPath = StringToPath(m_TargetPath);
-
- if (!IsDir(DataPath))
+ if (!IsDir(m_DataPath))
{
throw OptionParseException("data path must exist");
}
- if (TargetPath.empty())
+ if (m_TargetPath.empty())
{
throw OptionParseException("target path must be given");
}
- std::filesystem::path RootManifestPath = DataPath / "root_manifest";
- std::filesystem::path TargetRootManifestPath = TargetPath / "root_manifest";
+ std::filesystem::path RootManifestPath = m_DataPath / "root_manifest";
+ std::filesystem::path TargetRootManifestPath = m_TargetPath / "root_manifest";
if (!TryCopy(RootManifestPath, TargetRootManifestPath))
{
throw OptionParseException("data path is invalid, missing root_manifest");
}
- std::filesystem::path CachePath = DataPath / "cache";
- std::filesystem::path TargetCachePath = TargetPath / "cache";
+ std::filesystem::path CachePath = m_DataPath / "cache";
+ std::filesystem::path TargetCachePath = m_TargetPath / "cache";
// Copy cache state
DirectoryContent CacheDirectoryContent;
@@ -781,8 +778,8 @@ CopyStateCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
}
}
- std::filesystem::path CasPath = DataPath / "cas";
- std::filesystem::path TargetCasPath = TargetPath / "cas";
+ std::filesystem::path CasPath = m_DataPath / "cas";
+ std::filesystem::path TargetCasPath = m_TargetPath / "cas";
{
std::filesystem::path UCasRootPath = CasPath / ".ucas_root";
diff --git a/src/zen/cmds/admin_cmd.h b/src/zen/cmds/admin_cmd.h
index 8b6d3e258..c593b2cac 100644
--- a/src/zen/cmds/admin_cmd.h
+++ b/src/zen/cmds/admin_cmd.h
@@ -155,10 +155,10 @@ public:
virtual cxxopts::Options& Options() override { return m_Options; }
private:
- cxxopts::Options m_Options{"copy-state", "Copy zen server disk state"};
- std::string m_DataPath;
- std::string m_TargetPath;
- bool m_SkipLogs = false;
+ cxxopts::Options m_Options{"copy-state", "Copy zen server disk state"};
+ std::filesystem::path m_DataPath;
+ std::filesystem::path m_TargetPath;
+ bool m_SkipLogs = false;
};
} // namespace zen
diff --git a/src/zen/cmds/builds_cmd.cpp b/src/zen/cmds/builds_cmd.cpp
index cdcd79f58..6607fd6d2 100644
--- a/src/zen/cmds/builds_cmd.cpp
+++ b/src/zen/cmds/builds_cmd.cpp
@@ -164,24 +164,6 @@ namespace {
);
- std::filesystem::path MakeSafeAbsolutePath(std::filesystem::path Path)
- {
- std::filesystem::path AbsolutePath = std::filesystem::absolute(Path).make_preferred();
-#if ZEN_PLATFORM_WINDOWS && 1
- const std::string_view Prefix = "\\\\?\\";
- const std::u8string PrefixU8(Prefix.begin(), Prefix.end());
- std::u8string PathString = AbsolutePath.u8string();
- if (!PathString.empty() && !PathString.starts_with(PrefixU8))
- {
- PathString.insert(0, PrefixU8);
- return std::filesystem::path(PathString);
- }
-#endif
- return AbsolutePath;
- }
-
- std::filesystem::path MakeSafeAbsolutePath(const std::string PathString) { return MakeSafeAbsolutePath(StringToPath(PathString)); }
-
bool IsFileWithRetry(const std::filesystem::path& Path)
{
std::error_code Ec;
@@ -3169,8 +3151,9 @@ namespace {
auto ParseManifest = [](const std::filesystem::path& Path,
const std::filesystem::path& ManifestPath) -> std::vector<std::filesystem::path> {
std::vector<std::filesystem::path> AssetPaths;
- std::filesystem::path AbsoluteManifestPath = ManifestPath.is_absolute() ? ManifestPath : Path / ManifestPath;
- IoBuffer ManifestContent = ReadFile(AbsoluteManifestPath).Flatten();
+ std::filesystem::path AbsoluteManifestPath =
+ MakeSafeAbsolutePath(ManifestPath.is_absolute() ? ManifestPath : Path / ManifestPath);
+ IoBuffer ManifestContent = ReadFile(AbsoluteManifestPath).Flatten();
std::string_view ManifestString((const char*)ManifestContent.GetView().GetData(), ManifestContent.GetSize());
std::string_view::size_type Offset = 0;
while (Offset < ManifestContent.GetSize())
@@ -8769,7 +8752,7 @@ BuildsCommand::BuildsCommand()
m_Options.add_options()("h,help", "Print help");
auto AddSystemOptions = [this](cxxopts::Options& Ops) {
- Ops.add_option("", "", "system-dir", "Specify system root", cxxopts::value<std::string>(m_SystemRootDir), "<systemdir>");
+ Ops.add_option("", "", "system-dir", "Specify system root", cxxopts::value(m_SystemRootDir), "<systemdir>");
};
auto AddAuthOptions = [this](cxxopts::Options& Ops) {
@@ -9196,7 +9179,11 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
std::filesystem::path SystemRootDir;
auto ParseSystemOptions = [&]() {
- SystemRootDir = m_SystemRootDir.empty() ? PickDefaultSystemRootDirectory() : MakeSafeAbsolutePath(m_SystemRootDir);
+ if (m_SystemRootDir.empty())
+ {
+ m_SystemRootDir = PickDefaultSystemRootDirectory();
+ }
+ MakeSafeAbsolutePath(m_SystemRootDir);
};
auto ParseStorageOptions = [&]() {
@@ -9216,6 +9203,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
{
throw zen::OptionParseException(fmt::format("At least one storage option is required\n{}", m_UploadOptions.help()));
}
+ MakeSafeAbsolutePath(m_StoragePath);
};
std::unique_ptr<AuthMgr> Auth;
@@ -9225,7 +9213,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
.RetryCount = 2};
auto CreateAuthMgr = [&]() {
- ZEN_ASSERT(!SystemRootDir.empty());
+ ZEN_ASSERT(!m_SystemRootDir.empty());
if (!Auth)
{
if (m_EncryptionKey.empty())
@@ -9240,7 +9228,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
ZEN_CONSOLE("Warning: Using default encryption initialization vector");
}
- AuthConfig AuthMgrConfig = {.RootDirectory = SystemRootDir / "auth",
+ AuthConfig AuthMgrConfig = {.RootDirectory = m_SystemRootDir / "auth",
.EncryptionKey = AesKey256Bit::FromString(m_EncryptionKey),
.EncryptionIV = AesIV128Bit::FromString(m_EncryptionIV)};
if (!AuthMgrConfig.EncryptionKey.IsValid())
@@ -9303,7 +9291,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
}
else if (!m_AccessTokenPath.empty())
{
- std::string ResolvedAccessToken = ReadAccessTokenFromFile(MakeSafeAbsolutePath(m_AccessTokenPath));
+ MakeSafeAbsolutePath(m_AccessTokenPath);
+ std::string ResolvedAccessToken = ReadAccessTokenFromFile(m_AccessTokenPath);
if (!ResolvedAccessToken.empty())
{
ClientSettings.AccessTokenProvider = httpclientauth::CreateFromStaticToken(ResolvedAccessToken);
@@ -9545,10 +9534,9 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
}
else if (!m_StoragePath.empty())
{
- std::filesystem::path StoragePath = MakeSafeAbsolutePath(m_StoragePath);
- StorageDescription = fmt::format("folder {}", StoragePath);
- Result.BuildStorage = CreateFileBuildStorage(StoragePath, StorageStats, false, DefaultLatency, DefaultDelayPerKBSec);
- Result.StorageName = fmt::format("Disk {}", StoragePath.stem());
+ StorageDescription = fmt::format("folder {}", m_StoragePath);
+ Result.BuildStorage = CreateFileBuildStorage(m_StoragePath, StorageStats, false, DefaultLatency, DefaultDelayPerKBSec);
+ Result.StorageName = fmt::format("Disk {}", m_StoragePath.stem());
}
else
{
@@ -9592,12 +9580,146 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
return Result;
};
+ auto ParsePath = [&]() {
+ if (m_Path.empty())
+ {
+ throw zen::OptionParseException(fmt::format("local-path is required\n{}", m_UploadOptions.help()));
+ }
+ MakeSafeAbsolutePath(m_Path);
+ };
+
+ auto ParseDiffPath = [&]() {
+ if (m_DiffPath.empty())
+ {
+ throw zen::OptionParseException(fmt::format("compare-path is required\n{}", m_DownloadOptions.help()));
+ }
+ MakeSafeAbsolutePath(m_DiffPath);
+ };
+
+ auto ParseBlobHash = [&]() -> IoHash {
+ if (m_BlobHash.empty())
+ {
+ throw zen::OptionParseException(fmt::format("Blob hash string is missing\n{}", m_UploadOptions.help()));
+ }
+
+ IoHash BlobHash;
+ if (!IoHash::TryParse(m_BlobHash, BlobHash))
+ {
+ throw zen::OptionParseException(fmt::format("Blob hash string is invalid\n{}", m_UploadOptions.help()));
+ }
+
+ return BlobHash;
+ };
+
+ auto ParseBuildId = [&]() -> Oid {
+ if (m_BuildId.length() != Oid::StringLength)
+ {
+ throw zen::OptionParseException(fmt::format("Invalid build id\n{}", m_UploadOptions.help()));
+ }
+ else if (Oid BuildId = Oid::FromHexString(m_BuildId); BuildId == Oid::Zero)
+ {
+ throw zen::OptionParseException(fmt::format("Invalid build id\n{}", m_UploadOptions.help()));
+ }
+ else
+ {
+ return BuildId;
+ }
+ };
+
+ auto ParseBuildPartId = [&]() -> Oid {
+ if (m_BuildPartId.length() != Oid::StringLength)
+ {
+ throw zen::OptionParseException(fmt::format("Invalid build part id\n{}", m_UploadOptions.help()));
+ }
+ else if (Oid BuildPartId = Oid::FromHexString(m_BuildPartId); BuildPartId == Oid::Zero)
+ {
+ throw zen::OptionParseException(fmt::format("Invalid build part id\n{}", m_UploadOptions.help()));
+ }
+ else
+ {
+ return BuildPartId;
+ }
+ };
+
+ auto ParseBuildPartIds = [&]() -> std::vector<Oid> {
+ std::vector<Oid> BuildPartIds;
+ for (const std::string& BuildPartId : m_BuildPartIds)
+ {
+ BuildPartIds.push_back(Oid::TryFromHexString(BuildPartId));
+ if (BuildPartIds.back() == Oid::Zero)
+ {
+ throw zen::OptionParseException(fmt::format("build-part-id '{}' is invalid\n{}", BuildPartId, m_DownloadOptions.help()));
+ }
+ }
+ return BuildPartIds;
+ };
+
+ auto ParseBuildMetadata = [&]() -> CbObject {
+ if (m_CreateBuild)
+ {
+ if (m_BuildMetadataPath.empty() && m_BuildMetadata.empty())
+ {
+ throw zen::OptionParseException(fmt::format("Options for builds target are missing\n{}", m_UploadOptions.help()));
+ }
+ if (!m_BuildMetadataPath.empty() && !m_BuildMetadata.empty())
+ {
+ throw zen::OptionParseException(fmt::format("Conflicting options for builds target\n{}", m_UploadOptions.help()));
+ }
+
+ if (!m_BuildMetadataPath.empty())
+ {
+ MakeSafeAbsolutePath(m_BuildMetadataPath);
+ IoBuffer MetaDataJson = ReadFile(m_BuildMetadataPath).Flatten();
+ std::string_view Json(reinterpret_cast<const char*>(MetaDataJson.GetData()), MetaDataJson.GetSize());
+ std::string JsonError;
+ CbObject MetaData = LoadCompactBinaryFromJson(Json, JsonError).AsObject();
+ if (!JsonError.empty())
+ {
+ throw std::runtime_error(
+ fmt::format("build metadata file '{}' is malformed. Reason: '{}'", m_BuildMetadataPath, JsonError));
+ }
+ return MetaData;
+ }
+ if (!m_BuildMetadata.empty())
+ {
+ CbObjectWriter MetaDataWriter(1024);
+ ForEachStrTok(m_BuildMetadata, ';', [&](std::string_view Pair) {
+ size_t SplitPos = Pair.find('=');
+ if (SplitPos == std::string::npos || SplitPos == 0)
+ {
+ throw std::runtime_error(fmt::format("build metadata key-value pair '{}' is malformed", Pair));
+ }
+ MetaDataWriter.AddString(Pair.substr(0, SplitPos), Pair.substr(SplitPos + 1));
+ return true;
+ });
+ return MetaDataWriter.Save();
+ }
+ }
+ else
+ {
+ if (!m_BuildMetadataPath.empty())
+ {
+ throw zen::OptionParseException(
+ fmt::format("metadata-path option is only valid if creating a build\n{}", m_UploadOptions.help()));
+ }
+ if (!m_BuildMetadata.empty())
+ {
+ throw zen::OptionParseException(
+ fmt::format("metadata option is only valid if creating a build\n{}", m_UploadOptions.help()));
+ }
+ }
+ return {};
+ };
+
BoostWorkerThreads = m_BoostWorkerThreads;
try
{
if (SubOption == &m_ListOptions)
{
+ MakeSafeAbsolutePath(m_ListQueryPath);
+ MakeSafeAbsolutePath(m_ListResultPath);
+
if (!m_ListResultPath.empty())
{
ZEN_CONSOLE("Running {}: {}", GetRunningExecutablePath(), ZEN_CFG_VERSION_BUILD_STRING_FULL);
@@ -9612,14 +9734,13 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
}
else
{
- std::filesystem::path ListQueryPath = MakeSafeAbsolutePath(m_ListQueryPath);
- if (ToLower(ListQueryPath.extension().string()) == ".cbo")
+ if (ToLower(m_ListQueryPath.extension().string()) == ".cbo")
{
- QueryObject = LoadCompactBinaryObject(IoBufferBuilder::MakeFromFile(ListQueryPath));
+ QueryObject = LoadCompactBinaryObject(IoBufferBuilder::MakeFromFile(m_ListQueryPath));
}
else
{
- IoBuffer MetaDataJson = ReadFile(ListQueryPath).Flatten();
+ IoBuffer MetaDataJson = ReadFile(m_ListQueryPath).Flatten();
std::string_view Json(reinterpret_cast<const char*>(MetaDataJson.GetData()), MetaDataJson.GetSize());
std::string JsonError;
QueryObject = LoadCompactBinaryFromJson(Json, JsonError).AsObject();
@@ -9634,19 +9755,22 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
BuildStorage::Statistics StorageStats;
BuildStorageCache::Statistics StorageCacheStats;
- const std::filesystem::path ZenFolderPath = m_ZenFolderPath.empty()
- ? MakeSafeAbsolutePath(std::filesystem::current_path()) / ZenFolderName
- : MakeSafeAbsolutePath(m_ZenFolderPath);
- CreateDirectories(ZenFolderPath);
- auto _ = MakeGuard([ZenFolderPath]() {
- if (CleanDirectory(ZenFolderPath, {}))
+ if (m_ZenFolderPath.empty())
+ {
+ m_ZenFolderPath = std::filesystem::current_path() / ZenFolderName;
+ }
+ MakeSafeAbsolutePath(m_ZenFolderPath);
+
+ CreateDirectories(m_ZenFolderPath);
+ auto _ = MakeGuard([this]() {
+ if (CleanDirectory(m_ZenFolderPath, {}))
{
std::error_code DummyEc;
- RemoveDir(ZenFolderPath, DummyEc);
+ RemoveDir(m_ZenFolderPath, DummyEc);
}
});
- StorageInstance Storage = CreateBuildStorage(StorageStats, StorageCacheStats, ZenTempFolderPath(ZenFolderPath));
+ StorageInstance Storage = CreateBuildStorage(StorageStats, StorageCacheStats, ZenTempFolderPath(m_ZenFolderPath));
CbObject Response = Storage.BuildStorage->ListBuilds(QueryObject);
ZEN_ASSERT(ValidateCompactBinary(Response.GetView(), CbValidateMode::All) == CbValidateError::None);
@@ -9658,17 +9782,16 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
}
else
{
- std::filesystem::path ListResultPath = MakeSafeAbsolutePath(m_ListResultPath);
- if (ToLower(ListResultPath.extension().string()) == ".cbo")
+ if (ToLower(m_ListResultPath.extension().string()) == ".cbo")
{
MemoryView ResponseView = Response.GetView();
- WriteFile(ListResultPath, IoBuffer(IoBuffer::Wrap, ResponseView.GetData(), ResponseView.GetSize()));
+ WriteFile(m_ListResultPath, IoBuffer(IoBuffer::Wrap, ResponseView.GetData(), ResponseView.GetSize()));
}
else
{
ExtendableStringBuilder<1024> SB;
CompactBinaryToJson(Response.GetView(), SB);
- WriteFile(ListResultPath, IoBuffer(IoBuffer::Wrap, SB.Data(), SB.Size()));
+ WriteFile(m_ListResultPath, IoBuffer(IoBuffer::Wrap, SB.Data(), SB.Size()));
}
}
@@ -9679,130 +9802,53 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
{
ZEN_CONSOLE("Running {}: {}", GetRunningExecutablePath(), ZEN_CFG_VERSION_BUILD_STRING_FULL);
- if (m_Path.empty())
- {
- throw zen::OptionParseException(fmt::format("local-path is required\n{}", m_UploadOptions.help()));
- }
-
- if (m_CreateBuild)
- {
- if (m_BuildMetadataPath.empty() && m_BuildMetadata.empty())
- {
- throw zen::OptionParseException(fmt::format("Options for builds target are missing\n{}", m_UploadOptions.help()));
- }
- if (!m_BuildMetadataPath.empty() && !m_BuildMetadata.empty())
- {
- throw zen::OptionParseException(fmt::format("Conflicting options for builds target\n{}", m_UploadOptions.help()));
- }
- }
- else
- {
- if (!m_BuildMetadataPath.empty())
- {
- throw zen::OptionParseException(
- fmt::format("metadata-path option is only valid if creating a build\n{}", m_UploadOptions.help()));
- }
- if (!m_BuildMetadata.empty())
- {
- throw zen::OptionParseException(
- fmt::format("metadata option is only valid if creating a build\n{}", m_UploadOptions.help()));
- }
- }
-
- std::filesystem::path Path = MakeSafeAbsolutePath(m_Path);
+ ParsePath();
if (m_BuildPartName.empty())
{
- m_BuildPartName = Path.filename().string();
+ m_BuildPartName = m_Path.filename().string();
}
- const bool GeneratedBuildId = m_BuildId.empty();
- if (GeneratedBuildId)
- {
- m_BuildId = Oid::NewOid().ToString();
- }
- else if (m_BuildId.length() != Oid::StringLength)
- {
- throw zen::OptionParseException(fmt::format("Invalid build id\n{}", m_UploadOptions.help()));
- }
- else if (Oid::FromHexString(m_BuildId) == Oid::Zero)
- {
- throw zen::OptionParseException(fmt::format("Invalid build id\n{}", m_UploadOptions.help()));
- }
-
- const bool GeneratedBuildPartId = m_BuildPartId.empty();
- if (GeneratedBuildPartId)
- {
- m_BuildPartId = Oid::NewOid().ToString();
- }
- else if (m_BuildPartId.length() != Oid::StringLength)
+ const Oid BuildId = m_BuildId.empty() ? Oid::NewOid() : ParseBuildId();
+ if (m_BuildId.empty())
{
- throw zen::OptionParseException(fmt::format("Invalid build id\n{}", m_UploadOptions.help()));
+ m_BuildId = BuildId.ToString();
}
- else if (Oid::FromHexString(m_BuildPartId) == Oid::Zero)
+ const Oid BuildPartId = m_BuildPartId.empty() ? Oid::NewOid() : ParseBuildPartId();
+ if (m_BuildPartId.empty())
{
- throw zen::OptionParseException(fmt::format("Invalid build part id\n{}", m_UploadOptions.help()));
+ m_BuildPartId = BuildPartId.ToString();
}
- const Oid BuildId = Oid::FromHexString(m_BuildId);
- const Oid BuildPartId = Oid::FromHexString(m_BuildPartId);
-
BuildStorage::Statistics StorageStats;
BuildStorageCache::Statistics StorageCacheStats;
- const std::filesystem::path ZenFolderPath = m_ZenFolderPath.empty()
- ? MakeSafeAbsolutePath(std::filesystem::current_path()) / ZenFolderName
- : MakeSafeAbsolutePath(m_ZenFolderPath);
- CreateDirectories(ZenFolderPath);
- auto _ = MakeGuard([ZenFolderPath]() {
- if (CleanDirectory(ZenFolderPath, {}))
+ if (m_ZenFolderPath.empty())
+ {
+ m_ZenFolderPath = std::filesystem::current_path() / ZenFolderName;
+ }
+ MakeSafeAbsolutePath(m_ZenFolderPath);
+
+ CreateDirectories(m_ZenFolderPath);
+ auto _ = MakeGuard([this]() {
+ if (CleanDirectory(m_ZenFolderPath, {}))
{
std::error_code DummyEc;
- RemoveDir(ZenFolderPath, DummyEc);
+ RemoveDir(m_ZenFolderPath, DummyEc);
}
});
- StorageInstance Storage = CreateBuildStorage(StorageStats, StorageCacheStats, ZenTempFolderPath(ZenFolderPath));
+ StorageInstance Storage = CreateBuildStorage(StorageStats, StorageCacheStats, ZenTempFolderPath(m_ZenFolderPath));
- CbObject MetaData;
- if (m_CreateBuild)
- {
- if (!m_BuildMetadataPath.empty())
- {
- std::filesystem::path MetadataPath = MakeSafeAbsolutePath(m_BuildMetadataPath);
- IoBuffer MetaDataJson = ReadFile(MetadataPath).Flatten();
- std::string_view Json(reinterpret_cast<const char*>(MetaDataJson.GetData()), MetaDataJson.GetSize());
- std::string JsonError;
- MetaData = LoadCompactBinaryFromJson(Json, JsonError).AsObject();
- if (!JsonError.empty())
- {
- throw std::runtime_error(
- fmt::format("build metadata file '{}' is malformed. Reason: '{}'", m_BuildMetadataPath, JsonError));
- }
- }
- if (!m_BuildMetadata.empty())
- {
- CbObjectWriter MetaDataWriter(1024);
- ForEachStrTok(m_BuildMetadata, ';', [&](std::string_view Pair) {
- size_t SplitPos = Pair.find('=');
- if (SplitPos == std::string::npos || SplitPos == 0)
- {
- throw std::runtime_error(fmt::format("build metadata key-value pair '{}' is malformed", Pair));
- }
- MetaDataWriter.AddString(Pair.substr(0, SplitPos), Pair.substr(SplitPos + 1));
- return true;
- });
- MetaData = MetaDataWriter.Save();
- }
- }
+ CbObject MetaData = ParseBuildMetadata();
UploadFolder(Storage,
BuildId,
BuildPartId,
m_BuildPartName,
- Path,
- ZenFolderPath,
- m_ManifestPath.empty() ? std::filesystem::path{} : MakeSafeAbsolutePath(m_ManifestPath),
+ m_Path,
+ m_ZenFolderPath,
+ m_ManifestPath,
m_FindBlockMaxCount,
m_BlockReuseMinPercentLimit,
m_AllowMultiparts,
@@ -9838,26 +9884,9 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
{
ZEN_CONSOLE("Running {}: {}", GetRunningExecutablePath(), ZEN_CFG_VERSION_BUILD_STRING_FULL);
- ParseSystemOptions();
+ ParsePath();
- if (m_Path.empty())
- {
- throw zen::OptionParseException(fmt::format("local-path is required\n{}", m_DownloadOptions.help()));
- }
- if (m_BuildId.empty())
- {
- throw zen::OptionParseException(fmt::format("build-id is required\n{}", m_DownloadOptions.help()));
- }
- Oid BuildId = Oid::TryFromHexString(m_BuildId);
- if (BuildId == Oid::Zero)
- {
- throw zen::OptionParseException(fmt::format("build-id is invalid\n{}", m_DownloadOptions.help()));
- }
-
- if (!m_BuildPartName.empty() && !m_BuildPartId.empty())
- {
- throw zen::OptionParseException(fmt::format("build-part-id conflicts with build-part-name\n{}", m_DownloadOptions.help()));
- }
+ const Oid BuildId = ParseBuildId();
if (m_PostDownloadVerify && m_PrimeCacheOnly)
{
@@ -9875,34 +9904,26 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
ZEN_WARN("ignoring 'allow-partial-block-requests' option when 'cache-prime-only' is enabled");
}
- std::vector<Oid> BuildPartIds;
- for (const std::string& BuildPartId : m_BuildPartIds)
+ std::vector<Oid> BuildPartIds = ParseBuildPartIds();
+
+ if (m_ZenFolderPath.empty())
{
- BuildPartIds.push_back(Oid::TryFromHexString(BuildPartId));
- if (BuildPartIds.back() == Oid::Zero)
- {
- throw zen::OptionParseException(
- fmt::format("build-part-id '{}' is invalid\n{}", BuildPartId, m_DownloadOptions.help()));
- }
+ m_ZenFolderPath = m_Path / ZenFolderName;
}
-
- std::filesystem::path Path = MakeSafeAbsolutePath(m_Path);
+ MakeSafeAbsolutePath(m_ZenFolderPath);
BuildStorage::Statistics StorageStats;
BuildStorageCache::Statistics StorageCacheStats;
- const std::filesystem::path ZenFolderPath =
- m_ZenFolderPath.empty() ? Path / ZenFolderName : MakeSafeAbsolutePath(m_ZenFolderPath);
-
- StorageInstance Storage = CreateBuildStorage(StorageStats, StorageCacheStats, ZenTempFolderPath(ZenFolderPath));
+ StorageInstance Storage = CreateBuildStorage(StorageStats, StorageCacheStats, ZenTempFolderPath(m_ZenFolderPath));
DownloadFolder(Storage,
BuildId,
BuildPartIds,
m_BuildPartNames,
- Path,
- ZenFolderPath,
- SystemRootDir,
+ m_Path,
+ m_ZenFolderPath,
+ m_SystemRootDir,
m_AllowMultiparts,
m_AllowPartialBlockRequests && !m_PrimeCacheOnly,
m_Clean,
@@ -9910,79 +9931,43 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
m_PrimeCacheOnly,
m_EnableScavenging);
- if (false)
- {
- ZEN_CONSOLE(
- "{}:\n"
- "Read: {}\n"
- "Write: {}\n"
- "Requests: {}\n"
- "Avg Request Time: {}\n"
- "Avg I/O Time: {}",
- Storage.StorageName,
- NiceBytes(StorageStats.TotalBytesRead.load()),
- NiceBytes(StorageStats.TotalBytesWritten.load()),
- StorageStats.TotalRequestCount.load(),
- StorageStats.TotalExecutionTimeUs.load() > 0
- ? NiceTimeSpanMs(StorageStats.TotalExecutionTimeUs.load() / 1000 / StorageStats.TotalRequestCount.load())
- : 0,
- StorageStats.TotalRequestCount.load() > 0
- ? NiceTimeSpanMs(StorageStats.TotalRequestTimeUs.load() / 1000 / StorageStats.TotalRequestCount.load())
- : 0);
- }
-
return AbortFlag ? 11 : 0;
}
if (SubOption == &m_DiffOptions)
{
- if (m_Path.empty())
- {
- throw zen::OptionParseException(fmt::format("local-path is required\n{}", m_DownloadOptions.help()));
- }
- if (m_DiffPath.empty())
- {
- throw zen::OptionParseException(fmt::format("compare-path is required\n{}", m_DownloadOptions.help()));
- }
- std::filesystem::path Path = MakeSafeAbsolutePath(m_Path);
- std::filesystem::path DiffPath = MakeSafeAbsolutePath(m_DiffPath);
- DiffFolders(Path, DiffPath, m_OnlyChunked);
+ ParsePath();
+ ParseDiffPath();
+
+ DiffFolders(m_Path, m_DiffPath, m_OnlyChunked);
return AbortFlag ? 11 : 0;
}
if (SubOption == &m_FetchBlobOptions)
{
ZEN_CONSOLE("Running {}: {}", GetRunningExecutablePath(), ZEN_CFG_VERSION_BUILD_STRING_FULL);
- if (m_BlobHash.empty())
- {
- throw zen::OptionParseException(fmt::format("Blob hash string is missing\n{}", m_UploadOptions.help()));
- }
-
- IoHash BlobHash;
- if (!IoHash::TryParse(m_BlobHash, BlobHash))
- {
- throw zen::OptionParseException(fmt::format("Blob hash string is invalid\n{}", m_UploadOptions.help()));
- }
+ IoHash BlobHash = ParseBlobHash();
const Oid BuildId = Oid::FromHexString(m_BuildId);
- std::filesystem::path Path = MakeSafeAbsolutePath(m_Path);
-
BuildStorage::Statistics StorageStats;
BuildStorageCache::Statistics StorageCacheStats;
- const std::filesystem::path ZenFolderPath = m_ZenFolderPath.empty()
- ? MakeSafeAbsolutePath(std::filesystem::current_path()) / ZenFolderName
- : MakeSafeAbsolutePath(m_ZenFolderPath);
- CreateDirectories(ZenFolderPath);
- auto _ = MakeGuard([ZenFolderPath]() {
- if (CleanDirectory(ZenFolderPath, {}))
+ if (m_ZenFolderPath.empty())
+ {
+ m_ZenFolderPath = std::filesystem::current_path() / ZenFolderName;
+ }
+ MakeSafeAbsolutePath(m_ZenFolderPath);
+
+ CreateDirectories(m_ZenFolderPath);
+ auto _ = MakeGuard([this]() {
+ if (CleanDirectory(m_ZenFolderPath, {}))
{
std::error_code DummyEc;
- RemoveDir(ZenFolderPath, DummyEc);
+ RemoveDir(m_ZenFolderPath, DummyEc);
}
});
- StorageInstance Storage = CreateBuildStorage(StorageStats, StorageCacheStats, ZenTempFolderPath(ZenFolderPath));
+ StorageInstance Storage = CreateBuildStorage(StorageStats, StorageCacheStats, ZenTempFolderPath(m_ZenFolderPath));
uint64_t CompressedSize;
uint64_t DecompressedSize;
@@ -10002,41 +9987,34 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
{
ZEN_CONSOLE("Running {}: {}", GetRunningExecutablePath(), ZEN_CFG_VERSION_BUILD_STRING_FULL);
- if (m_BuildId.empty())
- {
- throw zen::OptionParseException(fmt::format("build-id is required\n{}", m_DownloadOptions.help()));
- }
- Oid BuildId = Oid::TryFromHexString(m_BuildId);
- if (BuildId == Oid::Zero)
- {
- throw zen::OptionParseException(fmt::format("build-id is invalid\n{}", m_DownloadOptions.help()));
- }
+ Oid BuildId = ParseBuildId();
if (!m_BuildPartName.empty() && !m_BuildPartId.empty())
{
throw zen::OptionParseException(fmt::format("build-part-id conflicts with build-part-name\n{}", m_DownloadOptions.help()));
}
- std::filesystem::path Path = MakeSafeAbsolutePath(m_Path);
-
BuildStorage::Statistics StorageStats;
BuildStorageCache::Statistics StorageCacheStats;
- const std::filesystem::path ZenFolderPath = m_ZenFolderPath.empty()
- ? MakeSafeAbsolutePath(std::filesystem::current_path()) / ZenFolderName
- : MakeSafeAbsolutePath(m_ZenFolderPath);
- CreateDirectories(ZenFolderPath);
- auto _ = MakeGuard([ZenFolderPath]() {
- if (CleanDirectory(ZenFolderPath, {}))
+ if (m_ZenFolderPath.empty())
+ {
+ m_ZenFolderPath = std::filesystem::current_path() / ZenFolderName;
+ }
+ MakeSafeAbsolutePath(m_ZenFolderPath);
+
+ CreateDirectories(m_ZenFolderPath);
+ auto _ = MakeGuard([this]() {
+ if (CleanDirectory(m_ZenFolderPath, {}))
{
std::error_code DummyEc;
- RemoveDir(ZenFolderPath, DummyEc);
+ RemoveDir(m_ZenFolderPath, DummyEc);
}
});
- StorageInstance Storage = CreateBuildStorage(StorageStats, StorageCacheStats, ZenTempFolderPath(ZenFolderPath));
+ StorageInstance Storage = CreateBuildStorage(StorageStats, StorageCacheStats, ZenTempFolderPath(m_ZenFolderPath));
- Oid BuildPartId = Oid::TryFromHexString(m_BuildPartId);
+ const Oid BuildPartId = m_BuildPartName.empty() ? Oid::Zero : ParseBuildPartId();
ValidateStatistics ValidateStats;
DownloadStatistics DownloadStats;
@@ -10047,35 +10025,23 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
if (SubOption == &m_MultiTestDownloadOptions)
{
- SystemRootDir = (GetRunningExecutablePath().parent_path() / ".tmpzensystem").make_preferred();
- CreateDirectories(SystemRootDir);
- CleanDirectory(SystemRootDir, {});
- auto _ = MakeGuard([&]() { DeleteDirectories(SystemRootDir); });
+ m_SystemRootDir = (GetRunningExecutablePath().parent_path() / ".tmpzensystem").make_preferred();
+ CreateDirectories(m_SystemRootDir);
+ CleanDirectory(m_SystemRootDir, {});
+ auto _ = MakeGuard([&]() { DeleteDirectories(m_SystemRootDir); });
+
+ ParsePath();
- if (m_Path.empty())
+ if (m_ZenFolderPath.empty())
{
- throw zen::OptionParseException(fmt::format("local-path is required\n{}", m_DownloadOptions.help()));
+ m_ZenFolderPath = m_Path / ZenFolderName;
}
-
- // m_StoragePath = "D:\\buildstorage";
- // m_Path = "F:\\Saved\\DownloadedBuilds\\++Fortnite+Main-CL-XXXXXXXX\\WindowsClient";
- // std::vector<std::string> BuildIdStrings{"07d3942f0e7f4ca1b13b0587",
- // "07d394eed89d769f2254e75d",
- // "07d3953f22fa3f8000fa6f0a",
- // "07d3959df47ed1f42ddbe44c",
- // "07d395fa7803d50804f14417",
- // "07d3964f919d577a321a1fdd",
- // "07d396a6ce875004e16b9528"};
-
- std::filesystem::path Path = MakeSafeAbsolutePath(m_Path);
+ MakeSafeAbsolutePath(m_ZenFolderPath);
BuildStorage::Statistics StorageStats;
BuildStorageCache::Statistics StorageCacheStats;
- const std::filesystem::path ZenFolderPath =
- m_ZenFolderPath.empty() ? Path / ZenFolderName : MakeSafeAbsolutePath(m_ZenFolderPath);
-
- StorageInstance Storage = CreateBuildStorage(StorageStats, StorageCacheStats, ZenTempFolderPath(ZenFolderPath));
+ StorageInstance Storage = CreateBuildStorage(StorageStats, StorageCacheStats, ZenTempFolderPath(m_ZenFolderPath));
Stopwatch Timer;
for (const std::string& BuildIdString : m_BuildIds)
@@ -10089,9 +10055,9 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
BuildId,
{},
{},
- Path,
- ZenFolderPath,
- SystemRootDir,
+ m_Path,
+ m_ZenFolderPath,
+ m_SystemRootDir,
m_AllowMultiparts,
m_AllowPartialBlockRequests,
BuildIdString == m_BuildIds.front(),
@@ -10111,47 +10077,41 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
if (SubOption == &m_TestOptions)
{
- SystemRootDir = (GetRunningExecutablePath().parent_path() / ".tmpzensystem").make_preferred();
- CreateDirectories(SystemRootDir);
- CleanDirectory(SystemRootDir, {});
- auto _ = MakeGuard([&]() { DeleteDirectories(SystemRootDir); });
-
- if (m_Path.empty())
- {
- throw zen::OptionParseException(fmt::format("local-path is required\n{}", m_DownloadOptions.help()));
- }
+ m_SystemRootDir = (GetRunningExecutablePath().parent_path() / ".tmpzensystem").make_preferred();
+ CreateDirectories(m_SystemRootDir);
+ CleanDirectory(m_SystemRootDir, {});
+ auto _ = MakeGuard([&]() { DeleteDirectories(m_SystemRootDir); });
- std::filesystem::path Path = MakeSafeAbsolutePath(m_Path);
+ ParsePath();
m_BuildId = Oid::NewOid().ToString();
- m_BuildPartName = Path.filename().string();
+ m_BuildPartName = m_Path.filename().string();
m_BuildPartId = Oid::NewOid().ToString();
m_CreateBuild = true;
const Oid BuildId = Oid::FromHexString(m_BuildId);
const Oid BuildPartId = Oid::FromHexString(m_BuildPartId);
- std::filesystem::path StoragePath = MakeSafeAbsolutePath(m_StoragePath);
-
- if (m_OverrideHost.empty() && StoragePath.empty())
+ if (m_OverrideHost.empty() && m_StoragePath.empty())
{
- StoragePath = (GetRunningExecutablePath().parent_path() / ".tmpstore").make_preferred();
- CreateDirectories(StoragePath);
- CleanDirectory(StoragePath, {});
- m_StoragePath = StoragePath.generic_string();
+ m_StoragePath = (GetRunningExecutablePath().parent_path() / ".tmpstore").make_preferred();
+ CreateDirectories(m_StoragePath);
+ CleanDirectory(m_StoragePath, {});
+ m_StoragePath = m_StoragePath.generic_string();
}
+
auto __ = MakeGuard([&]() {
- if (m_OverrideHost.empty() && StoragePath.empty())
+ if (m_OverrideHost.empty() && m_StoragePath.empty())
{
- DeleteDirectories(StoragePath);
+ DeleteDirectories(m_StoragePath);
}
});
BuildStorage::Statistics StorageStats;
BuildStorageCache::Statistics StorageCacheStats;
- const std::filesystem::path DownloadPath = Path.parent_path() / (m_BuildPartName + "_test");
- const std::filesystem::path DownloadPath2 = Path.parent_path() / (m_BuildPartName + "_test2");
+ const std::filesystem::path DownloadPath = m_Path.parent_path() / (m_BuildPartName + "_test");
+ const std::filesystem::path DownloadPath2 = m_Path.parent_path() / (m_BuildPartName + "_test2");
auto ___ = MakeGuard([DownloadPath, DownloadPath2]() {
CleanDirectory(DownloadPath, true);
@@ -10160,10 +10120,13 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
DeleteDirectories(DownloadPath2);
});
- const std::filesystem::path ZenFolderPath =
- m_ZenFolderPath.empty() ? DownloadPath / ZenFolderName : MakeSafeAbsolutePath(m_ZenFolderPath);
+ if (m_ZenFolderPath.empty())
+ {
+ m_ZenFolderPath = m_Path / ZenFolderName;
+ }
+ MakeSafeAbsolutePath(m_ZenFolderPath);
- StorageInstance Storage = CreateBuildStorage(StorageStats, StorageCacheStats, ZenTempFolderPath(ZenFolderPath));
+ StorageInstance Storage = CreateBuildStorage(StorageStats, StorageCacheStats, ZenTempFolderPath(m_ZenFolderPath));
auto MakeMetaData = [](const Oid& BuildId) -> CbObject {
CbObjectWriter BuildMetaDataWriter;
@@ -10190,8 +10153,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
BuildId,
BuildPartId,
m_BuildPartName,
- Path,
- ZenFolderPath,
+ m_Path,
+ m_ZenFolderPath,
{},
m_FindBlockMaxCount,
m_BlockReuseMinPercentLimit,
@@ -10212,8 +10175,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
{BuildPartId},
{},
DownloadPath,
- ZenFolderPath,
- SystemRootDir,
+ DownloadPath / ZenFolderName,
+ m_SystemRootDir,
m_AllowMultiparts,
m_AllowPartialBlockRequests,
true,
@@ -10236,8 +10199,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
{BuildPartId},
{},
DownloadPath,
- ZenFolderPath,
- SystemRootDir,
+ DownloadPath / ZenFolderName,
+ m_SystemRootDir,
m_AllowMultiparts,
m_AllowPartialBlockRequests,
false,
@@ -10355,8 +10318,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
{BuildPartId},
{},
DownloadPath,
- ZenFolderPath,
- SystemRootDir,
+ DownloadPath / ZenFolderName,
+ m_SystemRootDir,
m_AllowMultiparts,
m_AllowPartialBlockRequests,
false,
@@ -10386,7 +10349,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
BuildPartId2,
m_BuildPartName,
DownloadPath,
- ZenFolderPath,
+ m_ZenFolderPath,
{},
m_FindBlockMaxCount,
m_BlockReuseMinPercentLimit,
@@ -10407,8 +10370,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
{BuildPartId},
{},
DownloadPath,
- ZenFolderPath,
- SystemRootDir,
+ DownloadPath / ZenFolderName,
+ m_SystemRootDir,
m_AllowMultiparts,
m_AllowPartialBlockRequests,
false,
@@ -10427,8 +10390,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
{BuildPartId2},
{},
DownloadPath,
- ZenFolderPath,
- SystemRootDir,
+ DownloadPath / ZenFolderName,
+ m_SystemRootDir,
m_AllowMultiparts,
m_AllowPartialBlockRequests,
false,
@@ -10447,8 +10410,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
{BuildPartId2},
{},
DownloadPath,
- ZenFolderPath,
- SystemRootDir,
+ DownloadPath / ZenFolderName,
+ m_SystemRootDir,
m_AllowMultiparts,
m_AllowPartialBlockRequests,
false,
@@ -10467,8 +10430,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
{BuildPartId},
{},
DownloadPath2,
- ZenFolderPath,
- SystemRootDir,
+ DownloadPath2 / ZenFolderName,
+ m_SystemRootDir,
m_AllowMultiparts,
m_AllowPartialBlockRequests,
false,
diff --git a/src/zen/cmds/builds_cmd.h b/src/zen/cmds/builds_cmd.h
index 7e1e7d0ca..cd2ae1f08 100644
--- a/src/zen/cmds/builds_cmd.h
+++ b/src/zen/cmds/builds_cmd.h
@@ -25,13 +25,13 @@ public:
private:
cxxopts::Options m_Options{Name, Description};
- std::string m_SystemRootDir;
+ std::filesystem::path m_SystemRootDir;
bool m_PlainProgress = false;
bool m_Verbose = false;
bool m_BoostWorkerThreads = false;
- std::string m_ZenFolderPath;
+ std::filesystem::path m_ZenFolderPath;
// cloud builds
std::string m_OverrideHost;
@@ -41,29 +41,29 @@ private:
std::string m_Bucket;
// file storage
- std::string m_StoragePath;
- bool m_WriteMetadataAsJson = false;
+ std::filesystem::path m_StoragePath;
+ bool m_WriteMetadataAsJson = false;
// cache
std::string m_ZenCacheHost;
bool m_PrimeCacheOnly = false;
- std::string m_BuildId;
- bool m_CreateBuild = false;
- std::string m_BuildMetadataPath;
- std::string m_BuildMetadata;
- std::string m_BuildPartName; // Defaults to name of leaf folder in m_Path
- std::string m_BuildPartId; // Defaults to a generated id when creating part, looked up when downloading using m_BuildPartName
- bool m_Clean = false;
- uint8_t m_BlockReuseMinPercentLimit = 85;
- bool m_AllowMultiparts = true;
- bool m_AllowPartialBlockRequests = true;
- std::string m_ManifestPath;
+ std::string m_BuildId;
+ bool m_CreateBuild = false;
+ std::filesystem::path m_BuildMetadataPath;
+ std::string m_BuildMetadata;
+ std::string m_BuildPartName; // Defaults to name of leaf folder in m_Path
+ std::string m_BuildPartId; // Defaults to a generated id when creating part, looked up when downloading using m_BuildPartName
+ bool m_Clean = false;
+ uint8_t m_BlockReuseMinPercentLimit = 85;
+ bool m_AllowMultiparts = true;
+ bool m_AllowPartialBlockRequests = true;
+ std::string m_ManifestPath; // Not a std::filesystem::path since it can be relative to m_Path
// Direct access token (may expire)
- std::string m_AccessToken;
- std::string m_AccessTokenEnv;
- std::string m_AccessTokenPath;
+ std::string m_AccessToken;
+ std::string m_AccessTokenEnv;
+ std::filesystem::path m_AccessTokenPath;
// Auth manager token encryption
std::string m_EncryptionKey; // 256 bit AES encryption key
@@ -84,11 +84,11 @@ private:
std::string m_Verb; // list, upload, download
- cxxopts::Options m_ListOptions{"list", "List available builds"};
- std::string m_ListQueryPath;
- std::string m_ListResultPath;
+ cxxopts::Options m_ListOptions{"list", "List available builds"};
+ std::filesystem::path m_ListQueryPath;
+ std::filesystem::path m_ListResultPath;
- std::string m_Path;
+ std::filesystem::path m_Path;
cxxopts::Options m_UploadOptions{"upload", "Upload a folder"};
uint64_t m_FindBlockMaxCount = 10000;
@@ -100,9 +100,9 @@ private:
bool m_PostDownloadVerify = false;
bool m_EnableScavenging = true;
- cxxopts::Options m_DiffOptions{"diff", "Compare two local folders"};
- std::string m_DiffPath;
- bool m_OnlyChunked = false;
+ cxxopts::Options m_DiffOptions{"diff", "Compare two local folders"};
+ std::filesystem::path m_DiffPath;
+ bool m_OnlyChunked = false;
cxxopts::Options m_FetchBlobOptions{"fetch-blob", "Fetch a blob from remote store"};
std::string m_BlobHash;
diff --git a/src/zen/cmds/cache_cmd.cpp b/src/zen/cmds/cache_cmd.cpp
index 185edc35d..4412eaf34 100644
--- a/src/zen/cmds/cache_cmd.cpp
+++ b/src/zen/cmds/cache_cmd.cpp
@@ -625,22 +625,20 @@ CacheGetCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
HttpClient Http(m_HostName);
- std::filesystem::path TargetPath;
if (!m_OutputPath.empty())
{
- TargetPath = std::filesystem::path(m_OutputPath);
- if (IsDir(TargetPath))
+ if (IsDir(m_OutputPath))
{
- TargetPath = TargetPath / (m_AttachmentHash.empty() ? m_ValueKey : m_AttachmentHash);
+ m_OutputPath = m_OutputPath / (m_AttachmentHash.empty() ? m_ValueKey : m_AttachmentHash);
}
else
{
- CreateDirectories(TargetPath.parent_path());
+ CreateDirectories(m_OutputPath.parent_path());
}
}
- if (TargetPath.empty())
+ if (m_OutputPath.empty())
{
- TargetPath = (m_AttachmentHash.empty() ? m_ValueKey : m_AttachmentHash);
+ m_OutputPath = (m_AttachmentHash.empty() ? m_ValueKey : m_AttachmentHash);
}
std::string Url = fmt::format("/z$/{}/{}/{}", m_Namespace, m_Bucket, m_ValueKey);
@@ -670,17 +668,17 @@ CacheGetCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
}
else
{
- WriteFile(TargetPath, IoBuffer(IoBuffer::Wrap, StringData.data(), StringData.length()));
- ZEN_CONSOLE("Wrote {} to '{}' ({})", NiceBytes(StringData.length()), TargetPath, ToString(ChunkData.GetContentType()));
+ WriteFile(m_OutputPath, IoBuffer(IoBuffer::Wrap, StringData.data(), StringData.length()));
+ ZEN_CONSOLE("Wrote {} to '{}' ({})", NiceBytes(StringData.length()), m_OutputPath, ToString(ChunkData.GetContentType()));
}
}
else
{
- if (!MoveToFile(TargetPath, ChunkData))
+ if (!MoveToFile(m_OutputPath, ChunkData))
{
- WriteFile(TargetPath, ChunkData);
+ WriteFile(m_OutputPath, ChunkData);
}
- ZEN_CONSOLE("Wrote {} to '{}' ({})", NiceBytes(ChunkData.GetSize()), TargetPath, ToString(ChunkData.GetContentType()));
+ ZEN_CONSOLE("Wrote {} to '{}' ({})", NiceBytes(ChunkData.GetSize()), m_OutputPath, ToString(ChunkData.GetContentType()));
}
}
else
diff --git a/src/zen/cmds/cache_cmd.h b/src/zen/cmds/cache_cmd.h
index 73702cada..b8a319359 100644
--- a/src/zen/cmds/cache_cmd.h
+++ b/src/zen/cmds/cache_cmd.h
@@ -108,15 +108,15 @@ public:
virtual cxxopts::Options& Options() override { return m_Options; }
private:
- cxxopts::Options m_Options{Name, Description};
- std::string m_HostName;
- std::string m_Namespace;
- std::string m_Bucket;
- std::string m_ValueKey;
- std::string m_AttachmentHash;
- std::string m_OutputPath;
- bool m_AsText = false;
- bool m_Decompress = true;
+ cxxopts::Options m_Options{Name, Description};
+ std::string m_HostName;
+ std::string m_Namespace;
+ std::string m_Bucket;
+ std::string m_ValueKey;
+ std::string m_AttachmentHash;
+ std::filesystem::path m_OutputPath;
+ bool m_AsText = false;
+ bool m_Decompress = true;
};
} // namespace zen
diff --git a/src/zen/cmds/copy_cmd.cpp b/src/zen/cmds/copy_cmd.cpp
index 53e80c896..e86b6964c 100644
--- a/src/zen/cmds/copy_cmd.cpp
+++ b/src/zen/cmds/copy_cmd.cpp
@@ -42,11 +42,8 @@ CopyCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
if (m_CopyTarget.empty())
throw std::runtime_error("No target specified");
- std::filesystem::path FromPath;
- std::filesystem::path ToPath;
-
- FromPath = m_CopySource;
- ToPath = m_CopyTarget;
+ std::filesystem::path FromPath = m_CopySource;
+ std::filesystem::path ToPath = m_CopyTarget;
std::error_code Ec;
std::filesystem::path FromCanonical = std::filesystem::canonical(FromPath, Ec);
diff --git a/src/zen/cmds/copy_cmd.h b/src/zen/cmds/copy_cmd.h
index 876aff3f5..e9735c159 100644
--- a/src/zen/cmds/copy_cmd.h
+++ b/src/zen/cmds/copy_cmd.h
@@ -19,11 +19,11 @@ public:
virtual ZenCmdCategory& CommandCategory() const override { return g_UtilitiesCategory; }
private:
- cxxopts::Options m_Options{"copy", "Copy files efficiently"};
- std::string m_CopySource;
- std::string m_CopyTarget;
- bool m_NoClone = false;
- bool m_MustClone = false;
+ cxxopts::Options m_Options{"copy", "Copy files efficiently"};
+ std::filesystem::path m_CopySource;
+ std::filesystem::path m_CopyTarget;
+ bool m_NoClone = false;
+ bool m_MustClone = false;
};
} // namespace zen
diff --git a/src/zen/cmds/dedup_cmd.h b/src/zen/cmds/dedup_cmd.h
index c4f0068e4..2721be2b9 100644
--- a/src/zen/cmds/dedup_cmd.h
+++ b/src/zen/cmds/dedup_cmd.h
@@ -21,8 +21,8 @@ public:
private:
cxxopts::Options m_Options{"dedup", "Deduplicate files"};
std::vector<std::string> m_Positional;
- std::string m_DedupSource;
- std::string m_DedupTarget;
+ std::filesystem::path m_DedupSource;
+ std::filesystem::path m_DedupTarget;
size_t m_SizeThreshold = 1024 * 1024;
};
diff --git a/src/zen/cmds/status_cmd.cpp b/src/zen/cmds/status_cmd.cpp
index 88c0b22a2..2b507e43d 100644
--- a/src/zen/cmds/status_cmd.cpp
+++ b/src/zen/cmds/status_cmd.cpp
@@ -32,17 +32,16 @@ StatusCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
uint16_t EffectivePort = 0;
if (!m_DataDir.empty())
{
- std::filesystem::path DataDir = StringToPath(m_DataDir);
- if (!IsFile(DataDir / ".lock"))
+ if (!IsFile(m_DataDir / ".lock"))
{
- ZEN_CONSOLE("lock file does not exist in directory '{}'", DataDir);
+ ZEN_CONSOLE("lock file does not exist in directory '{}'", m_DataDir);
return 1;
}
- LockFileInfo Info = ReadLockFilePayload(LoadCompactBinaryObject(IoBufferBuilder::MakeFromFile(DataDir / ".lock")));
+ LockFileInfo Info = ReadLockFilePayload(LoadCompactBinaryObject(IoBufferBuilder::MakeFromFile(m_DataDir / ".lock")));
std::string Reason;
if (!ValidateLockFileInfo(Info, Reason))
{
- ZEN_CONSOLE("lock file in directory '{}' is not valid. Reason: '{}'", DataDir, Reason);
+ ZEN_CONSOLE("lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, Reason);
return 1;
}
EffectivePort = Info.EffectiveListenPort;
diff --git a/src/zen/cmds/status_cmd.h b/src/zen/cmds/status_cmd.h
index 00ad0e758..46bda9ee6 100644
--- a/src/zen/cmds/status_cmd.h
+++ b/src/zen/cmds/status_cmd.h
@@ -20,9 +20,9 @@ public:
private:
int GetLockFileEffectivePort() const;
- cxxopts::Options m_Options{"status", "Show zen status"};
- uint16_t m_Port = 0;
- std::string m_DataDir;
+ cxxopts::Options m_Options{"status", "Show zen status"};
+ uint16_t m_Port = 0;
+ std::filesystem::path m_DataDir;
};
} // namespace zen
diff --git a/src/zen/cmds/up_cmd.cpp b/src/zen/cmds/up_cmd.cpp
index aacc115a0..f3bf2f66d 100644
--- a/src/zen/cmds/up_cmd.cpp
+++ b/src/zen/cmds/up_cmd.cpp
@@ -77,15 +77,13 @@ UpCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
}
}
- std::filesystem::path ProgramBaseDir = StringToPath(m_ProgramBaseDir);
-
- if (ProgramBaseDir.empty())
+ if (m_ProgramBaseDir.empty())
{
std::filesystem::path ExePath = zen::GetRunningExecutablePath();
- ProgramBaseDir = ExePath.parent_path();
+ m_ProgramBaseDir = ExePath.parent_path();
}
ZenServerEnvironment ServerEnvironment;
- ServerEnvironment.Initialize(ProgramBaseDir);
+ ServerEnvironment.Initialize(m_ProgramBaseDir);
ZenServerInstance Server(ServerEnvironment);
std::string ServerArguments = GlobalOptions.PassthroughCommandLine;
if ((m_Port != 0) && (ServerArguments.find("--port"sv) == std::string::npos))
@@ -155,20 +153,18 @@ AttachCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
Instance.Sweep();
ZenServerState::ZenServerEntry* Entry = Instance.Lookup(m_Port);
- std::filesystem::path DataDir = StringToPath(m_DataDir);
-
- if (!DataDir.empty())
+ if (!m_DataDir.empty())
{
- if (!IsFile(DataDir / ".lock"))
+ if (!IsFile(m_DataDir / ".lock"))
{
- ZEN_CONSOLE("lock file does not exist in directory '{}'", DataDir);
+ ZEN_CONSOLE("lock file does not exist in directory '{}'", m_DataDir);
return 1;
}
- LockFileInfo Info = ReadLockFilePayload(LoadCompactBinaryObject(IoBufferBuilder::MakeFromFile(DataDir / ".lock")));
+ LockFileInfo Info = ReadLockFilePayload(LoadCompactBinaryObject(IoBufferBuilder::MakeFromFile(m_DataDir / ".lock")));
std::string Reason;
if (!ValidateLockFileInfo(Info, Reason))
{
- ZEN_CONSOLE("lock file in directory '{}' is not valid. Reason: '{}'", DataDir, Reason);
+ ZEN_CONSOLE("lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, Reason);
return 1;
}
Entry = Instance.LookupByEffectivePort(Info.EffectiveListenPort);
@@ -218,27 +214,24 @@ DownCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
Instance.Initialize();
ZenServerState::ZenServerEntry* Entry = Instance.Lookup(m_Port);
- std::filesystem::path ProgramBaseDir = StringToPath(m_ProgramBaseDir);
- if (ProgramBaseDir.empty())
+ if (m_ProgramBaseDir.empty())
{
std::filesystem::path ExePath = GetRunningExecutablePath();
- ProgramBaseDir = ExePath.parent_path();
+ m_ProgramBaseDir = ExePath.parent_path();
}
- std::filesystem::path DataDir = StringToPath(m_DataDir);
-
- if (!DataDir.empty())
+ if (!m_DataDir.empty())
{
- if (!IsFile(DataDir / ".lock"))
+ if (!IsFile(m_DataDir / ".lock"))
{
- ZEN_CONSOLE("lock file does not exist in directory '{}'", DataDir);
+ ZEN_CONSOLE("lock file does not exist in directory '{}'", m_DataDir);
return 1;
}
- LockFileInfo Info = ReadLockFilePayload(LoadCompactBinaryObject(IoBufferBuilder::MakeFromFile(DataDir / ".lock")));
+ LockFileInfo Info = ReadLockFilePayload(LoadCompactBinaryObject(IoBufferBuilder::MakeFromFile(m_DataDir / ".lock")));
std::string Reason;
if (!ValidateLockFileInfo(Info, Reason))
{
- ZEN_CONSOLE("lock file in directory '{}' is not valid. Reason: '{}'", DataDir, Reason);
+ ZEN_CONSOLE("lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, Reason);
return 1;
}
Entry = Instance.LookupByEffectivePort(Info.EffectiveListenPort);
@@ -251,7 +244,7 @@ DownCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
try
{
ZenServerEnvironment ServerEnvironment;
- ServerEnvironment.Initialize(ProgramBaseDir);
+ ServerEnvironment.Initialize(m_ProgramBaseDir);
ZenServerInstance Server(ServerEnvironment);
Server.AttachToRunningServer(EntryPort);
@@ -316,7 +309,7 @@ DownCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
if (m_ForceTerminate)
{
// Try to find the running executable by path name
- std::filesystem::path ServerExePath = ProgramBaseDir / "zenserver" ZEN_EXE_SUFFIX_LITERAL;
+ std::filesystem::path ServerExePath = m_ProgramBaseDir / "zenserver" ZEN_EXE_SUFFIX_LITERAL;
ProcessHandle RunningProcess;
if (std::error_code Ec = FindProcess(ServerExePath, RunningProcess); !Ec)
{
diff --git a/src/zen/cmds/up_cmd.h b/src/zen/cmds/up_cmd.h
index 32d8ddab3..c9af16749 100644
--- a/src/zen/cmds/up_cmd.h
+++ b/src/zen/cmds/up_cmd.h
@@ -18,11 +18,11 @@ public:
virtual cxxopts::Options& Options() override { return m_Options; }
private:
- cxxopts::Options m_Options{"up", "Bring up zen service"};
- uint16_t m_Port = 0;
- bool m_ShowConsole = false;
- bool m_ShowLog = false;
- std::string m_ProgramBaseDir;
+ cxxopts::Options m_Options{"up", "Bring up zen service"};
+ uint16_t m_Port = 0;
+ bool m_ShowConsole = false;
+ bool m_ShowLog = false;
+ std::filesystem::path m_ProgramBaseDir;
};
class AttachCommand : public ZenCmdBase
@@ -35,10 +35,10 @@ public:
virtual cxxopts::Options& Options() override { return m_Options; }
private:
- cxxopts::Options m_Options{"attach", "Add a sponsor process to a running zen service"};
- uint16_t m_Port = 0;
- int m_OwnerPid = 0;
- std::string m_DataDir;
+ cxxopts::Options m_Options{"attach", "Add a sponsor process to a running zen service"};
+ uint16_t m_Port = 0;
+ int m_OwnerPid = 0;
+ std::filesystem::path m_DataDir;
};
class DownCommand : public ZenCmdBase
@@ -51,11 +51,11 @@ public:
virtual cxxopts::Options& Options() override { return m_Options; }
private:
- cxxopts::Options m_Options{"down", "Bring down zen service"};
- uint16_t m_Port = 0;
- bool m_ForceTerminate = false;
- std::string m_ProgramBaseDir;
- std::string m_DataDir;
+ cxxopts::Options m_Options{"down", "Bring down zen service"};
+ uint16_t m_Port = 0;
+ bool m_ForceTerminate = false;
+ std::filesystem::path m_ProgramBaseDir;
+ std::filesystem::path m_DataDir;
};
} // namespace zen
diff --git a/src/zen/cmds/workspaces_cmd.cpp b/src/zen/cmds/workspaces_cmd.cpp
index 5f3f8f7ca..2ddd0c73a 100644
--- a/src/zen/cmds/workspaces_cmd.cpp
+++ b/src/zen/cmds/workspaces_cmd.cpp
@@ -139,18 +139,16 @@ WorkspaceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
m_HostName = ResolveTargetHostSpec(m_HostName);
- std::filesystem::path SystemRootDir = StringToPath(m_SystemRootDir);
-
- if (SystemRootDir.empty())
+ if (m_SystemRootDir.empty())
{
- SystemRootDir = PickDefaultSystemRootDirectory();
- if (SystemRootDir.empty())
+ m_SystemRootDir = PickDefaultSystemRootDirectory();
+ if (m_SystemRootDir.empty())
{
throw zen::OptionParseException("unable to resolve system root directory");
}
}
- std::filesystem::path StatePath = SystemRootDir / "workspaces";
+ std::filesystem::path StatePath = m_SystemRootDir / "workspaces";
if (!ParseOptions(*SubOption, gsl::narrow<int>(SubCommandArguments.size()), SubCommandArguments.data()))
{
@@ -392,18 +390,20 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char**
m_HostName = ResolveTargetHostSpec(m_HostName);
- std::filesystem::path SystemRootDir = StringToPath(m_SystemRootDir);
-
- if (SystemRootDir.empty())
+ if (m_SystemRootDir.empty())
{
- SystemRootDir = PickDefaultSystemRootDirectory();
- if (SystemRootDir.empty())
+ m_SystemRootDir = PickDefaultSystemRootDirectory();
+ if (m_SystemRootDir.empty())
{
throw zen::OptionParseException("unable to resolve system root directory");
}
}
+ else
+ {
+ MakeSafeAbsolutePath(m_SystemRootDir);
+ }
- std::filesystem::path StatePath = SystemRootDir / "workspaces";
+ std::filesystem::path StatePath = m_SystemRootDir / "workspaces";
if (!ParseOptions(*SubOption, gsl::narrow<int>(SubCommandArguments.size()), SubCommandArguments.data()))
{
@@ -412,8 +412,7 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char**
if (SubOption == &m_CreateOptions)
{
- std::filesystem::path WorkspaceRoot = StringToPath(m_WorkspaceRoot);
- if (WorkspaceRoot.empty())
+ if (m_WorkspaceRoot.empty())
{
if (m_WorkspaceId.empty())
{
@@ -432,15 +431,15 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char**
ZEN_CONSOLE("Workspace {} does not exist", m_WorkspaceId);
return 0;
}
- WorkspaceRoot = WorkspaceConfig.RootPath;
+ m_WorkspaceRoot = WorkspaceConfig.RootPath;
}
else
{
- RemoveTrailingPathSeparator(WorkspaceRoot);
+ RemoveTrailingPathSeparator(m_WorkspaceRoot);
if (m_WorkspaceId.empty())
{
- m_WorkspaceId = Workspaces::PathToId(WorkspaceRoot).ToString();
- ZEN_CONSOLE("Using generated workspace id {} from path '{}'", m_WorkspaceId, WorkspaceRoot);
+ m_WorkspaceId = Workspaces::PathToId(m_WorkspaceRoot).ToString();
+ ZEN_CONSOLE("Using generated workspace id {} from path '{}'", m_WorkspaceId, m_WorkspaceRoot);
}
else
{
@@ -449,25 +448,23 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char**
throw zen::OptionParseException(fmt::format("workspace id '{}' is invalid", m_WorkspaceId));
}
}
- if (Workspaces::AddWorkspace(Log(), StatePath, {.Id = Oid::FromHexString(m_WorkspaceId), .RootPath = WorkspaceRoot}))
+ if (Workspaces::AddWorkspace(Log(), StatePath, {.Id = Oid::FromHexString(m_WorkspaceId), .RootPath = m_WorkspaceRoot}))
{
- ZEN_CONSOLE("Created workspace {} using root path '{}'", m_WorkspaceId, WorkspaceRoot);
+ ZEN_CONSOLE("Created workspace {} using root path '{}'", m_WorkspaceId, m_WorkspaceRoot);
}
else
{
- ZEN_CONSOLE("Using existing workspace {} with root path '{}'", m_WorkspaceId, WorkspaceRoot);
+ ZEN_CONSOLE("Using existing workspace {} with root path '{}'", m_WorkspaceId, m_WorkspaceRoot);
}
}
- std::filesystem::path SharePath = StringToPath(m_SharePath);
-
- RemoveLeadingPathSeparator(SharePath);
- RemoveTrailingPathSeparator(SharePath);
+ RemoveLeadingPathSeparator(m_SharePath);
+ RemoveTrailingPathSeparator(m_SharePath);
if (m_ShareId.empty())
{
- m_ShareId = Workspaces::PathToId(SharePath).ToString();
- ZEN_CONSOLE("Using generated share id {}, for path '{}'", m_ShareId, SharePath);
+ m_ShareId = Workspaces::PathToId(m_SharePath).ToString();
+ ZEN_CONSOLE("Using generated share id {}, for path '{}'", m_ShareId, m_SharePath);
}
if (Oid::TryFromHexString(m_ShareId) == Oid::Zero)
@@ -476,8 +473,8 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char**
}
if (Workspaces::AddWorkspaceShare(Log(),
- WorkspaceRoot,
- {.Id = Oid::FromHexString(m_ShareId), .SharePath = SharePath, .Alias = m_Alias}))
+ m_WorkspaceRoot,
+ {.Id = Oid::FromHexString(m_ShareId), .SharePath = m_SharePath, .Alias = m_Alias}))
{
if (!m_HostName.empty())
{
diff --git a/src/zen/cmds/workspaces_cmd.h b/src/zen/cmds/workspaces_cmd.h
index 86452e25e..d85d8f7d8 100644
--- a/src/zen/cmds/workspaces_cmd.h
+++ b/src/zen/cmds/workspaces_cmd.h
@@ -21,9 +21,9 @@ public:
virtual cxxopts::Options& Options() override { return m_Options; }
private:
- cxxopts::Options m_Options{Name, Description};
- std::string m_HostName;
- std::string m_SystemRootDir;
+ cxxopts::Options m_Options{Name, Description};
+ std::string m_HostName;
+ std::filesystem::path m_SystemRootDir;
std::string m_Verb; // create, info, remove
@@ -53,17 +53,17 @@ public:
virtual cxxopts::Options& Options() override { return m_Options; }
private:
- cxxopts::Options m_Options{Name, Description};
- std::string m_HostName;
- std::string m_SystemRootDir;
- std::string m_WorkspaceId;
- std::string m_WorkspaceRoot;
- std::string m_Verb; // create, info, remove
- std::string m_ShareId;
- std::string m_Alias;
-
- cxxopts::Options m_CreateOptions{"create", "Create a workspace share"};
- std::string m_SharePath;
+ cxxopts::Options m_Options{Name, Description};
+ std::string m_HostName;
+ std::filesystem::path m_SystemRootDir;
+ std::string m_WorkspaceId;
+ std::filesystem::path m_WorkspaceRoot;
+ std::string m_Verb; // create, info, remove
+ std::string m_ShareId;
+ std::string m_Alias;
+
+ cxxopts::Options m_CreateOptions{"create", "Create a workspace share"};
+ std::filesystem::path m_SharePath;
bool m_Refresh = false;
diff --git a/src/zen/zen.h b/src/zen/zen.h
index 6765101db..dd0fd44b3 100644
--- a/src/zen/zen.h
+++ b/src/zen/zen.h
@@ -5,10 +5,7 @@
#include <zencore/except.h>
#include <zencore/timer.h>
#include <zencore/zencore.h>
-
-ZEN_THIRD_PARTY_INCLUDES_START
-#include <cxxopts.hpp>
-ZEN_THIRD_PARTY_INCLUDES_END
+#include <zenutil/commandlineoptions.h>
namespace cpr {
class Response;
diff --git a/src/zencore/filesystem.cpp b/src/zencore/filesystem.cpp
index ad796cb4a..018330d9b 100644
--- a/src/zencore/filesystem.cpp
+++ b/src/zencore/filesystem.cpp
@@ -2670,22 +2670,6 @@ SetFileReadOnly(const std::filesystem::path& Filename, bool ReadOnly)
return Result;
}
-std::filesystem::path
-StringToPath(const std::string_view& Path)
-{
- std::string_view UnquotedPath = Path;
-
- if (Path.length() > 2 && Path.front() == '\"' && Path.back() == '\"')
- {
- UnquotedPath = Path.substr(1, Path.length() - 2);
- }
- if (UnquotedPath.ends_with('/') || UnquotedPath.ends_with('\\') || UnquotedPath.ends_with(std::filesystem::path::preferred_separator))
- {
- UnquotedPath = UnquotedPath.substr(0, UnquotedPath.length() - 1);
- }
- return std::filesystem::path(UnquotedPath).make_preferred();
-}
-
//////////////////////////////////////////////////////////////////////////
//
// Testing related code follows...
diff --git a/src/zencore/include/zencore/filesystem.h b/src/zencore/include/zencore/filesystem.h
index 66deffa6f..1bc3943df 100644
--- a/src/zencore/include/zencore/filesystem.h
+++ b/src/zencore/include/zencore/filesystem.h
@@ -372,8 +372,6 @@ uint32_t MakeFileModeReadOnly(uint32_t FileMode, bool ReadOnly);
bool SetFileReadOnly(const std::filesystem::path& Filename, bool ReadOnly, std::error_code& Ec);
bool SetFileReadOnly(const std::filesystem::path& Filename, bool ReadOnly);
-std::filesystem::path StringToPath(const std::string_view& Path);
-
//////////////////////////////////////////////////////////////////////////
void filesystem_forcelink(); // internal
diff --git a/src/zencore/include/zencore/process.h b/src/zencore/include/zencore/process.h
index 0c5931ba0..d1394cd9a 100644
--- a/src/zencore/include/zencore/process.h
+++ b/src/zencore/include/zencore/process.h
@@ -100,9 +100,6 @@ int GetProcessId(CreateProcResult ProcId);
std::filesystem::path GetProcessExecutablePath(int Pid, std::error_code& OutEc);
std::error_code FindProcess(const std::filesystem::path& ExecutableImage, ProcessHandle& OutHandle);
-std::vector<std::string> ParseCommandLine(std::string_view CommandLine);
-std::vector<char*> StripCommandlineQuotes(std::vector<std::string>& InOutArgs);
-
void process_forcelink(); // internal
} // namespace zen
diff --git a/src/zencore/process.cpp b/src/zencore/process.cpp
index 2fe5b8948..48efc3f85 100644
--- a/src/zencore/process.cpp
+++ b/src/zencore/process.cpp
@@ -1047,118 +1047,6 @@ FindProcess(const std::filesystem::path& ExecutableImage, ProcessHandle& OutHand
#endif // ZEN_PLATFORM_LINUX
}
-std::vector<std::string>
-ParseCommandLine(std::string_view CommandLine)
-{
- auto IsWhitespaceOrEnd = [](std::string_view CommandLine, std::string::size_type Pos) {
- if (Pos == CommandLine.length())
- {
- return true;
- }
- if (CommandLine[Pos] == ' ')
- {
- return true;
- }
- return false;
- };
-
- bool IsParsingArg = false;
- bool IsInQuote = false;
-
- std::string::size_type Pos = 0;
- std::string::size_type ArgStart = 0;
- std::vector<std::string> Args;
- while (Pos < CommandLine.length())
- {
- if (IsInQuote)
- {
- if (CommandLine[Pos] == '"' && IsWhitespaceOrEnd(CommandLine, Pos + 1))
- {
- Args.push_back(std::string(CommandLine.substr(ArgStart, Pos - ArgStart + 1)));
- Pos++;
- IsInQuote = false;
- IsParsingArg = false;
- }
- else
- {
- Pos++;
- }
- }
- else if (IsParsingArg)
- {
- ZEN_ASSERT(Pos > ArgStart);
- if (CommandLine[Pos] == ' ')
- {
- Args.push_back(std::string(CommandLine.substr(ArgStart, Pos - ArgStart)));
- Pos++;
- IsParsingArg = false;
- }
- else if (CommandLine[Pos] == '"')
- {
- IsInQuote = true;
- Pos++;
- }
- else
- {
- Pos++;
- }
- }
- else if (CommandLine[Pos] == '"')
- {
- IsInQuote = true;
- IsParsingArg = true;
- ArgStart = Pos;
- Pos++;
- }
- else if (CommandLine[Pos] != ' ')
- {
- IsParsingArg = true;
- ArgStart = Pos;
- Pos++;
- }
- else
- {
- Pos++;
- }
- }
- if (IsParsingArg)
- {
- ZEN_ASSERT(Pos > ArgStart);
- Args.push_back(std::string(CommandLine.substr(ArgStart)));
- }
-
- return Args;
-}
-
-std::vector<char*>
-StripCommandlineQuotes(std::vector<std::string>& InOutArgs)
-{
- std::vector<char*> RawArgs;
- RawArgs.reserve(InOutArgs.size());
- for (std::string& Arg : InOutArgs)
- {
- std::string::size_type EscapedQuotePos = Arg.find("\\\"", 1);
- while (EscapedQuotePos != std::string::npos && Arg.rfind('\"', EscapedQuotePos - 1) != std::string::npos)
- {
- Arg.erase(EscapedQuotePos, 1);
- EscapedQuotePos = Arg.find("\\\"", EscapedQuotePos);
- }
-
- if (Arg.starts_with("\""))
- {
- if (Arg.find('"', 1) == Arg.length() - 1)
- {
- if (Arg.find(' ', 1) == std::string::npos)
- {
- Arg = Arg.substr(1, Arg.length() - 2);
- }
- }
- }
- RawArgs.push_back(const_cast<char*>(Arg.c_str()));
- }
- return RawArgs;
-}
-
#if ZEN_WITH_TESTS
void
@@ -1235,36 +1123,6 @@ TEST_CASE("BuildArgV")
}
}
-TEST_CASE("CommandLine")
-{
- std::vector<std::string> v1 = ParseCommandLine("c:\\my\\exe.exe \"quoted arg\" \"one\",two,\"three\\\"");
- CHECK_EQ(v1[0], "c:\\my\\exe.exe");
- CHECK_EQ(v1[1], "\"quoted arg\"");
- CHECK_EQ(v1[2], "\"one\",two,\"three\\\"");
-
- std::vector<std::string> v2 = ParseCommandLine(
- "--tracehost 127.0.0.1 builds download --url=https://jupiter.devtools.epicgames.com --namespace=ue.oplog "
- "--bucket=citysample.packaged-build.fortnite-main.windows \"c:\\just\\a\\path\" "
- "--access-token-path=\"C:\\Users\\dan.engelbrecht\\jupiter-token.json\" \"D:\\Dev\\Spaced Folder\\Target\\\" "
- "--alt-path=\"D:\\Dev\\Spaced Folder2\\Target\\\" 07dn23ifiwesnvoasjncasab --build-part-name win64,linux,ps5");
-
- std::vector<char*> v2Stripped = StripCommandlineQuotes(v2);
- CHECK_EQ(v2Stripped[0], std::string("--tracehost"));
- CHECK_EQ(v2Stripped[1], std::string("127.0.0.1"));
- CHECK_EQ(v2Stripped[2], std::string("builds"));
- CHECK_EQ(v2Stripped[3], std::string("download"));
- CHECK_EQ(v2Stripped[4], std::string("--url=https://jupiter.devtools.epicgames.com"));
- CHECK_EQ(v2Stripped[5], std::string("--namespace=ue.oplog"));
- CHECK_EQ(v2Stripped[6], std::string("--bucket=citysample.packaged-build.fortnite-main.windows"));
- CHECK_EQ(v2Stripped[7], std::string("c:\\just\\a\\path"));
- CHECK_EQ(v2Stripped[8], std::string("--access-token-path=\"C:\\Users\\dan.engelbrecht\\jupiter-token.json\""));
- CHECK_EQ(v2Stripped[9], std::string("\"D:\\Dev\\Spaced Folder\\Target\""));
- CHECK_EQ(v2Stripped[10], std::string("--alt-path=\"D:\\Dev\\Spaced Folder2\\Target\""));
- CHECK_EQ(v2Stripped[11], std::string("07dn23ifiwesnvoasjncasab"));
- CHECK_EQ(v2Stripped[12], std::string("--build-part-name"));
- CHECK_EQ(v2Stripped[13], std::string("win64,linux,ps5"));
-}
-
TEST_SUITE_END(/* core.process */);
#endif
diff --git a/src/zenserver/config.cpp b/src/zenserver/config.cpp
index 31c104110..fecc1043a 100644
--- a/src/zenserver/config.cpp
+++ b/src/zenserver/config.cpp
@@ -15,6 +15,7 @@
#include <zencore/logging.h>
#include <zencore/string.h>
#include <zenhttp/zenhttp.h>
+#include <zenutil/commandlineoptions.h>
ZEN_THIRD_PARTY_INCLUDES_START
#include <fmt/format.h>
@@ -178,27 +179,6 @@ ParseBucketConfigs(std::span<std::string> Buckets)
return Cfg;
}
-static std::string
-MakeSafePath(const std::string_view Path)
-{
-#if ZEN_PLATFORM_WINDOWS
- if (Path.empty())
- {
- return std::string(Path);
- }
-
- std::string FixedPath(Path);
- std::replace(FixedPath.begin(), FixedPath.end(), '/', '\\');
- if (!FixedPath.starts_with("\\\\?\\"))
- {
- FixedPath.insert(0, "\\\\?\\");
- }
- return FixedPath;
-#else
- return std::string(Path);
-#endif
-};
-
class CachePolicyOption : public LuaConfig::OptionValue
{
public:
@@ -324,7 +304,7 @@ public:
std::string Name = Bucket.value().get_or("name", std::string("Default"));
std::string Directory = Bucket.value().get_or("directory", std::string());
- Value.Buckets.push_back({.Name = std::move(Name), .Directory = LuaConfig::MakeSafePath(Directory)});
+ Value.Buckets.push_back({.Name = std::move(Name), .Directory = MakeSafeAbsolutePath(Directory)});
}
}
}
@@ -525,7 +505,7 @@ ParseConfigFile(const std::filesystem::path& Path,
if (!OutputConfigFile.empty())
{
- std::filesystem::path WritePath(MakeSafePath(OutputConfigFile));
+ std::filesystem::path WritePath(MakeSafeAbsolutePath(OutputConfigFile));
zen::ExtendableStringBuilder<512> ConfigStringBuilder;
LuaOptions.Print(ConfigStringBuilder, CmdLineResult);
zen::BasicFile Output;
@@ -1110,12 +1090,12 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions)
}
logging::RefreshLogLevels();
- ServerOptions.SystemRootDir = MakeSafePath(SystemRootDir);
- ServerOptions.DataDir = MakeSafePath(DataDir);
- ServerOptions.BaseSnapshotDir = MakeSafePath(BaseSnapshotDir);
- ServerOptions.ContentDir = MakeSafePath(ContentDir);
- ServerOptions.AbsLogFile = MakeSafePath(AbsLogFile);
- ServerOptions.ConfigFile = MakeSafePath(ConfigFile);
+ ServerOptions.SystemRootDir = MakeSafeAbsolutePath(SystemRootDir);
+ ServerOptions.DataDir = MakeSafeAbsolutePath(DataDir);
+ ServerOptions.BaseSnapshotDir = MakeSafeAbsolutePath(BaseSnapshotDir);
+ ServerOptions.ContentDir = MakeSafeAbsolutePath(ContentDir);
+ ServerOptions.AbsLogFile = MakeSafeAbsolutePath(AbsLogFile);
+ ServerOptions.ConfigFile = MakeSafeAbsolutePath(ConfigFile);
ServerOptions.UpstreamCacheConfig.CachePolicy = ParseUpstreamCachePolicy(UpstreamCachePolicyOptions);
if (!BaseSnapshotDir.empty())
diff --git a/src/zenserver/config/luaconfig.cpp b/src/zenserver/config/luaconfig.cpp
index f742fa34a..2c54de29e 100644
--- a/src/zenserver/config/luaconfig.cpp
+++ b/src/zenserver/config/luaconfig.cpp
@@ -4,27 +4,6 @@
namespace zen::LuaConfig {
-std::string
-MakeSafePath(const std::string_view Path)
-{
-#if ZEN_PLATFORM_WINDOWS
- if (Path.empty())
- {
- return std::string(Path);
- }
-
- std::string FixedPath(Path);
- std::replace(FixedPath.begin(), FixedPath.end(), '/', '\\');
- if (!FixedPath.starts_with("\\\\?\\"))
- {
- FixedPath.insert(0, "\\\\?\\");
- }
- return FixedPath;
-#else
- return std::string(Path);
-#endif
-};
-
void
EscapeBackslash(std::string& InOutString)
{
@@ -101,7 +80,7 @@ FilePathOption::Parse(sol::object Object)
std::string Str = Object.as<std::string>();
if (!Str.empty())
{
- Value = MakeSafePath(Str);
+ Value = MakeSafeAbsolutePath(Str);
}
}
diff --git a/src/zenserver/config/luaconfig.h b/src/zenserver/config/luaconfig.h
index 76b3088a3..ce7013a9a 100644
--- a/src/zenserver/config/luaconfig.h
+++ b/src/zenserver/config/luaconfig.h
@@ -4,10 +4,10 @@
#include <zenbase/concepts.h>
#include <zencore/fmtutils.h>
+#include <zenutil/commandlineoptions.h>
ZEN_THIRD_PARTY_INCLUDES_START
#include <fmt/format.h>
-#include <cxxopts.hpp>
#include <sol/sol.hpp>
ZEN_THIRD_PARTY_INCLUDES_END
@@ -20,8 +20,7 @@ ZEN_THIRD_PARTY_INCLUDES_END
namespace zen::LuaConfig {
-std::string MakeSafePath(const std::string_view Path);
-void EscapeBackslash(std::string& InOutString);
+void EscapeBackslash(std::string& InOutString);
class OptionValue
{
diff --git a/src/zenutil/commandlineoptions.cpp b/src/zenutil/commandlineoptions.cpp
new file mode 100644
index 000000000..0dffa42f0
--- /dev/null
+++ b/src/zenutil/commandlineoptions.cpp
@@ -0,0 +1,213 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include <zenutil/commandlineoptions.h>
+
+#include <filesystem>
+#if ZEN_WITH_TESTS
+# include <zencore/testing.h>
+#endif // ZEN_WITH_TESTS
+
+void
+cxxopts::values::parse_value(const std::string& text, std::filesystem::path& value)
+{
+ value = zen::StringToPath(text);
+}
+
+namespace zen {
+
+std::vector<std::string>
+ParseCommandLine(std::string_view CommandLine)
+{
+ auto IsWhitespaceOrEnd = [](std::string_view CommandLine, std::string::size_type Pos) {
+ if (Pos == CommandLine.length())
+ {
+ return true;
+ }
+ if (CommandLine[Pos] == ' ')
+ {
+ return true;
+ }
+ return false;
+ };
+
+ bool IsParsingArg = false;
+ bool IsInQuote = false;
+
+ std::string::size_type Pos = 0;
+ std::string::size_type ArgStart = 0;
+ std::vector<std::string> Args;
+ while (Pos < CommandLine.length())
+ {
+ if (IsInQuote)
+ {
+ if (CommandLine[Pos] == '"' && IsWhitespaceOrEnd(CommandLine, Pos + 1))
+ {
+ Args.push_back(std::string(CommandLine.substr(ArgStart, Pos - ArgStart + 1)));
+ Pos++;
+ IsInQuote = false;
+ IsParsingArg = false;
+ }
+ else
+ {
+ Pos++;
+ }
+ }
+ else if (IsParsingArg)
+ {
+ ZEN_ASSERT(Pos > ArgStart);
+ if (CommandLine[Pos] == ' ')
+ {
+ Args.push_back(std::string(CommandLine.substr(ArgStart, Pos - ArgStart)));
+ Pos++;
+ IsParsingArg = false;
+ }
+ else if (CommandLine[Pos] == '"')
+ {
+ IsInQuote = true;
+ Pos++;
+ }
+ else
+ {
+ Pos++;
+ }
+ }
+ else if (CommandLine[Pos] == '"')
+ {
+ IsInQuote = true;
+ IsParsingArg = true;
+ ArgStart = Pos;
+ Pos++;
+ }
+ else if (CommandLine[Pos] != ' ')
+ {
+ IsParsingArg = true;
+ ArgStart = Pos;
+ Pos++;
+ }
+ else
+ {
+ Pos++;
+ }
+ }
+ if (IsParsingArg)
+ {
+ ZEN_ASSERT(Pos > ArgStart);
+ Args.push_back(std::string(CommandLine.substr(ArgStart)));
+ }
+
+ return Args;
+}
+
+std::vector<char*>
+StripCommandlineQuotes(std::vector<std::string>& InOutArgs)
+{
+ std::vector<char*> RawArgs;
+ RawArgs.reserve(InOutArgs.size());
+ for (std::string& Arg : InOutArgs)
+ {
+ std::string::size_type EscapedQuotePos = Arg.find("\\\"", 1);
+ while (EscapedQuotePos != std::string::npos && Arg.rfind('\"', EscapedQuotePos - 1) != std::string::npos)
+ {
+ Arg.erase(EscapedQuotePos, 1);
+ EscapedQuotePos = Arg.find("\\\"", EscapedQuotePos);
+ }
+
+ if (Arg.starts_with("\""))
+ {
+ if (Arg.find('"', 1) == Arg.length() - 1)
+ {
+ if (Arg.find(' ', 1) == std::string::npos)
+ {
+ Arg = Arg.substr(1, Arg.length() - 2);
+ }
+ }
+ }
+ RawArgs.push_back(const_cast<char*>(Arg.c_str()));
+ }
+ return RawArgs;
+}
+
+void
+MakeSafeAbsolutePathÍnPlace(std::filesystem::path& Path)
+{
+ if (!Path.empty())
+ {
+ std::filesystem::path AbsolutePath = std::filesystem::absolute(Path).make_preferred();
+#if ZEN_PLATFORM_WINDOWS
+ const std::string_view Prefix = "\\\\?\\";
+ const std::u8string PrefixU8(Prefix.begin(), Prefix.end());
+ std::u8string PathString = AbsolutePath.u8string();
+ if (!PathString.empty() && !PathString.starts_with(PrefixU8))
+ {
+ PathString.insert(0, PrefixU8);
+ Path = PathString;
+ }
+#endif // ZEN_PLATFORM_WINDOWS
+ }
+}
+
+std::filesystem::path
+MakeSafeAbsolutePath(const std::filesystem::path& Path)
+{
+ std::filesystem::path Tmp(Path);
+ MakeSafeAbsolutePathÍnPlace(Tmp);
+ return Tmp;
+}
+
+std::filesystem::path
+StringToPath(const std::string_view& Path)
+{
+ std::string_view UnquotedPath = Path;
+
+ if (UnquotedPath.length() > 2 && UnquotedPath.front() == '\"' && UnquotedPath.back() == '\"')
+ {
+ UnquotedPath = UnquotedPath.substr(1, UnquotedPath.length() - 2);
+ }
+
+ if (UnquotedPath.ends_with('/') || UnquotedPath.ends_with('\\') || UnquotedPath.ends_with(std::filesystem::path::preferred_separator))
+ {
+ UnquotedPath = UnquotedPath.substr(0, UnquotedPath.length() - 1);
+ }
+
+ return std::filesystem::path(UnquotedPath).make_preferred();
+}
+
+#if ZEN_WITH_TESTS
+
+void
+commandlineoptions_forcelink()
+{
+}
+
+TEST_CASE("CommandLine")
+{
+ std::vector<std::string> v1 = ParseCommandLine("c:\\my\\exe.exe \"quoted arg\" \"one\",two,\"three\\\"");
+ CHECK_EQ(v1[0], "c:\\my\\exe.exe");
+ CHECK_EQ(v1[1], "\"quoted arg\"");
+ CHECK_EQ(v1[2], "\"one\",two,\"three\\\"");
+
+ std::vector<std::string> v2 = ParseCommandLine(
+ "--tracehost 127.0.0.1 builds download --url=https://jupiter.devtools.epicgames.com --namespace=ue.oplog "
+ "--bucket=citysample.packaged-build.fortnite-main.windows \"c:\\just\\a\\path\" "
+ "--access-token-path=\"C:\\Users\\dan.engelbrecht\\jupiter-token.json\" \"D:\\Dev\\Spaced Folder\\Target\\\" "
+ "--alt-path=\"D:\\Dev\\Spaced Folder2\\Target\\\" 07dn23ifiwesnvoasjncasab --build-part-name win64,linux,ps5");
+
+ std::vector<char*> v2Stripped = StripCommandlineQuotes(v2);
+ CHECK_EQ(v2Stripped[0], std::string("--tracehost"));
+ CHECK_EQ(v2Stripped[1], std::string("127.0.0.1"));
+ CHECK_EQ(v2Stripped[2], std::string("builds"));
+ CHECK_EQ(v2Stripped[3], std::string("download"));
+ CHECK_EQ(v2Stripped[4], std::string("--url=https://jupiter.devtools.epicgames.com"));
+ CHECK_EQ(v2Stripped[5], std::string("--namespace=ue.oplog"));
+ CHECK_EQ(v2Stripped[6], std::string("--bucket=citysample.packaged-build.fortnite-main.windows"));
+ CHECK_EQ(v2Stripped[7], std::string("c:\\just\\a\\path"));
+ CHECK_EQ(v2Stripped[8], std::string("--access-token-path=\"C:\\Users\\dan.engelbrecht\\jupiter-token.json\""));
+ CHECK_EQ(v2Stripped[9], std::string("\"D:\\Dev\\Spaced Folder\\Target\""));
+ CHECK_EQ(v2Stripped[10], std::string("--alt-path=\"D:\\Dev\\Spaced Folder2\\Target\""));
+ CHECK_EQ(v2Stripped[11], std::string("07dn23ifiwesnvoasjncasab"));
+ CHECK_EQ(v2Stripped[12], std::string("--build-part-name"));
+ CHECK_EQ(v2Stripped[13], std::string("win64,linux,ps5"));
+}
+
+#endif
+} // namespace zen
diff --git a/src/zenutil/include/zenutil/commandlineoptions.h b/src/zenutil/include/zenutil/commandlineoptions.h
new file mode 100644
index 000000000..3afbac854
--- /dev/null
+++ b/src/zenutil/include/zenutil/commandlineoptions.h
@@ -0,0 +1,28 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include <zencore/zencore.h>
+#include <filesystem>
+
+ZEN_THIRD_PARTY_INCLUDES_START
+
+namespace cxxopts::values {
+// We declare this specialization before including cxxopts to make it stick
+void parse_value(const std::string& text, std::filesystem::path& value);
+} // namespace cxxopts::values
+
+#include <cxxopts.hpp>
+ZEN_THIRD_PARTY_INCLUDES_END
+
+namespace zen {
+
+std::vector<std::string> ParseCommandLine(std::string_view CommandLine);
+std::vector<char*> StripCommandlineQuotes(std::vector<std::string>& InOutArgs);
+void MakeSafeAbsolutePathÍnPlace(std::filesystem::path& Path);
+std::filesystem::path MakeSafeAbsolutePath(const std::filesystem::path& Path);
+std::filesystem::path StringToPath(const std::string_view& Path);
+
+void commandlineoptions_forcelink(); // internal
+
+} // namespace zen
diff --git a/src/zenutil/zenutil.cpp b/src/zenutil/zenutil.cpp
index 19eb63ce9..aff9156f4 100644
--- a/src/zenutil/zenutil.cpp
+++ b/src/zenutil/zenutil.cpp
@@ -7,6 +7,7 @@
# include <zenutil/cache/cacherequests.h>
# include <zenutil/cache/rpcrecording.h>
# include <zenutil/chunkedfile.h>
+# include <zenutil/commandlineoptions.h>
namespace zen {
@@ -17,6 +18,7 @@ zenutil_forcelinktests()
cache::rpcrecord_forcelink();
cacherequests_forcelink();
chunkedfile_forcelink();
+ commandlineoptions_forcelink();
}
} // namespace zen