Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/tools/Versions.cpp
diff options
context:
space:
mode:
authorLeitwolf112026-11-01 22:16:14 +0300
committerLeitwolf112026-11-01 22:16:14 +0300
commitda3c9d0880dbd4a74e0e43f092d10f5ae42c5043 (patch)
tree4676a526585dbb0e2f6d8c9c18978ed01092c2e2 /tools/Versions.cpp
parent241b5567d09d59e82bff18287c32ada480deb8c0 (diff)
downloadcatch3-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.cpp205
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