[M1] add layout::discover

This commit is contained in:
2026-05-08 11:03:44 +00:00
parent b0c54b8f5a
commit 3dc082147a
6 changed files with 373 additions and 0 deletions

142
src/layout/layout.cpp Normal file
View File

@@ -0,0 +1,142 @@
module cargoxx.layout;
import std;
import cargoxx.util;
namespace cargoxx::layout {
namespace {
namespace fs = std::filesystem;
auto top_level_cpp(const fs::path& dir) -> std::vector<fs::path> {
std::vector<fs::path> out;
std::error_code ec;
if (!fs::exists(dir, ec) || ec) {
return out;
}
for (const auto& entry : fs::directory_iterator{dir}) {
if (entry.is_regular_file() && entry.path().extension() == ".cpp") {
out.push_back(entry.path());
}
}
std::ranges::sort(out);
return out;
}
void walk_library_sources(const fs::path& src_dir, const fs::path& lib_path,
const fs::path& main_path, std::vector<fs::path>& cppm_out,
std::vector<fs::path>& cpp_out) {
std::error_code ec;
if (!fs::exists(src_dir, ec) || ec) {
return;
}
for (const auto& entry : fs::recursive_directory_iterator{src_dir}) {
if (!entry.is_regular_file()) {
continue;
}
const auto& p = entry.path();
// Exclude the bin/ subtree — it holds extra binary roots, not library sources.
auto rel = fs::relative(p, src_dir);
if (!rel.empty() && rel.begin()->string() == "bin") {
continue;
}
if (p == lib_path || p == main_path) {
continue;
}
const auto ext = p.extension();
if (ext == ".cppm") {
cppm_out.push_back(p);
} else if (ext == ".cpp") {
cpp_out.push_back(p);
}
}
}
auto by_name(const Target& a, const Target& b) -> bool {
return a.name < b.name;
}
} // namespace
auto discover(const fs::path& project_root, const std::string& package_name)
-> util::Result<DiscoveredLayout> {
const auto src_dir = project_root / "src";
const auto lib_path = src_dir / "lib.cppm";
const auto main_path = src_dir / "main.cpp";
const auto bin_dir = src_dir / "bin";
const auto tests_dir = project_root / "tests";
const auto examples_dir = project_root / "examples";
DiscoveredLayout out;
std::error_code ec;
if (fs::exists(lib_path, ec) && !ec) {
std::vector<fs::path> cppm{lib_path};
std::vector<fs::path> cpp;
walk_library_sources(src_dir, lib_path, main_path, cppm, cpp);
std::ranges::sort(cppm);
std::ranges::sort(cpp);
out.library = Target{
.kind = TargetKind::Library,
.name = package_name,
.entry = lib_path,
.additional_sources = std::move(cpp),
.module_units = std::move(cppm),
};
}
if (fs::exists(main_path, ec) && !ec) {
out.binaries.push_back(Target{
.kind = TargetKind::Binary,
.name = package_name,
.entry = main_path,
.additional_sources = {},
.module_units = {},
});
}
for (const auto& f : top_level_cpp(bin_dir)) {
out.binaries.push_back(Target{
.kind = TargetKind::Binary,
.name = f.stem().string(),
.entry = f,
.additional_sources = {},
.module_units = {},
});
}
std::ranges::sort(out.binaries, by_name);
for (const auto& f : top_level_cpp(tests_dir)) {
out.tests.push_back(Target{
.kind = TargetKind::Test,
.name = f.stem().string(),
.entry = f,
.additional_sources = {},
.module_units = {},
});
}
for (const auto& f : top_level_cpp(examples_dir)) {
out.examples.push_back(Target{
.kind = TargetKind::Example,
.name = f.stem().string(),
.entry = f,
.additional_sources = {},
.module_units = {},
});
}
if (!out.library && out.binaries.empty()) {
return std::unexpected(util::Error{
util::ErrorCode::LayoutNoTarget,
"no target found",
"expected one of: src/main.cpp, src/lib.cppm",
project_root,
std::nullopt,
});
}
return out;
}
} // namespace cargoxx::layout

View File

@@ -1,3 +1,32 @@
export module cargoxx.layout;
import std;
import cargoxx.util;
export namespace cargoxx::layout {
enum class TargetKind { Library, Binary, Test, Example };
struct Target {
TargetKind kind;
std::string name;
std::filesystem::path entry;
std::vector<std::filesystem::path> additional_sources;
std::vector<std::filesystem::path> module_units;
bool operator==(const Target&) const = default;
};
struct DiscoveredLayout {
std::optional<Target> library;
std::vector<Target> binaries;
std::vector<Target> tests;
std::vector<Target> examples;
bool operator==(const DiscoveredLayout&) const = default;
};
auto discover(const std::filesystem::path& project_root, const std::string& package_name)
-> util::Result<DiscoveredLayout>;
} // namespace cargoxx::layout