// SPDX-License-Identifier: LGPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® import std; import Crafter.Build; namespace fs = std::filesystem; using namespace Crafter; // `crafter-build clean` exists so the fix for a stale build is discoverable // rather than folklore. It must not need the project to load: the state you most // want to clear is one where the build is already broken. namespace { std::int32_t Failures = 0; void Check(bool cond, std::string_view msg) { if (!cond) { std::println(std::cerr, "FAIL: {}", msg); ++Failures; } } fs::path StageProject(std::string_view leaf) { fs::path root = fs::temp_directory_path() / std::format("crafter-build-clean-{}", leaf); fs::remove_all(root); fs::create_directories(root / "bin" / "app-x86_64-pc-linux-gnu-native-native-0badf00d"); fs::create_directories(root / "build" / "app-x86_64-pc-linux-gnu-native-native-0badf00d"); std::ofstream(root / "bin" / "app-x86_64-pc-linux-gnu-native-native-0badf00d" / "app") << "binary"; std::ofstream(root / "build" / "app-x86_64-pc-linux-gnu-native-native-0badf00d" / "main_impl.o") << "object"; std::ofstream(root / "keep-me.txt") << "untouched"; return root; } } int main() { { // A project file that could never compile: clean has to work anyway. fs::path root = StageProject("broken"); fs::path projectFile = root / "project.cpp"; std::ofstream(projectFile) << "this is not valid C++ at all\n"; std::vector removed = CleanProject(projectFile); Check(removed.size() == 2, std::format("both trees reported removed, got {}", removed.size())); Check(!fs::exists(root / "bin"), "bin/ is gone"); Check(!fs::exists(root / "build"), "build/ is gone"); Check(fs::exists(root / "keep-me.txt"), "unrelated files in the project root are left alone"); Check(fs::exists(projectFile), "the project file itself is left alone"); } { // Idempotent, and quiet about it — nothing to remove is not an error. fs::path root = StageProject("twice"); fs::path projectFile = root / "project.cpp"; std::ofstream(projectFile) << "\n"; CleanProject(projectFile); std::vector second = CleanProject(projectFile); Check(second.empty(), "a second clean reports nothing removed"); } { // Resolved relative to the project file, not the cwd, so --project= // cleans the project it names. fs::path root = StageProject("elsewhere"); fs::path projectFile = root / "project.cpp"; std::ofstream(projectFile) << "\n"; fs::path cwdBin = fs::current_path() / "bin"; bool cwdBinExisted = fs::exists(cwdBin); CleanProject(projectFile); Check(!fs::exists(root / "bin"), "the named project's bin/ is gone"); Check(fs::exists(cwdBin) == cwdBinExisted, "the cwd's bin/ is untouched"); } if (Failures > 0) { std::println(std::cerr, "{} assertions failed", Failures); return 1; } return 0; }