diff options
| author | Dan Engelbrecht <[email protected]> | 2025-04-22 16:28:08 +0200 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2025-04-22 16:28:08 +0200 |
| commit | 732a1cb1e78abbabaa0d926e9b1e58a36538dc1b (patch) | |
| tree | 08d47d333f68e2f99ec7ec922fa72dea4ee96dbc /src | |
| parent | xmake updatefrontend (diff) | |
| download | zen-732a1cb1e78abbabaa0d926e9b1e58a36538dc1b.tar.xz zen-732a1cb1e78abbabaa0d926e9b1e58a36538dc1b.zip | |
add cxxopts overload for parsing file paths from command line (#362)
Diffstat (limited to 'src')
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 |