#include <dds/compile.hpp> | #include <dds/compile.hpp> | ||||
#include <dds/logging.hpp> | #include <dds/logging.hpp> | ||||
#include <dds/proc.hpp> | #include <dds/proc.hpp> | ||||
#include <dds/project.hpp> | |||||
#include <dds/source.hpp> | #include <dds/source.hpp> | ||||
#include <dds/toolchain.hpp> | #include <dds/toolchain.hpp> | ||||
#include <libman/index.hpp> | #include <libman/index.hpp> | ||||
#include <libman/parse.hpp> | #include <libman/parse.hpp> | ||||
#include <range/v3/range/conversion.hpp> | |||||
#include <range/v3/view/filter.hpp> | |||||
#include <range/v3/view/transform.hpp> | |||||
#include <algorithm> | #include <algorithm> | ||||
#include <chrono> | #include <chrono> | ||||
#include <iomanip> | #include <iomanip> | ||||
} | } | ||||
dds::compilation_set collect_compiles(const build_params& params, const library_manifest& man) { | dds::compilation_set collect_compiles(const build_params& params, const library_manifest& man) { | ||||
source_list sources = source_file::collect_pf_sources(params.root); | |||||
const bool need_compile_deps = params.build_tests || params.build_apps || params.build_deps; | |||||
auto project = project::from_directory(params.root); | |||||
assert(project.main_library()); | |||||
const auto& sources = project.main_library()->sources(); | |||||
const bool need_compile_deps = params.build_tests || params.build_apps || params.build_deps; | |||||
std::vector<fs::path> dep_includes; | std::vector<fs::path> dep_includes; | ||||
std::vector<std::string> dep_defines; | std::vector<std::string> dep_defines; | ||||
compiles.execute_all(params.toolchain, params.parallel_jobs); | compiles.execute_all(params.toolchain, params.parallel_jobs); | ||||
source_list sources = source_file::collect_pf_sources(params.root); | |||||
using namespace ranges::view; | |||||
source_list sources = compiles.compilations // | |||||
| transform(DDS_TL(_1.source)) // | |||||
| ranges::to_vector; | |||||
archive_spec arc; | archive_spec arc; | ||||
for (const auto& comp : compiles.compilations) { | |||||
if (comp.source.kind == source_kind::source) { | |||||
arc.input_files.push_back(comp.obj); | |||||
} | |||||
} | |||||
// Collect object files that make up that library | |||||
arc.input_files = compiles.compilations | |||||
// Only library sources | |||||
| filter(DDS_TL(_1.source.kind == source_kind::source)) // | |||||
| transform(DDS_TL(_1.obj)) // | |||||
| ranges::to_vector; | |||||
arc.out_path = params.out_root | arc.out_path = params.out_root | ||||
/ (fmt::format("lib{}{}", params.export_name, params.toolchain.archive_suffix())); | / (fmt::format("lib{}{}", params.export_name, params.toolchain.archive_suffix())); |
if (!compile_res.okay()) { | if (!compile_res.okay()) { | ||||
spdlog::error("Compilation failed: {}", source.path.string()); | spdlog::error("Compilation failed: {}", source.path.string()); | ||||
spdlog::error("Subcommand FAILED: {}\n{}", quote_command(cmd), compile_res.output); | spdlog::error("Subcommand FAILED: {}\n{}", quote_command(cmd), compile_res.output); | ||||
throw compile_failure("Compilation failed."); | |||||
throw compile_failure(fmt::format("Compilation failed for {}.", source.path.string())); | |||||
} | } | ||||
// MSVC prints the filename of the source file. Dunno why, but they do. | // MSVC prints the filename of the source file. Dunno why, but they do. |
#include <dds/library.hpp> | |||||
#include <spdlog/spdlog.h> | |||||
using namespace dds; | |||||
namespace { | |||||
struct pf_info { | |||||
source_list sources; | |||||
fs::path inc_dir; | |||||
fs::path src_dir; | |||||
}; | |||||
pf_info collect_pf_sources(path_ref path) { | |||||
auto include_dir = path / "include"; | |||||
auto src_dir = path / "src"; | |||||
source_list sources; | |||||
if (fs::exists(include_dir)) { | |||||
if (!fs::is_directory(include_dir)) { | |||||
throw std::runtime_error("The `include` at the root of the project is not a directory"); | |||||
} | |||||
auto inc_sources = source_file::collect_for_dir(include_dir); | |||||
// Drop any source files we found within `include/` | |||||
erase_if(sources, [&](auto& info) { | |||||
if (info.kind != source_kind::header) { | |||||
spdlog::warn("Source file in `include` will not be compiled: {}", | |||||
info.path.string()); | |||||
return true; | |||||
} | |||||
return false; | |||||
}); | |||||
extend(sources, inc_sources); | |||||
} | |||||
if (fs::exists(src_dir)) { | |||||
if (!fs::is_directory(src_dir)) { | |||||
throw std::runtime_error("The `src` at the root of the project is not a directory"); | |||||
} | |||||
auto src_sources = source_file::collect_for_dir(src_dir); | |||||
extend(sources, src_sources); | |||||
} | |||||
return {std::move(sources), include_dir, src_dir}; | |||||
} | |||||
} // namespace | |||||
library library::from_directory(path_ref lib_dir, std::string_view name) { | |||||
auto [sources, inc_dir, src_dir] = collect_pf_sources(lib_dir); | |||||
auto lib = library(name, std::move(sources));; | |||||
if (fs::exists(inc_dir)) { | |||||
lib._pub_inc_dir = inc_dir; | |||||
if (fs::exists(src_dir)) { | |||||
lib._priv_inc_dir = src_dir; | |||||
} | |||||
} else { | |||||
lib._pub_inc_dir = src_dir; | |||||
lib._priv_inc_dir = src_dir; | |||||
} | |||||
return lib; | |||||
} |
#pragma once | |||||
#include <dds/source.hpp> | |||||
#include <optional> | |||||
#include <string> | |||||
namespace dds { | |||||
struct library_ident { | |||||
std::string namespace_; | |||||
std::string name; | |||||
}; | |||||
class library { | |||||
std::string _name; | |||||
source_list _sources; | |||||
fs::path _pub_inc_dir; | |||||
fs::path _priv_inc_dir; | |||||
library(std::string_view name, source_list&& src) | |||||
: _name(name) | |||||
, _sources(std::move(src)) {} | |||||
public: | |||||
static library from_directory(path_ref, std::string_view name); | |||||
static library from_directory(path_ref path) { | |||||
return from_directory(path, path.filename().string()); | |||||
} | |||||
path_ref public_include_dir() const noexcept { return _pub_inc_dir; } | |||||
path_ref private_include_dir() const noexcept { return _priv_inc_dir; } | |||||
const source_list& sources() const noexcept { return _sources; } | |||||
}; | |||||
} // namespace dds |
#include <dds/project.hpp> | |||||
#include <dds/source.hpp> | |||||
#include <range/v3/range/conversion.hpp> | |||||
#include <range/v3/view/filter.hpp> | |||||
#include <range/v3/view/transform.hpp> | |||||
using namespace dds; | |||||
namespace { | |||||
bool has_library_dirs(path_ref p) { return fs::exists(p / "src") || fs::exists(p / "include"); } | |||||
std::vector<library> collect_submodules(path_ref pf_libs_dir) { | |||||
if (!fs::exists(pf_libs_dir)) { | |||||
return {}; | |||||
} | |||||
using namespace ranges::view; | |||||
return fs::directory_iterator(pf_libs_dir) // | |||||
| filter(has_library_dirs) // | |||||
| transform(DDS_TL(library::from_directory(_1))) // | |||||
| ranges::to_vector; | |||||
} | |||||
} // namespace | |||||
project project::from_directory(path_ref pf_dir_path) { | |||||
std::optional<library> main_lib; | |||||
if (has_library_dirs(pf_dir_path)) { | |||||
main_lib = library::from_directory(pf_dir_path); | |||||
} | |||||
return project(std::move(main_lib), collect_submodules(pf_dir_path / "libs")); | |||||
} |
#pragma once | |||||
#include <dds/library.hpp> | |||||
#include <dds/util.hpp> | |||||
#include <optional> | |||||
#include <vector> | |||||
namespace dds { | |||||
class project { | |||||
std::optional<library> _main_lib; | |||||
std::vector<library> _submodules; | |||||
project(std::optional<library>&& ml, std::vector<library>&& mods) | |||||
: _main_lib(std::move(ml)) | |||||
, _submodules(std::move(mods)) {} | |||||
public: | |||||
static project from_directory(path_ref pr_dir); | |||||
const library* main_library() const noexcept { | |||||
if (_main_lib) { | |||||
return &*_main_lib; | |||||
} | |||||
return nullptr; | |||||
} | |||||
}; | |||||
} // namespace dds |
#include <spdlog/spdlog.h> | #include <spdlog/spdlog.h> | ||||
#include <range/v3/range/conversion.hpp> | |||||
#include <range/v3/view/filter.hpp> | |||||
#include <range/v3/view/transform.hpp> | |||||
#include <algorithm> | #include <algorithm> | ||||
#include <optional> | #include <optional> | ||||
#include <vector> | #include <vector> | ||||
return source_file{path, *kind}; | return source_file{path, *kind}; | ||||
} | } | ||||
source_list source_file::collect_for_dir(path_ref path) { | |||||
source_list ret; | |||||
for (auto entry : fs::recursive_directory_iterator(path)) { | |||||
if (!entry.is_regular_file()) { | |||||
continue; | |||||
} | |||||
auto sf = source_file::from_path(entry.path()); | |||||
if (!sf) { | |||||
spdlog::warn("Couldn't infer a source file kind for file: {}", entry.path().string()); | |||||
} | |||||
ret.emplace_back(std::move(*sf)); | |||||
} | |||||
return ret; | |||||
source_list source_file::collect_for_dir(path_ref src) { | |||||
using namespace ranges::view; | |||||
// Strips nullopt elements and lifts the value from the results | |||||
auto drop_nulls = // | |||||
filter(DDS_TL(_1.has_value())) // | |||||
| transform(DDS_TL(*_1)); | |||||
// Collect all source files from the directory | |||||
return // | |||||
fs::recursive_directory_iterator(src) // | |||||
| filter(DDS_TL(_1.is_regular_file())) // | |||||
| transform(DDS_TL(source_file::from_path(_1))) // | |||||
// source_file::from_path returns an optional. Drop nulls | |||||
| drop_nulls // | |||||
| ranges::to_vector; | |||||
} | } | ||||
source_list source_file::collect_pf_sources(path_ref path) { | |||||
auto include_dir = path / "include"; | |||||
auto src_dir = path / "src"; | |||||
source_list sources; | |||||
if (fs::exists(include_dir)) { | |||||
if (!fs::is_directory(include_dir)) { | |||||
throw std::runtime_error("The `include` at the root of the project is not a directory"); | |||||
} | |||||
auto inc_sources = source_file::collect_for_dir(include_dir); | |||||
// Drop any source files we found within `include/` | |||||
erase_if(sources, [&](auto& info) { | |||||
if (info.kind != source_kind::header) { | |||||
spdlog::warn("Source file in `include` will not be compiled: {}", | |||||
info.path.string()); | |||||
return true; | |||||
} | |||||
return false; | |||||
}); | |||||
extend(sources, inc_sources); | |||||
} | |||||
if (fs::exists(src_dir)) { | |||||
if (!fs::is_directory(src_dir)) { | |||||
throw std::runtime_error("The `src` at the root of the project is not a directory"); | |||||
} | |||||
auto src_sources = source_file::collect_for_dir(src_dir); | |||||
extend(sources, src_sources); | |||||
} | |||||
return sources; | |||||
} |
static std::optional<source_file> from_path(path_ref) noexcept; | static std::optional<source_file> from_path(path_ref) noexcept; | ||||
static std::vector<source_file> collect_for_dir(path_ref); | static std::vector<source_file> collect_for_dir(path_ref); | ||||
static std::vector<source_file> collect_pf_sources(path_ref); | |||||
}; | }; | ||||
using source_list = std::vector<source_file>; | using source_list = std::vector<source_file>; |
namespace dds { | namespace dds { | ||||
namespace detail { | |||||
template <std::size_t I, | |||||
typename A = std::nullptr_t, | |||||
typename B = std::nullptr_t, | |||||
typename C = std::nullptr_t, | |||||
typename D = std::nullptr_t> | |||||
decltype(auto) nth_arg(A&& a = nullptr, B&& b = nullptr, C&& c = nullptr, D&& d = nullptr) { | |||||
if constexpr (I == 0) { | |||||
return (A &&) a; | |||||
} else if constexpr (I == 1) { | |||||
return (B &&) b; | |||||
} else if constexpr (I == 2) { | |||||
return (C &&) c; | |||||
} else if constexpr (I == 3) { | |||||
return (D &&) d; | |||||
} | |||||
} | |||||
} // namespace detail | |||||
// Based on https://github.com/Quincunx271/TerseLambda | |||||
#define DDS_CTL(...) \ | |||||
(auto&&... _args_)->auto { \ | |||||
[[maybe_unused]] auto&& _1 = ::dds::detail::nth_arg<0>((decltype(_args_)&&)(_args_)...); \ | |||||
[[maybe_unused]] auto&& _2 = ::dds::detail::nth_arg<1>((decltype(_args_)&&)(_args_)...); \ | |||||
[[maybe_unused]] auto&& _3 = ::dds::detail::nth_arg<2>((decltype(_args_)&&)(_args_)...); \ | |||||
[[maybe_unused]] auto&& _4 = ::dds::detail::nth_arg<3>((decltype(_args_)&&)(_args_)...); \ | |||||
static_assert(sizeof...(_args_) <= 4); \ | |||||
decltype(auto) result = (__VA_ARGS__); \ | |||||
return result; \ | |||||
} | |||||
#define DDS_TL [] DDS_CTL | |||||
inline namespace file_utils { | inline namespace file_utils { | ||||
namespace fs = std::filesystem; | namespace fs = std::filesystem; |