// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® import std; import Crafter.Build; // Pinned: the Flatpak builds with the base app's crafter-build, which can be // older than the rules' master. Move the pin together with the base. #pragma crafter dependency "https://forgejo.catcrafts.net/Catcrafts/Crafter.LintRules.git" commit "7d932892017800b4e1f1a15fe7aa49b3daee9ceb" import Crafter.LintRules; namespace fs = std::filesystem; using namespace Crafter; namespace { // The umbrella, then the partitions in import order: each after the ones // it imports, which is also the order the module map in AGENTS.md uses. constexpr std::array Interfaces = { "interfaces/Crafter.Review", "interfaces/Crafter.Review-Unicode", "interfaces/Crafter.Review-Json", "interfaces/Crafter.Review-Text", "interfaces/Crafter.Review-Crypto", "interfaces/Crafter.Review-Core", "interfaces/Crafter.Review-Driver", }; constexpr std::array Implementations = { "implementations/Crafter.Review-Text", "implementations/Crafter.Review-Crypto", "implementations/Crafter.Review-Core", "implementations/Crafter.Review-Driver", }; // One per directory under tests/, each an ordinary main() that links the // library: exit 0 passes, nonzero fails, 77 skips. constexpr std::array Tests = { "Crypto", "Text", }; // The version the binary reports: CI sets it from the tag, a checkout // describes itself, anything else is a development build. A define on // the library, so each version is its own library variant. std::string Version() { if (const char* env = std::getenv("CRAFTER_REVIEW_VERSION"); env && *env) return env; auto described = RunCommandWithTimeout("git describe --tags --always --dirty --exclude=latest 2>/dev/null", std::chrono::seconds(10)); std::string version = described.output; while (!version.empty() && (version.back() == '\n' || version.back() == '\r')) version.pop_back(); if (described.exitCode != 0 || version.empty()) return "0.0.0-dev"; return version; } } extern "C" Configuration CrafterBuildProject(std::span args) { // Crafter.Network for its TCP and HTTP/1.1 clients and listener, without // QUIC: msquic is most of a fresh build and nothing here speaks HTTP/3. // From forgejo at a pinned commit, or with --local from the checkout next // to this one (which is also how Crafter.Network then finds // Crafter.Thread). std::vector depArgs(args.begin(), args.end()); depArgs.emplace_back("--no-quic"); const bool useLocal = std::ranges::find(args, std::string_view("--local")) != args.end(); Configuration* network = useLocal ? LocalProject({ .projectFile = "../Crafter.Network/project.cpp", .args = depArgs }) : GitProject({ .source = { .url = "https://forgejo.catcrafts.net/Catcrafts/Crafter.Network.git", .commit = "f68321685006be65f6bc0436fd0175611f1e0a55" }, .args = depArgs, }); static auto ReviewLib = std::make_unique(); ReviewLib->path = "./"; ReviewLib->name = "crafter.review-lib"; ReviewLib->outputName = "crafter-review"; ApplyStandardArgs(*ReviewLib, args); ReviewLib->type = ConfigurationType::LibraryStatic; ReviewLib->dependencies = { network }; // Backslash-escaped quotes: the define travels through the shell before // it reaches clang, which must still see a string literal. ReviewLib->defines.push_back({"CRAFTER_REVIEW_VERSION", std::format("\\\"{}\\\"", Version())}); { std::vector interfaces(Interfaces.begin(), Interfaces.end()); std::vector implementations(Implementations.begin(), Implementations.end()); ReviewLib->GetInterfacesAndImplementations(interfaces, implementations); } Configuration cfg; cfg.path = "./"; cfg.name = "crafter.review-exe"; cfg.outputName = "crafter-review"; ApplyStandardArgs(cfg, args); cfg.type = ConfigurationType::Executable; cfg.dependencies = { ReviewLib.get() }; { std::array interfaces = {}; std::array implementations = { "implementations/main" }; cfg.GetInterfacesAndImplementations(interfaces, implementations); } for (const std::string_view test : Tests) { cfg.AddTest(test).Dependencies({ ReviewLib.get() }); } LintRules::AddLiteRules(cfg); return cfg; }