ab5c468ace
canon new lib|exe creates a project following P1204R0 (Canonical Project Structure) with a .canon.toml manifest and a CMakeLists.txt whose managed block is rendered from that manifest. Generators return a plan; the executor checks every operation before writing, so conflicts write nothing and --dry-run writes nothing. Tests: unit tests next to each source file, golden copies of generated projects, and an end-to-end build of both project kinds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
99 lines
4.1 KiB
C++
99 lines
4.1 KiB
C++
#include <canon/cmake.hpp>
|
|
|
|
#include <canon/details/testing.hpp>
|
|
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <string_view>
|
|
|
|
using namespace canon;
|
|
|
|
namespace {
|
|
|
|
std::size_t count(std::string_view haystack, std::string_view needle) {
|
|
std::size_t n = 0;
|
|
for (auto at = haystack.find(needle); at != std::string_view::npos;
|
|
at = haystack.find(needle, at + needle.size())) {
|
|
++n;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
manifest library() {
|
|
manifest m{.project = {project_kind::library, "libhello", "hello", "hello"}};
|
|
m.targets.push_back(
|
|
{.name = "libhello", .type = target_type::library, .sources = {"hello/hello.cpp"}});
|
|
m.modules.push_back({.path = "hello/hello", .target = "libhello", .unit_test = true});
|
|
m.tests.push_back(
|
|
{.name = "basics", .kind = test_kind::driver, .path = "tests/basics", .target = "libhello"});
|
|
return m;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
{
|
|
const std::string block = render_managed_block(library());
|
|
CANON_CHECK(block.starts_with(managed_begin));
|
|
CANON_CHECK(block.ends_with(std::string(managed_end) + "\n"));
|
|
CANON_CHECK_EQ(count(block, managed_begin), 1u);
|
|
CANON_CHECK_EQ(count(block, managed_end), 1u);
|
|
CANON_CHECK(block.contains("add_library(libhello\n hello/hello.cpp\n)\n"));
|
|
CANON_CHECK(block.contains("set_target_properties(libhello PROPERTIES PREFIX \"\")\n"));
|
|
CANON_CHECK(block.contains(
|
|
"target_include_directories(libhello PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})\n"));
|
|
CANON_CHECK(block.contains("target_compile_features(libhello PUBLIC cxx_std_23)\n"));
|
|
CANON_CHECK(block.contains(" add_executable(hello.hello.test hello/hello.test.cpp)\n"
|
|
" target_link_libraries(hello.hello.test PRIVATE libhello)\n"
|
|
" add_test(NAME hello/hello.test COMMAND hello.hello.test)\n"));
|
|
CANON_CHECK(block.contains(" add_executable(tests.basics tests/basics/driver.cpp)\n"
|
|
" target_link_libraries(tests.basics PRIVATE libhello)\n"
|
|
" add_test(NAME basics COMMAND tests.basics)\n"));
|
|
|
|
// Rendering is a pure function of the manifest.
|
|
CANON_CHECK_EQ(render_managed_block(library()), block);
|
|
}
|
|
|
|
{
|
|
manifest m{.project = {project_kind::executable, "tool", "tool", "tool"}};
|
|
m.targets.push_back({.name = "tool",
|
|
.type = target_type::executable,
|
|
.sources = {"tool/tool.cpp", "tool/greet.cpp"}});
|
|
m.modules.push_back({.path = "tool/greet", .target = "tool", .unit_test = true});
|
|
m.tests.push_back(
|
|
{.name = "run", .kind = test_kind::run, .target = "tool", .expect = "Hi \"$x\";"});
|
|
|
|
const std::string block = render_managed_block(m);
|
|
CANON_CHECK(block.contains("add_executable(tool\n"));
|
|
CANON_CHECK(!block.contains("PREFIX"));
|
|
CANON_CHECK(block.contains("target_include_directories(tool PRIVATE"));
|
|
CANON_CHECK(block.contains(" add_executable(tool.greet.test tool/greet.test.cpp tool/greet.cpp)\n"));
|
|
CANON_CHECK(block.contains(" add_test(NAME run COMMAND tool)\n"));
|
|
CANON_CHECK(block.contains(
|
|
R"( set_tests_properties(run PROPERTIES PASS_REGULAR_EXPRESSION "Hi \"\$x\"\;"))"));
|
|
}
|
|
|
|
{
|
|
manifest m = library();
|
|
m.targets.clear();
|
|
m.targets.push_back({.name = "libother", .type = target_type::library});
|
|
bool threw = false;
|
|
try {
|
|
(void)render_managed_block(m);
|
|
} catch (const std::invalid_argument& e) {
|
|
threw = std::string_view(e.what()).contains("libhello");
|
|
}
|
|
CANON_CHECK(threw);
|
|
}
|
|
|
|
{
|
|
const std::string file = render_cmakelists(library());
|
|
CANON_CHECK(file.starts_with("cmake_minimum_required(VERSION 3.20)\n"
|
|
"project(libhello VERSION 0.1.0 LANGUAGES CXX)\n"));
|
|
CANON_CHECK(file.contains("set(CMAKE_CXX_STANDARD 23)\n"));
|
|
CANON_CHECK(file.ends_with(render_managed_block(library())));
|
|
}
|
|
|
|
return details::exit_status();
|
|
}
|