[M1] add layout::discover
This commit is contained in:
142
src/layout/layout.cpp
Normal file
142
src/layout/layout.cpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user