diff options
| author | Leitwolf11 | 2026-11-01 22:16:14 +0300 |
|---|---|---|
| committer | Leitwolf11 | 2026-11-01 22:16:14 +0300 |
| commit | da3c9d0880dbd4a74e0e43f092d10f5ae42c5043 (patch) | |
| tree | 4676a526585dbb0e2f6d8c9c18978ed01092c2e2 /tools/Versions.cpp | |
| parent | 241b5567d09d59e82bff18287c32ada480deb8c0 (diff) | |
| download | catch3-da3c9d0880dbd4a74e0e43f092d10f5ae42c5043.tar.gz | |
Move the tools from Python to C++17 and Bazel
The scripts in use become programs run with `bazelisk run //tools:<tool>`,
and arguments follow `--`:
amalgamate the single-file distribution; --check only compares it
check licenses, convenience headers, header guards, file names,
versions and whether the distribution is up to date
third-party compares third_party/ with its archives, or writes it
version prints the version, or moves every place that states it
release-notes prints a section of the release notes
repo-info reports the checkout and the tools on PATH; --json writes
it through yyjson, with the fields of Tala's repo-info
amalgamate writes what the Python script wrote, byte for byte, except for
the time of generation. That line is gone so that the output is
deterministic and --check can compare it.
MODULE.bazel now pins the archives of third_party/ by version and sha256,
so Bazel fetches and checks them instead of a tool of ours, and Renovate
reads the versions there.
Scripts nobody used are gone: fixWhitespace, updateDocumentSnippets,
buildAndTest, and updateDocumentToC, which rewrote unrelated documents.
CheckConvenienceHeaders leaves CTest, since //tools:check runs it in CI.
The Python of tests/TestScripts stays: those are tests, not tools.
Diffstat (limited to 'tools/Versions.cpp')
| -rw-r--r-- | tools/Versions.cpp | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/tools/Versions.cpp b/tools/Versions.cpp new file mode 100644 index 00000000..73a2d663 --- /dev/null +++ b/tools/Versions.cpp @@ -0,0 +1,205 @@ +// Copyright (c) 2026 Progmasoft. +// SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-2.0 + +#include <algorithm> +#include <cctype> +#include <charconv> + +#include "tools/Versions.hpp" + +namespace Progmasoft::Catch3::Tools +{ + namespace + { + constexpr std::string_view kVersionHeader + = "src/Progmasoft/Catch3/Version.hpp"; + } // namespace + + std::optional<Version> + ParseVersion(std::string_view text) + { + Version version{}; + std::size_t count = 0; + while (true) + { + const auto dot = text.find('.'); + const auto number = text.substr(0, dot); + if (count == version.size() || number.empty() + || (number.size() > 1 && number.front() == '0')) + { + return std::nullopt; + } + const auto *end = number.data() + number.size(); + const auto parsed + = std::from_chars(number.data(), end, version[count]); + if (parsed.ec != std::errc{} || parsed.ptr != end) + { + return std::nullopt; + } + ++count; + if (dot == std::string_view::npos) + { + break; + } + text.remove_prefix(dot + 1); + } + if (count < 3) + { + return std::nullopt; + } + return version; + } + + std::string + FormatVersion(const Version &version) + { + return std::to_string(version[0]) + "." + std::to_string(version[1]) + + "." + std::to_string(version[2]) + "." + + std::to_string(version[3]); + } + + Version + NextVersion(Version version) + { + if (version[3] < 9) + { + ++version[3]; + return version; + } + version[3] = 0; + if (version[2] < 9) + { + ++version[2]; + return version; + } + version[2] = 0; + if (version[1] < 19) + { + ++version[1]; + return version; + } + version[1] = 0; + ++version[0]; + return version; + } + + const std::array<VersionSite, 5> & + VersionSites() + { + static constexpr std::array<VersionSite, 5> kSites{ { + { "CMakeLists.txt", "\n VERSION ", "\n" }, + { "MODULE.bazel", "module(name = \"catch3\", version = \"", "\"" }, + { "meson.build", "\n version: '", "'" }, + { "xmake.lua", "\nset_version(\"", "\"" }, + { kVersionHeader, "kVersion = \"", "\"" }, + } }; + return kSites; + } + + std::optional<std::string> + ReadVersionSite(std::string_view text, const VersionSite &site) + { + const auto before = text.find(site.Before); + if (before == std::string_view::npos) + { + return std::nullopt; + } + const auto first = before + site.Before.size(); + const auto end = text.find(site.After, first); + if (end == std::string_view::npos) + { + return std::nullopt; + } + return std::string{ text.substr(first, end - first) }; + } + + std::optional<std::string> + WriteVersionSite(std::string_view text, + const VersionSite &site, + std::string_view version) + { + const auto current = ReadVersionSite(text, site); + if (!current) + { + return std::nullopt; + } + const auto first = text.find(site.Before) + site.Before.size(); + std::string result{ text.substr(0, first) }; + result += version; + result += text.substr(first + current->size()); + return result; + } + + std::optional<std::string> + CurrentVersion(const std::filesystem::path &root, Problems &problems) + { + const auto &sites = VersionSites(); + const auto &site + = *std::find_if(sites.begin(), + sites.end(), + [](const VersionSite &candidate) { + return candidate.File == kVersionHeader; + }); + auto version + = ReadVersionSite(ReadOrReport(root / site.File, problems), site); + if (!version) + { + problems.emplace_back("no version in Version.hpp"); + } + return version; + } + + void + CheckVersions(const std::filesystem::path &root, Problems &problems) + { + const auto current = CurrentVersion(root, problems); + if (!current) + { + return; + } + for (const auto &site : VersionSites()) + { + const auto version + = ReadVersionSite(ReadOrReport(root / site.File, problems), + site); + if (version != current) + { + problems.push_back(std::string{ site.File } + " states " + + version.value_or("no version") + + ", Version.hpp states " + *current); + } + } + } + + void + SetVersion(const std::filesystem::path &root, + const std::string &version, + Problems &problems) + { + for (const auto &site : VersionSites()) + { + const auto path = root / site.File; + const auto text + = WriteVersionSite(ReadOrReport(path, problems), site, version); + if (!text || !WriteText(path, *text)) + { + problems.push_back("cannot set the version in " + + std::string{ site.File }); + } + } + } + + std::optional<std::string> + ReleaseNotesOf(std::string_view notes, std::string_view version) + { + const auto heading = "\n## " + std::string{ version } + "\n"; + const auto found = notes.find(heading); + if (found == std::string_view::npos) + { + return std::nullopt; + } + auto section = notes.substr(found + heading.size()); + section = section.substr(0, section.find("\n## ")); + return std::string{ Trimmed(section) } + "\n"; + } +} // namespace Progmasoft::Catch3::Tools |