| @@ -3,11 +3,16 @@ | |||
| #include <dds/compile.hpp> | |||
| #include <dds/logging.hpp> | |||
| #include <dds/proc.hpp> | |||
| #include <dds/project.hpp> | |||
| #include <dds/source.hpp> | |||
| #include <dds/toolchain.hpp> | |||
| #include <libman/index.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 <chrono> | |||
| #include <iomanip> | |||
| @@ -166,8 +171,11 @@ void link_apps(const source_list& sources, | |||
| } | |||
| 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<std::string> dep_defines; | |||
| @@ -268,14 +276,19 @@ void dds::build(const build_params& params, const library_manifest& man) { | |||
| 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; | |||
| 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 | |||
| / (fmt::format("lib{}{}", params.export_name, params.toolchain.archive_suffix())); | |||
| @@ -39,7 +39,7 @@ void file_compilation::compile(const toolchain& tc) const { | |||
| if (!compile_res.okay()) { | |||
| spdlog::error("Compilation failed: {}", source.path.string()); | |||
| 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. | |||
| @@ -0,0 +1,67 @@ | |||
| #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; | |||
| } | |||
| @@ -0,0 +1,37 @@ | |||
| #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 | |||
| @@ -0,0 +1,34 @@ | |||
| #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")); | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| #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 | |||
| @@ -2,6 +2,10 @@ | |||
| #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 <optional> | |||
| #include <vector> | |||
| @@ -61,51 +65,19 @@ std::optional<source_file> source_file::from_path(path_ref path) noexcept { | |||
| 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; | |||
| } | |||
| @@ -21,7 +21,6 @@ struct source_file { | |||
| 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_pf_sources(path_ref); | |||
| }; | |||
| using source_list = std::vector<source_file>; | |||
| @@ -8,6 +8,41 @@ | |||
| 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 { | |||
| namespace fs = std::filesystem; | |||